diff options
| author | Lennart Poettering <lennart@poettering.net> | 2007-10-28 19:13:50 +0000 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2007-10-28 19:13:50 +0000 | 
| commit | a67c21f093202f142438689d3f7cfbdf4ea82eea (patch) | |
| tree | 5c3295037f033904bc11ab8b3adae5b7331101e9 /src | |
| parent | 6687dd013169fd8436aa1b45ccdacff074a40d05 (diff) | |
merge 'lennart' branch back into trunk.
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1971 fefdeb5f-60dc-0310-8127-8f9354f1896f
Diffstat (limited to 'src')
270 files changed, 28673 insertions, 11201 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 93f331d6..e17e5ece 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,7 @@ pulseincludedir=$(includedir)/pulse  pulsecoreincludedir=$(includedir)/pulsecore  pulseconfdir=$(sysconfdir)/pulse  pulselibexecdir=$(libexecdir)/pulse +xdgautostartdir=$(sysconfdir)/xdg/autostart  ###################################  #            Defines              # @@ -76,15 +77,14 @@ endif  if OS_IS_WIN32  PA_THREAD_OBJS = \ -		pulsecore/once-win32.c pulsecore/once.h \  		pulsecore/mutex-win32.c pulsecore/mutex.h \ -		pulsecore/thread-win32.c pulsecore/thread.h +		pulsecore/thread-win32.c pulsecore/thread.h \ +		pulsecore/semaphore-win32.c pulsecore/semaphore.h  else  PA_THREAD_OBJS = \ -		pulsecore/atomic.h \ -		pulsecore/once-posix.c pulsecore/once.h \  		pulsecore/mutex-posix.c pulsecore/mutex.h \ -		pulsecore/thread-posix.c pulsecore/thread.h +		pulsecore/thread-posix.c pulsecore/thread.h \ +		pulsecore/semaphore-posix.c pulsecore/semaphore.h  endif  ################################### @@ -100,13 +100,19 @@ EXTRA_DIST = \  		depmod.py \  		daemon/esdcompat.in \  		utils/padsp \ -		modules/module-defs.h.m4 +		modules/module-defs.h.m4 \ +		daemon/pulseaudio-module-xsmp.desktop  pulseconf_DATA = \  		default.pa \  		daemon.conf \  		client.conf +if HAVE_X11 +xdgautostart_DATA = \ +		daemon/pulseaudio-module-xsmp.desktop +endif +  BUILT_SOURCES = \  		pulse/version.h @@ -122,13 +128,13 @@ pulseaudio_SOURCES = \  		daemon/cpulimit.c daemon/cpulimit.h \  		daemon/daemon-conf.c daemon/daemon-conf.h \  		daemon/dumpmodules.c daemon/dumpmodules.h \ +		daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \  		daemon/main.c \  		pulsecore/gccmacro.h -pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)  pulseaudio_CPPFLAGS = $(AM_CPPFLAGS) -pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) \ -		$(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) +pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) $(LIBSAMPLERATE_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) @@ -151,7 +157,8 @@ endif  bin_PROGRAMS += \  		pacat \  		pactl \ -		paplay +		paplay \ +		pasuspender  if HAVE_AF_UNIX  bin_PROGRAMS += pacmd @@ -182,6 +189,11 @@ pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)  pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)  pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +pasuspender_SOURCES = utils/pasuspender.c +pasuspender_LDADD = $(AM_LDADD) libpulse.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  pacmd_CFLAGS = $(AM_CFLAGS)  pacmd_LDADD = $(AM_LDADD) libpulse.la @@ -219,7 +231,14 @@ noinst_PROGRAMS = \  		hook-list-test \  		memblock-test \  		thread-test \ -		flist-test +		flist-test \ +		asyncq-test \ +		asyncmsgq-test \ +		queue-test \ +		rtpoll-test \ +		sig2str-test \ +		resampler-test \ +		smoother-test  if HAVE_SIGXCPU  noinst_PROGRAMS += \ @@ -274,13 +293,31 @@ thread_test_CFLAGS = $(AM_CFLAGS)  thread_test_LDADD = $(AM_LDADD) libpulsecore.la  thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -flist_test_SOURCES = tests/flist-test.c \ -		pulsecore/atomic.h \ -		pulsecore/flist.c pulsecore/flist.h +flist_test_SOURCES = tests/flist-test.c  flist_test_CFLAGS = $(AM_CFLAGS)  flist_test_LDADD = $(AM_LDADD) libpulsecore.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_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_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_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_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 @@ -341,6 +378,21 @@ interpol_test_LDADD = $(AM_LDADD) libpulse.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_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_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) + +smoother_test_SOURCES = tests/smoother-test.c +smoother_test_LDADD = $(AM_LDADD) libpulsecore.la +smoother_test_CFLAGS = $(AM_CFLAGS) +smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +  ###################################  #         Client library          #  ################################### @@ -455,6 +507,10 @@ libpulse_la_SOURCES += \  		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 \  		$(PA_THREAD_OBJS)  if OS_IS_WIN32 @@ -515,10 +571,26 @@ libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la  libpulsedsp_la_LDFLAGS = -avoid-version  ################################### +#      Speex Resampler            # +################################### + +noinst_LTLIBRARIES = libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la + +libspeex_resampler_fixed_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfx -DOUTSIDE_SPEEX -DFIXED_POINT +libspeex_resampler_fixed_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h pulsecore/speex/fixed_generic.h pulsecore/speexwrap.h + +libspeex_resampler_float_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfl -DOUTSIDE_SPEEX +libspeex_resampler_float_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h + +libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS) +libffmpeg_resampler_la_SOURCES = pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h + +###################################  #      Daemon core library        #  ################################### -pulsecoreinclude_HEADERS = \ +#pulsecoreinclude_HEADERS = +noinst_HEADERS = \  		pulsecore/autoload.h \  		pulsecore/atomic.h \  		pulsecore/cli-command.h \ @@ -567,6 +639,7 @@ pulsecoreinclude_HEADERS = \  		pulsecore/refcnt.h \  		pulsecore/mutex.h \  		pulsecore/thread.h \ +		pulsecore/semaphore.h \  		pulsecore/once.h  lib_LTLIBRARIES += libpulsecore.la @@ -608,6 +681,7 @@ libpulsecore_la_SOURCES += \  		pulsecore/memchunk.c pulsecore/memchunk.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/namereg.c pulsecore/namereg.h \  		pulsecore/pid.c pulsecore/pid.h \ @@ -636,6 +710,19 @@ libpulsecore_la_SOURCES += \  		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 \  		$(PA_THREAD_OBJS)  if OS_IS_WIN32 @@ -645,13 +732,14 @@ endif  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) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LIBICONV) +libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LIBICONV) libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la  ###################################  #   Plug-in support libraries     #  ################################### -pulsecoreinclude_HEADERS += \ +#pulsecoreinclude_HEADERS += +noinst_HEADERS += \  		pulsecore/socket-util.h \  		pulsecore/iochannel.h \  		pulsecore/socket-server.h \ @@ -700,9 +788,9 @@ modlibexec_LTLIBRARIES = \  		libauthkey-prop.la \  		libstrlist.la \  		libprotocol-simple.la \ -		libprotocol-esound.la \ +		libprotocol-http.la \  		libprotocol-native.la \ -		libprotocol-http.la +		libprotocol-esound.la  # We need to emulate sendmsg/recvmsg to support this on Win32  if !OS_IS_WIN32 @@ -711,7 +799,8 @@ modlibexec_LTLIBRARIES += \  endif  if HAVE_X11 -pulsecoreinclude_HEADERS += \ +#pulsecoreinclude_HEADERS += +noinst_HEADERS += \  		pulsecore/x11wrap.h \  		pulsecore/x11prop.h @@ -721,7 +810,8 @@ modlibexec_LTLIBRARIES += \  endif  if HAVE_AVAHI -pulsecoreinclude_HEADERS += \ +#pulsecoreinclude_HEADERS += +noinst_HEADERS += \  		pulsecore/avahi-wrap.h  modlibexec_LTLIBRARIES += \ @@ -851,19 +941,23 @@ modlibexec_LTLIBRARIES += \  		module-cli.la \  		module-cli-protocol-tcp.la \  		module-simple-protocol-tcp.la \ -		module-esound-protocol-tcp.la \ -		module-native-protocol-tcp.la \ -		module-native-protocol-fd.la \ -		module-sine.la \ -		module-combine.la \ -		module-tunnel-sink.la \ -		module-tunnel-source.la \  		module-null-sink.la \ -		module-esound-sink.la \ -		module-http-protocol-tcp.la \  		module-detect.la \  		module-volume-restore.la \ -		module-rescue-streams.la +		module-default-device-restore.la \ +		module-rescue-streams.la \ +		module-suspend-on-idle.la \ +		module-http-protocol-tcp.la \ +		module-sine.la \ +		module-native-protocol-tcp.la \ +		module-native-protocol-fd.la \ +		module-esound-protocol-tcp.la \ +		module-combine.la \ +		module-remap-sink.la \ +		module-ladspa-sink.la \ +		module-esound-sink.la +#		module-tunnel-sink.la +#		module-tunnel-source.la  # See comment at librtp.la above  if !OS_IS_WIN32 @@ -876,9 +970,9 @@ if HAVE_AF_UNIX  modlibexec_LTLIBRARIES += \  		module-cli-protocol-unix.la \  		module-simple-protocol-unix.la \ -		module-esound-protocol-unix.la \ +		module-http-protocol-unix.la \  		module-native-protocol-unix.la \ -		module-http-protocol-unix.la +		module-esound-protocol-unix.la  endif  if HAVE_MKFIFO @@ -901,14 +995,14 @@ endif  if HAVE_X11  modlibexec_LTLIBRARIES += \  		module-x11-bell.la \ -		module-x11-publish.la +		module-x11-publish.la \ +		module-x11-xsmp.la  endif  if HAVE_OSS  modlibexec_LTLIBRARIES += \  		liboss-util.la \ -		module-oss.la \ -		module-oss-mmap.la +		module-oss.la  endif  if HAVE_ALSA @@ -952,10 +1046,10 @@ pulselibexec_PROGRAMS = \  		gconf-helper  endif -if OS_IS_WIN32 -modlibexec_LTLIBRARIES += \ -		module-waveout.la -endif +#if OS_IS_WIN32 +#modlibexec_LTLIBRARIES += \ +#		module-waveout.la +#endif  if HAVE_HAL  modlibexec_LTLIBRARIES += \ @@ -980,6 +1074,8 @@ SYMDEF_FILES = \  		modules/module-native-protocol-fd-symdef.h \  		modules/module-sine-symdef.h \  		modules/module-combine-symdef.h \ +		modules/module-remap-sink-symdef.h \ +		modules/module-ladspa-sink-symdef.h \  		modules/module-esound-compat-spawnfd-symdef.h \  		modules/module-esound-compat-spawnpid-symdef.h \  		modules/module-match-symdef.h \ @@ -994,8 +1090,8 @@ SYMDEF_FILES = \  		modules/module-http-protocol-unix-symdef.h \  		modules/module-x11-bell-symdef.h \  		modules/module-x11-publish-symdef.h \ +		modules/module-x11-xsmp-symdef.h \  		modules/module-oss-symdef.h \ -		modules/module-oss-mmap-symdef.h \  		modules/module-alsa-sink-symdef.h \  		modules/module-alsa-source-symdef.h \  		modules/module-solaris-symdef.h \ @@ -1006,7 +1102,9 @@ SYMDEF_FILES = \  		modules/module-jack-sink-symdef.h \  		modules/module-jack-source-symdef.h \  		modules/module-volume-restore-symdef.h \ +		modules/module-default-device-restore-symdef.h \  		modules/module-rescue-streams-symdef.h \ +		modules/module-suspend-on-idle-symdef.h \  		modules/module-hal-detect-symdef.h \  		modules/gconf/module-gconf-symdef.h @@ -1126,18 +1224,27 @@ module_combine_la_SOURCES = modules/module-combine.c  module_combine_la_LDFLAGS = -module -avoid-version  module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore.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_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\" $(AM_CFLAGS) +module_ladspa_sink_la_LDFLAGS = -module -avoid-version +module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.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_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 libauthkey-prop.la libsocket-util.la libiochannel.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 libauthkey-prop.la libsocket-util.la libiochannel.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 libauthkey-prop.la libsocket-util.la libiochannel.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 libauthkey-prop.la libsocket-util.la libiochannel.la  # X11 @@ -1151,6 +1258,11 @@ 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 libauthkey-prop.la libx11prop.la libstrlist.la libpulsecore.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) libpulsecore.la +  # OSS  liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h @@ -1161,10 +1273,6 @@ 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_mmap_la_SOURCES = modules/module-oss-mmap.c -module_oss_mmap_la_LDFLAGS = -module -avoid-version -module_oss_mmap_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore.la -  # ALSA  libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h @@ -1211,10 +1319,10 @@ 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_CFLAGS = $(AM_CFLAGS) +#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_CFLAGS = $(AM_CFLAGS)  # Hardware autodetection module  module_detect_la_SOURCES = modules/module-detect.c @@ -1228,16 +1336,28 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version  module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la  module_volume_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_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_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_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 +module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la libsocket-util.la  module_rtp_send_la_CFLAGS = $(AM_CFLAGS)  module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c @@ -1275,7 +1395,7 @@ module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.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) +gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la  gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)  gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) @@ -1304,8 +1424,8 @@ default.pa: daemon/default.pa.win32  else  default.pa: daemon/default.pa.in Makefile  	sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ -	    -e 's,@HAVE_HAL_TRUE\@,@HAVE_HAL_TRUE@,g' \ -	    -e 's,@HAVE_HAL_FALSE\@,@HAVE_HAL_FALSE@,g' < $< > $@ +            -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \ +	    -e 's,@PA_SOEXT\@,.so,g' < $< > $@  endif  daemon.conf: daemon/daemon.conf.in Makefile @@ -1323,4 +1443,13 @@ install-exec-hook:  massif: pulseaudio  	libtool --mode=execute valgrind --tool=massif --depth=6  --alloc-fn=pa_xmalloc --alloc-fn=pa_xmalloc0 --alloc-fn=pa_xrealloc --alloc-fn=dbus_realloc --alloc-fn=pa_xnew0_internal --alloc-fn=pa_xnew_internal ./pulseaudio +update-speex: +	wget -O pulsecore/speex/speex_resampler.h http://svn.xiph.org/trunk/speex/include/speex/speex_resampler.h +	wget -O pulsecore/speex/resample.c http://svn.xiph.org/trunk/speex/libspeex/resample.c +	wget -O pulsecore/speex/arch.h http://svn.xiph.org/trunk/speex/libspeex/arch.h +	wget -O pulsecore/speex/fixed_generic.h http://svn.xiph.org/trunk/speex/libspeex/fixed_generic.h + +update-ffmpeg: +	wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co +  .PHONY: utils/padsp diff --git a/src/daemon/caps.c b/src/daemon/caps.c index f92db743..5b4008a5 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -26,11 +26,11 @@  #include <config.h>  #endif -#include <assert.h>  #include <unistd.h>  #include <errno.h>  #include <string.h>  #include <sys/types.h> +#include <pulsecore/macro.h>  #ifdef HAVE_SYS_CAPABILITY_H  #include <sys/capability.h> @@ -60,7 +60,7 @@ void pa_drop_root(void) {      if (uid == 0 || geteuid() != 0)          return; -    pa_log_info("dropping root rights."); +    pa_log_info("Dropping root priviliges.");  #if defined(HAVE_SETRESUID)      setresuid(uid, uid, uid); @@ -88,8 +88,9 @@ int pa_limit_caps(void) {      cap_value_t nice_cap = CAP_SYS_NICE;      caps = cap_init(); -    assert(caps); +    pa_assert(caps);      cap_clear(caps); +    cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);      cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);      if (cap_set_proc(caps) < 0) @@ -98,7 +99,7 @@ int pa_limit_caps(void) {      if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)          goto fail; -    pa_log_info("dropped capabilities successfully."); +    pa_log_info("Dropped capabilities successfully.");      r = 1; @@ -114,14 +115,14 @@ int pa_drop_caps(void) {      int r = -1;      caps = cap_init(); -    assert(caps); +    pa_assert(caps);      cap_clear(caps);      prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);      if (cap_set_proc(caps) < 0) { -        pa_log("failed to drop capabilities: %s", pa_cstrerror(errno)); +        pa_log("Failed to drop capabilities: %s", pa_cstrerror(errno));          goto fail;      } diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index dc757c9c..6b7b2671 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -26,7 +26,6 @@  #endif  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <stdio.h>  #include <getopt.h> @@ -36,6 +35,7 @@  #include <pulsecore/core-util.h>  #include <pulsecore/strbuf.h> +#include <pulsecore/macro.h>  #include "cmdline.h" @@ -63,7 +63,9 @@ enum {      ARG_CHECK,      ARG_NO_CPU_LIMIT,      ARG_DISABLE_SHM, -    ARG_SYSTEM +    ARG_DUMP_RESAMPLE_METHODS, +    ARG_SYSTEM, +    ARG_CLEANUP_SHM  };  /* Tabel for getopt_long() */ @@ -92,12 +94,16 @@ static struct option long_options[] = {      {"system",                      2, 0, ARG_SYSTEM},      {"no-cpu-limit",                2, 0, ARG_NO_CPU_LIMIT},      {"disable-shm",                 2, 0, ARG_DISABLE_SHM}, +    {"dump-resample-methods",       2, 0, ARG_DUMP_RESAMPLE_METHODS}, +    {"cleanup-shm",                 2, 0, ARG_CLEANUP_SHM},      {NULL, 0, 0, 0}  };  void pa_cmdline_help(const char *argv0) {      const char *e; +    pa_assert(argv0); +      if ((e = strrchr(argv0, '/')))          e++;      else @@ -109,6 +115,8 @@ void pa_cmdline_help(const char *argv0) {             "      --version                         Show version\n"             "      --dump-conf                       Dump default configuration\n"             "      --dump-modules                    Dump list of available modules\n" +           "      --dump-resample-methods           Dump available resample methods\n" +           "      --cleanup-shm                     Cleanup stale shared memory segments\n"             "  -k  --kill                            Kill a running daemon\n"             "      --check                           Check for a running daemon\n\n" @@ -131,9 +139,8 @@ void pa_cmdline_help(const char *argv0) {             "  -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" -           "                                        (one of src-sinc-medium-quality,\n" -           "                                        src-sinc-best-quality,src-sinc-fastest\n" -           "                                        src-zero-order-hold,src-linear,trivial)\n" +           "                                        (See --dump-resample-methods for\n" +           "                                        possible values)\n"             "      --use-pid-file[=BOOL]             Create a PID file\n"             "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"             "                                        platforms that support it.\n" @@ -152,7 +159,10 @@ void pa_cmdline_help(const char *argv0) {  int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {      pa_strbuf *buf = NULL;      int c; -    assert(conf && argc && argv); + +    pa_assert(conf); +    pa_assert(argc > 0); +    pa_assert(argv);      buf = pa_strbuf_new(); @@ -178,6 +188,14 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d                  conf->cmd = PA_CMD_DUMP_MODULES;                  break; +            case ARG_DUMP_RESAMPLE_METHODS: +                conf->cmd = PA_CMD_DUMP_RESAMPLE_METHODS; +                break; + +            case ARG_CLEANUP_SHM: +                conf->cmd = PA_CMD_CLEANUP_SHM; +                break; +              case 'k':              case ARG_KILL:                  conf->cmd = PA_CMD_KILL; @@ -193,9 +211,12 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d                  break;              case ARG_FILE: -            case 'F': -                pa_strbuf_printf(buf, ".include %s\n", optarg); +            case 'F': { +                char *p; +                pa_strbuf_printf(buf, ".include %s\n", p = pa_make_path_absolute(optarg)); +                pa_xfree(p);                  break; +            }              case 'C':                  pa_strbuf_puts(buf, "load-module module-cli exit_on_eof=1\n"); diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index d4ac1d86..ab212129 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -30,6 +30,7 @@  #include <pulsecore/core-util.h>  #include <pulsecore/core-error.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "cpulimit.h" @@ -38,7 +39,6 @@  #include <errno.h>  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <sys/time.h>  #include <unistd.h>  #include <signal.h> @@ -92,23 +92,18 @@ static enum  {  /* Reset the SIGXCPU timer to the next t seconds */  static void reset_cpu_time(int t) { -    int r;      long n;      struct rlimit rl;      struct rusage ru;      /* Get the current CPU time of the current process */ -    r = getrusage(RUSAGE_SELF, &ru); -    assert(r >= 0); +    pa_assert_se(getrusage(RUSAGE_SELF, &ru) >= 0);      n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t; - -    r = getrlimit(RLIMIT_CPU, &rl); -    assert(r >= 0); +    pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0);      rl.rlim_cur = n; -    r = setrlimit(RLIMIT_CPU, &rl); -    assert(r >= 0); +    pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);  }  /* A simple, thread-safe puts() work-alike */ @@ -118,7 +113,7 @@ static void write_err(const char *p) {  /* The signal handler, called on every SIGXCPU */  static void signal_handler(int sig) { -    assert(sig == SIGXCPU); +    pa_assert(sig == SIGXCPU);      if (phase == PHASE_IDLE) {          time_t now; @@ -130,7 +125,7 @@ static void signal_handler(int sig) {          time(&now);  #ifdef PRINT_CPU_LOAD -        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/(now-last_time)*100);          write_err(t);  #endif @@ -160,7 +155,12 @@ static void signal_handler(int sig) {  /* Callback for IO events on the FIFO */  static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {      char c; -    assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); +    pa_assert(m); +    pa_assert(e); +    pa_assert(f == PA_IO_EVENT_INPUT); +    pa_assert(e == io_event); +    pa_assert(fd == the_pipe[0]); +      pa_read(the_pipe[0], &c, sizeof(c), NULL);      m->quit(m, 1); /* Quit the main loop */  } @@ -168,7 +168,13 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags  /* Initializes CPU load limiter */  int pa_cpu_limit_init(pa_mainloop_api *m) {      struct sigaction sa; -    assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed); + +    pa_assert(m); +    pa_assert(!api); +    pa_assert(!io_event); +    pa_assert(the_pipe[0] == -1); +    pa_assert(the_pipe[1] == -1); +    pa_assert(!installed);      time(&last_time); @@ -178,10 +184,10 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {          return -1;      } -    pa_make_nonblock_fd(the_pipe[0]); -    pa_make_nonblock_fd(the_pipe[1]); -    pa_fd_set_cloexec(the_pipe[0], 1); -    pa_fd_set_cloexec(the_pipe[1], 1); +    pa_make_fd_nonblock(the_pipe[0]); +    pa_make_fd_nonblock(the_pipe[1]); +    pa_make_fd_cloexec(the_pipe[0]); +    pa_make_fd_cloexec(the_pipe[1]);      api = m;      io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); @@ -208,24 +214,18 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {  /* Shutdown CPU load limiter */  void pa_cpu_limit_done(void) { -    int r;      if (io_event) { -        assert(api); +        pa_assert(api);          api->io_free(io_event);          io_event = NULL;          api = NULL;      } -    if (the_pipe[0] >= 0) -        close(the_pipe[0]); -    if (the_pipe[1] >= 0) -        close(the_pipe[1]); -    the_pipe[0] = the_pipe[1] = -1; +    pa_close_pipe(the_pipe);      if (installed) { -        r = sigaction(SIGXCPU, &sigaction_prev, NULL); -        assert(r >= 0); +        pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);          installed = 0;      }  } diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index 8cab327f..920e3717 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -29,7 +29,6 @@  #include <errno.h>  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <unistd.h>  #include <pulse/xmalloc.h> @@ -39,19 +38,14 @@  #include <pulsecore/strbuf.h>  #include <pulsecore/conf-parser.h>  #include <pulsecore/resampler.h> +#include <pulsecore/macro.h>  #include "daemon-conf.h" -#ifndef OS_IS_WIN32 -# define PATH_SEP "/" -#else -# define PATH_SEP "\\" -#endif - -#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "default.pa" -#define DEFAULT_SCRIPT_FILE_USER PATH_SEP "default.pa" -#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf" -#define DEFAULT_CONFIG_FILE_USER PATH_SEP "daemon.conf" +#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa" +#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa" +#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf" +#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"  #define ENV_SCRIPT_FILE "PULSE_SCRIPT"  #define ENV_CONFIG_FILE "PULSE_CONFIG" @@ -72,31 +66,34 @@ static const pa_daemon_conf default_conf = {      .default_script_file = NULL,      .log_target = PA_LOG_SYSLOG,      .log_level = PA_LOG_NOTICE, -    .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST, +    .resample_method = PA_RESAMPLER_AUTO,      .config_file = NULL,      .use_pid_file = 1,      .system_instance = 0,      .no_cpu_limit = 0, -    .disable_shm = 0 +    .disable_shm = 0, +    .default_n_fragments = 4, +    .default_fragment_size_msec = 25, +    .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }  #ifdef HAVE_SYS_RESOURCE_H      , .rlimit_as = { .value = 0, .is_set = 0 },      .rlimit_core = { .value = 0, .is_set = 0 },      .rlimit_data = { .value = 0, .is_set = 0 },      .rlimit_fsize = { .value = 0, .is_set = 0 }, -    .rlimit_nofile = { .value = 200, .is_set = 1 }, +    .rlimit_nofile = { .value = 256, .is_set = 1 },      .rlimit_stack = { .value = 0, .is_set = 0 }  #ifdef RLIMIT_NPROC      , .rlimit_nproc = { .value = 0, .is_set = 0 }  #endif  #ifdef RLIMIT_MEMLOCK -    , .rlimit_memlock = { .value = 0, .is_set = 1 } +    , .rlimit_memlock = { .value = 16384, .is_set = 1 }  #endif  #endif  };  pa_daemon_conf* pa_daemon_conf_new(void) {      FILE *f; -    pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); +    pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);      if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))          fclose(f); @@ -106,7 +103,7 @@ pa_daemon_conf* pa_daemon_conf_new(void) {  }  void pa_daemon_conf_free(pa_daemon_conf *c) { -    assert(c); +    pa_assert(c);      pa_xfree(c->script_commands);      pa_xfree(c->dl_search_path);      pa_xfree(c->default_script_file); @@ -115,7 +112,8 @@ void pa_daemon_conf_free(pa_daemon_conf *c) {  }  int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) { -    assert(c && string); +    pa_assert(c); +    pa_assert(string);      if (!strcmp(string, "auto"))          c->auto_log_target = 1; @@ -133,7 +131,8 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {  int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {      uint32_t u; -    assert(c && string); +    pa_assert(c); +    pa_assert(string);      if (pa_atou(string, &u) >= 0) {          if (u >= PA_LOG_LEVEL_MAX) @@ -158,7 +157,8 @@ int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {  int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {      int m; -    assert(c && string); +    pa_assert(c); +    pa_assert(string);      if ((m = pa_parse_resample_method(string)) < 0)          return -1; @@ -169,7 +169,11 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {  static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {      pa_daemon_conf *c = data; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {          pa_log("[%s:%u] Invalid log target '%s'.", filename, line, rvalue); @@ -181,7 +185,11 @@ static int parse_log_target(const char *filename, unsigned line, const char *lva  static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {      pa_daemon_conf *c = data; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {          pa_log("[%s:%u] Invalid log level '%s'.", filename, line, rvalue); @@ -193,10 +201,14 @@ static int parse_log_level(const char *filename, unsigned line, const char *lval  static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {      pa_daemon_conf *c = data; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) { -        pa_log("[%s:%u] Inavalid resample method '%s'.", filename, line, rvalue); +        pa_log("[%s:%u] Invalid resample method '%s'.", filename, line, rvalue);          return -1;      } @@ -206,10 +218,11 @@ static int parse_resample_method(const char *filename, unsigned line, const char  static int parse_rlimit(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {  #ifdef HAVE_SYS_RESOURCE_H      struct pa_rlimit *r = data; -    assert(filename); -    assert(lvalue); -    assert(rvalue); -    assert(r); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(r);      if (rvalue[strspn(rvalue, "\t ")] == 0) {          /* Empty string */ @@ -218,7 +231,7 @@ static int parse_rlimit(const char *filename, unsigned line, const char *lvalue,      } else {          int32_t k;          if (pa_atoi(rvalue, &k) < 0) { -            pa_log("[%s:%u] Inavalid rlimit '%s'.", filename, line, rvalue); +            pa_log("[%s:%u] Invalid rlimit '%s'.", filename, line, rvalue);              return -1;          }          r->is_set = k >= 0; @@ -231,43 +244,138 @@ 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) { +    pa_daemon_conf *c = data; +    pa_sample_format_t f; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if ((f = pa_parse_sample_format(rvalue)) < 0) { +        pa_log("[%s:%u] Invalid sample format '%s'.", filename, line, rvalue); +        return -1; +    } + +    c->default_sample_spec.format = f; +    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) { +    pa_daemon_conf *c = data; +    int32_t r; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atoi(rvalue, &r) < 0 || r > PA_RATE_MAX || r <= 0) { +        pa_log("[%s:%u] Invalid sample rate '%s'.", filename, line, rvalue); +        return -1; +    } + +    c->default_sample_spec.rate = r; +    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) { +    pa_daemon_conf *c = data; +    int32_t n; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atoi(rvalue, &n) < 0 || n > PA_CHANNELS_MAX || n <= 0) { +        pa_log("[%s:%u] Invalid sample channels '%s'.", filename, line, rvalue); +        return -1; +    } + +    c->default_sample_spec.channels = (uint8_t) n; +    return 0; +} + +static int parse_fragments(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +    pa_daemon_conf *c = data; +    int32_t n; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atoi(rvalue, &n) < 0 || n < 2) { +        pa_log("[%s:%u] Invalid number of fragments '%s'.", filename, line, rvalue); +        return -1; +    } + +    c->default_n_fragments = (unsigned) n; +    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) { +    pa_daemon_conf *c = data; +    int32_t n; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atoi(rvalue, &n) < 0 || n < 1) { +        pa_log("[%s:%u] Invalid fragment size '%s'.", filename, line, rvalue); +        return -1; +    } + +    c->default_fragment_size_msec = (unsigned) n; +    return 0; +} +  int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {      int r = -1;      FILE *f = NULL;      pa_config_item table[] = { -        { "daemonize",               pa_config_parse_bool,    NULL }, -        { "fail",                    pa_config_parse_bool,    NULL }, -        { "high-priority",           pa_config_parse_bool,    NULL }, -        { "disallow-module-loading", pa_config_parse_bool,    NULL }, -        { "exit-idle-time",          pa_config_parse_int,     NULL }, -        { "module-idle-time",        pa_config_parse_int,     NULL }, -        { "scache-idle-time",        pa_config_parse_int,     NULL }, -        { "dl-search-path",          pa_config_parse_string,  NULL }, -        { "default-script-file",     pa_config_parse_string,  NULL }, -        { "log-target",              parse_log_target,        NULL }, -        { "log-level",               parse_log_level,         NULL }, -        { "verbose",                 parse_log_level,         NULL }, -        { "resample-method",         parse_resample_method,   NULL }, -        { "use-pid-file",            pa_config_parse_bool,    NULL }, -        { "system-instance",         pa_config_parse_bool,    NULL }, -        { "no-cpu-limit",            pa_config_parse_bool,    NULL }, -        { "disable-shm",             pa_config_parse_bool,    NULL }, +        { "daemonize",                  pa_config_parse_bool,     NULL }, +        { "fail",                       pa_config_parse_bool,     NULL }, +        { "high-priority",              pa_config_parse_bool,     NULL }, +        { "disallow-module-loading",    pa_config_parse_bool,     NULL }, +        { "exit-idle-time",             pa_config_parse_int,      NULL }, +        { "module-idle-time",           pa_config_parse_int,      NULL }, +        { "scache-idle-time",           pa_config_parse_int,      NULL }, +        { "dl-search-path",             pa_config_parse_string,   NULL }, +        { "default-script-file",        pa_config_parse_string,   NULL }, +        { "log-target",                 parse_log_target,         NULL }, +        { "log-level",                  parse_log_level,          NULL }, +        { "verbose",                    parse_log_level,          NULL }, +        { "resample-method",            parse_resample_method,    NULL }, +        { "use-pid-file",               pa_config_parse_bool,     NULL }, +        { "system-instance",            pa_config_parse_bool,     NULL }, +        { "no-cpu-limit",               pa_config_parse_bool,     NULL }, +        { "disable-shm",                pa_config_parse_bool,     NULL }, +        { "default-sample-format",      parse_sample_format,      NULL }, +        { "default-sample-rate",        parse_sample_rate,        NULL }, +        { "default-sample-channels",    parse_sample_channels,    NULL }, +        { "default-fragments",          parse_fragments,          NULL }, +        { "default-fragment-size-msec", parse_fragment_size_msec, NULL },  #ifdef HAVE_SYS_RESOURCE_H -        { "rlimit-as",               parse_rlimit,            NULL }, -        { "rlimit-core",             parse_rlimit,            NULL }, -        { "rlimit-data",             parse_rlimit,            NULL }, -        { "rlimit-fsize",            parse_rlimit,            NULL }, -        { "rlimit-nofile",           parse_rlimit,            NULL }, -        { "rlimit-stack",            parse_rlimit,            NULL }, +        { "rlimit-as",                  parse_rlimit,             NULL }, +        { "rlimit-core",                parse_rlimit,             NULL }, +        { "rlimit-data",                parse_rlimit,             NULL }, +        { "rlimit-fsize",               parse_rlimit,             NULL }, +        { "rlimit-nofile",              parse_rlimit,             NULL }, +        { "rlimit-stack",               parse_rlimit,             NULL },  #ifdef RLIMIT_NPROC -        { "rlimit-nproc",            parse_rlimit,            NULL }, +        { "rlimit-nproc",               parse_rlimit,             NULL },  #endif  #ifdef RLIMIT_MEMLOCK -        { "rlimit-memlock",          parse_rlimit,            NULL }, +        { "rlimit-memlock",             parse_rlimit,             NULL },  #endif  #endif -        { NULL,                      NULL,                    NULL }, +        { NULL,                         NULL,                     NULL },      };      table[0].data = &c->daemonize; @@ -287,25 +395,29 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {      table[14].data = &c->system_instance;      table[15].data = &c->no_cpu_limit;      table[16].data = &c->disable_shm; +    table[17].data = c; +    table[18].data = c; +    table[19].data = c; +    table[20].data = c; +    table[21].data = c;  #ifdef HAVE_SYS_RESOURCE_H -    table[17].data = &c->rlimit_as; -    table[18].data = &c->rlimit_core; -    table[19].data = &c->rlimit_data; -    table[20].data = &c->rlimit_fsize; -    table[21].data = &c->rlimit_nofile; -    table[22].data = &c->rlimit_stack; +    table[22].data = &c->rlimit_as; +    table[23].data = &c->rlimit_core; +    table[24].data = &c->rlimit_data; +    table[25].data = &c->rlimit_fsize; +    table[26].data = &c->rlimit_nofile; +    table[27].data = &c->rlimit_stack;  #ifdef RLIMIT_NPROC -    table[23].data = &c->rlimit_nproc; +    table[28].data = &c->rlimit_nproc;  #endif  #ifdef RLIMIT_MEMLOCK  #ifndef RLIMIT_NPROC  #error "Houston, we have a numbering problem!"  #endif -    table[24].data = &c->rlimit_memlock; +    table[29].data = &c->rlimit_memlock;  #endif  #endif -      pa_xfree(c->config_file);      c->config_file = NULL; @@ -314,7 +426,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {          pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");      if (!f && errno != ENOENT) { -        pa_log("WARNING: failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno)); +        pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));          goto finish;      } @@ -351,12 +463,16 @@ static const char* const log_level_to_string[] = {  };  char *pa_daemon_conf_dump(pa_daemon_conf *c) { -    pa_strbuf *s = pa_strbuf_new(); +    pa_strbuf *s; + +    pa_assert(c); + +    s = pa_strbuf_new();      if (c->config_file)          pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file); -    assert(c->log_level <= PA_LOG_LEVEL_MAX); +    pa_assert(c->log_level <= PA_LOG_LEVEL_MAX);      pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);      pa_strbuf_printf(s, "fail = %i\n", !!c->fail); @@ -373,7 +489,12 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {      pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file);      pa_strbuf_printf(s, "system-instance = %i\n", !!c->system_instance);      pa_strbuf_printf(s, "no-cpu-limit = %i\n", !!c->no_cpu_limit); -    pa_strbuf_printf(s, "disable_shm = %i\n", !!c->disable_shm); +    pa_strbuf_printf(s, "disable-shm = %i\n", !!c->disable_shm); +    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);  #ifdef HAVE_SYS_RESOURCE_H      pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);      pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 4843a610..4d37861d 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -26,6 +26,7 @@  ***/  #include <pulsecore/log.h> +#include <pulse/sample.h>  #ifdef HAVE_SYS_RESOURCE_H  #include <sys/resource.h> @@ -39,7 +40,9 @@ typedef enum pa_daemon_conf_cmd {      PA_CMD_DUMP_CONF,      PA_CMD_DUMP_MODULES,      PA_CMD_KILL, -    PA_CMD_CHECK +    PA_CMD_CHECK, +    PA_CMD_DUMP_RESAMPLE_METHODS, +    PA_CMD_CLEANUP_SHM  } pa_daemon_conf_cmd_t;  #ifdef HAVE_SYS_RESOURCE_H @@ -80,6 +83,8 @@ typedef struct pa_daemon_conf {  #endif  #endif +    unsigned default_n_fragments, default_fragment_size_msec; +    pa_sample_spec default_sample_spec;  } 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 29b22a42..2132bf3d 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -92,10 +92,19 @@  ; rlimit-core = -1  ; rlimit-data = -1  ; rlimit-fsize = -1 -; rlimit-nofile = 200 +; rlimit-nofile = 256  ; rlimit-stack = -1  ; rlimit-nproc = -1 -; rlimit-memlock = 25 +; rlimit-memlock = 16384  ## Disable shared memory data transfer   ; disable-shm = 0 + +## Default sample format +; default-sample-format = s16le +; default-sample-rate = 44100 +; default-sample-channels = 2 + +## Default fragment settings, for device drivers that need this +; default-fragments = 4 +; default-fragment-size-msec = 25 diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index af2a6789..597993c4 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -1,5 +1,4 @@ -#!@PA_BINARY@ -nF  - +#!@PA_BINARY@ -nF  #  # This file is part of PulseAudio.  # @@ -17,8 +16,19 @@  # along with PulseAudio; if not, write to the Free Software Foundation,  # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.nofail + +### 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-coldplug /usr/share/sounds/startup3.wav +#load-sample-lazy pulse-access /usr/share/sounds/generic.wav -### Load audio drivers statically +.fail + +### Load audio drivers statically (it's probably better to not load +### these drivers manually, but instead use module-hal-detect -- +### see below -- for doing this automatically)  #load-module module-alsa-sink  #load-module module-alsa-source device=hw:1,0  #load-module module-oss device="/dev/dsp" sink_name=output source_name=input @@ -27,19 +37,13 @@  #load-module module-pipe-sink  ### Automatically load driver modules depending on the hardware available -@HAVE_HAL_TRUE@load-module module-hal-detect - +.ifexists @PA_DLSEARCHPATH@/module-hal-detect@PA_SOEXT@ +load-module module-hal-detect +.else  ### Alternatively use the static hardware detection module (for systems that -### lack HAL support -@HAVE_HAL_FALSE@load-module module-detect - -### Load audio drivers automatically on access -#add-autoload-sink output module-oss device="/dev/dsp" sink_name=output source_name=input -#add-autoload-source input module-oss device="/dev/dsp" sink_name=output source_name=input -#add-autoload-sink output module-oss-mmap device="/dev/dsp" sink_name=output source_name=input -#add-autoload-source input module-oss-mmap device="/dev/dsp" sink_name=output source_name=input -#add-autoload-sink output module-alsa-sink sink_name=output -#add-autoload-source input module-alsa-source source_name=input +### lack HAL support) +load-module module-detect +.endif  ### Load several protocols  load-module module-esound-protocol-unix @@ -61,27 +65,36 @@ load-module module-native-protocol-unix  ### Automatically restore the volume of playback streams  load-module module-volume-restore +### Automatically restore the default sink/source when changed by the user during runtime +load-module module-default-device-restore +  ### Automatically move streams to the default sink if the sink they are  ### connected to dies, similar for sources  load-module module-rescue-streams -### Make some devices default -#set-default-sink output -#set-default-source input - -.nofail - -### Load something to the sample cache -load-sample x11-bell /usr/share/sounds/gtk-events/activate.wav -#load-sample-dir-lazy /usr/share/sounds/*.wav +### Automatically suspend sinks/sources that become idle for too long +load-module module-suspend-on-idle  ### Load X11 bell module -load-module module-x11-bell sample=x11-bell +#load-module module-x11-bell sample=x11-bell  ### Publish connection data in the X11 root window +.ifexists @PA_DLSEARCHPATH@/module-x11-publish@PA_SOEXT@  load-module module-x11-publish +.endif + +### Register ourselves in the X11 session manager +# Deactivated by default, to avoid deadlock when PA is started as esd from gnome-session +# Instead we load this via /etc/xdg/autostart/ and "pactl load-module" now +# load-module module-x11-xsmp  ### Load additional modules from GConf settings. This can be configured with the paprefs tool.  ### Please keep in mind that the modules configured by paprefs might conflict with manually  ### loaded modules. +.ifexists @PA_DLSEARCHPATH@/module-gconf@PA_SOEXT@  load-module module-gconf +.endif + +### Make some devices default +#set-default-sink output +#set-default-source input diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c index 6743622a..ad7fab20 100644 --- a/src/daemon/dumpmodules.c +++ b/src/daemon/dumpmodules.c @@ -28,26 +28,30 @@  #include <string.h>  #include <getopt.h> -#include <assert.h>  #include <stdio.h>  #include <ltdl.h>  #include <pulse/util.h>  #include <pulsecore/modinfo.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "dumpmodules.h"  #define PREFIX "module-"  static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) { -    assert(name && i); +    pa_assert(name); +    pa_assert(i); +      printf("%-40s%s\n", name, i->description ? i->description : "n/a");  }  static void long_info(const char *name, const char *path, pa_modinfo *i) {      static int nl = 0; -    assert(name && i); +    pa_assert(name); +    pa_assert(i);      if (nl)          printf("\n"); @@ -76,6 +80,8 @@ static void long_info(const char *name, const char *path, pa_modinfo *i) {  static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {      pa_modinfo *i; +    pa_assert(name); +      if ((i = pa_modinfo_get_by_name(path ? path : name))) {          info(name, path, i);          pa_modinfo_free(i); @@ -93,7 +99,7 @@ static int is_preloaded(const char *name) {          if (l->address)              continue; -        snprintf(buf, sizeof(buf), "%s", l->name); +        pa_snprintf(buf, sizeof(buf), "%s", l->name);          if ((e = strrchr(buf, '.')))              *e = 0; @@ -121,6 +127,8 @@ static int callback(const char *path, lt_ptr data) {  }  void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) { +    pa_assert(c); +      if (argc > 0) {          int i;          for (i = 0; i < argc; i++) @@ -137,7 +145,7 @@ void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {              if (strlen(l->name) <= sizeof(PREFIX)-1 || strncmp(l->name, PREFIX, sizeof(PREFIX)-1))                  continue; -            snprintf(buf, sizeof(buf), "%s", l->name); +            pa_snprintf(buf, sizeof(buf), "%s", l->name);              if ((e = strrchr(buf, '.')))                  *e = 0; diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c new file mode 100644 index 00000000..6025c6e3 --- /dev/null +++ b/src/daemon/ltdl-bind-now.c @@ -0,0 +1,160 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#if HAVE_SYS_DL_H +#include <sys/dl.h> +#endif + +#include <ltdl.h> + +#include <pulsecore/macro.h> +#include <pulsecore/mutex.h> +#include <pulsecore/thread.h> +#include <pulsecore/log.h> + +#include "ltdl-bind-now.h" + +#ifdef RTLD_NOW +#define PA_BIND_NOW RTLD_NOW +#elif defined(DL_NOW) +#define PA_BIND_NOW DL_NOW +#else +#undef PA_BIND_NOW +#endif + +static pa_mutex *libtool_mutex = NULL; + +PA_STATIC_TLS_DECLARE_NO_FREE(libtool_tls); + +static void libtool_lock(void) { +    pa_mutex_lock(libtool_mutex); +} + +static void libtool_unlock(void) { +    pa_mutex_unlock(libtool_mutex); +} + +static void libtool_set_error(const char *error) { +    PA_STATIC_TLS_SET(libtool_tls, (char*) error); +} + +static const char *libtool_get_error(void) { +    return PA_STATIC_TLS_GET(libtool_tls); +} + +#ifdef PA_BIND_NOW + +/* +  To avoid lazy relocations during runtime in our RT threads we add +  our own shared object loader with uses RTLD_NOW if it is +  available. The standard ltdl loader prefers RTLD_LAZY. + +  Please note that this loader doesn't have any influence on +  relocations on any libraries that are already loaded into our +  process, i.e. because the pulseaudio binary links directly to +  them. To disable lazy relocations for those libraries it is possible +  to set $LT_BIND_NOW before starting the pulsaudio binary. +*/ + +static lt_module bind_now_open(lt_user_data d, const char *fname) { +    lt_module m; + +    pa_assert(fname); + +    if (!(m = dlopen(fname, PA_BIND_NOW))) { +        libtool_set_error(dlerror()); +        return NULL; +    } + +    return m; +} + +static int bind_now_close(lt_user_data d, lt_module m) { + +    pa_assert(m); + +    if (dlclose(m) != 0){ +        libtool_set_error(dlerror()); +        return 1; +    } + +    return 0; +} + +static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) { +    lt_ptr ptr; + +    pa_assert(m); +    pa_assert(symbol); + +    if (!(ptr = dlsym(m, symbol))) { +        libtool_set_error(dlerror()); +        return NULL; +    } + +    return ptr; +} + +#endif + +void pa_ltdl_init(void) { + +#ifdef PA_BIND_NOW +    lt_dlloader *place; +    static const struct lt_user_dlloader loader = { +        .module_open = bind_now_open, +        .module_close = bind_now_close, +        .find_sym = bind_now_find_sym +    }; +#endif + +    pa_assert_se(lt_dlinit() == 0); +    pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE)); +    pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0); + +#ifdef PA_BIND_NOW + +    if (!(place = lt_dlloader_find("dlopen"))) +        place = lt_dlloader_next(NULL); + +    /* Add our BIND_NOW loader as the default module loader. */ +    if (lt_dlloader_add(place, &loader, "bind-now-loader") != 0) +        pa_log_warn("Failed to add bind-now-loader."); +#endif +} + +void pa_ltdl_done(void) { +    pa_assert_se(lt_dlexit() == 0); +    pa_mutex_free(libtool_mutex); +    libtool_mutex = NULL; +} + diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h new file mode 100644 index 00000000..e19c7bc1 --- /dev/null +++ b/src/daemon/ltdl-bind-now.h @@ -0,0 +1,32 @@ +#ifndef foopulsecoreltdlbindnowhfoo +#define foopulsecoreltdlbindnowhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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. +***/ + + +void pa_ltdl_init(void); +void pa_ltdl_done(void); + +#endif + diff --git a/src/daemon/main.c b/src/daemon/main.c index 91cc3a2f..6c9f6627 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -33,7 +33,6 @@  #include <stdio.h>  #include <signal.h>  #include <stddef.h> -#include <assert.h>  #include <ltdl.h>  #include <limits.h>  #include <fcntl.h> @@ -59,13 +58,16 @@  #include <tcpd.h>  #endif -#include "../pulsecore/winsock.h" +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +#endif  #include <pulse/mainloop.h>  #include <pulse/mainloop-signal.h>  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> +#include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/core.h>  #include <pulsecore/memblock.h> @@ -78,12 +80,20 @@  #include <pulsecore/pid.h>  #include <pulsecore/namereg.h>  #include <pulsecore/random.h> +#include <pulsecore/rtsig.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/macro.h> +#include <pulsecore/mutex.h> +#include <pulsecore/thread.h> +#include <pulsecore/once.h> +#include <pulsecore/shm.h>  #include "cmdline.h"  #include "cpulimit.h"  #include "daemon-conf.h"  #include "dumpmodules.h"  #include "caps.h" +#include "ltdl-bind-now.h"  #ifdef HAVE_LIBWRAP  /* Only one instance of these variables */ @@ -120,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) { -    pa_log_info("Got signal %s.", pa_strsignal(sig)); +    pa_log_info("Got signal %s.", pa_sig2str(sig));      switch (sig) {  #ifdef SIGUSR1 @@ -153,14 +163,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,      }  } -static void close_pipe(int p[2]) { -    if (p[0] != -1) -        close(p[0]); -    if (p[1] != -1) -        close(p[1]); -    p[0] = p[1] = -1; -} -  #define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))  #if defined(HAVE_PWD_H) && defined(HAVE_GRP_H) @@ -281,7 +283,7 @@ static int create_runtime_dir(void) {  static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {      struct rlimit rl; -    assert(r); +    pa_assert(r);      if (!r->is_set)          return; @@ -313,13 +315,11 @@ int main(int argc, char *argv[]) {      pa_strbuf *buf = NULL;      pa_daemon_conf *conf = NULL;      pa_mainloop *mainloop = NULL; -      char *s; -    int r, retval = 1, d = 0; +    int r = 0, retval = 1, d = 0;      int daemon_pipe[2] = { -1, -1 };      int suid_root, real_root;      int valid_pid_file = 0; -      gid_t gid = (gid_t) -1;  #ifdef OS_IS_WIN32 @@ -327,6 +327,23 @@ int main(int argc, char *argv[]) {      struct timeval tv;  #endif + +#if defined(__linux__) && defined(__OPTIMIZE__) +    /* +       Disable lazy relocations to make usage of external libraries +       more deterministic for our RT threads. We abuse __OPTIMIZE__ as +       a check whether we are a debug build or not. +    */ + +    if (!getenv("LD_BIND_NOW")) { +        putenv(pa_xstrdup("LD_BIND_NOW=1")); + +        /* We have to execute ourselves, because the libc caches the +         * value of $LD_BIND_NOW on initialization. */ +        pa_assert_se(execv("/proc/self/exe", argv) == 0); +    } +#endif +  #ifdef HAVE_GETUID      real_root = getuid() == 0;      suid_root = !real_root && geteuid() == 0; @@ -336,16 +353,26 @@ int main(int argc, char *argv[]) {  #endif      if (suid_root) { -        if (pa_limit_caps() > 0) -            /* We managed to drop capabilities except the needed -             * ones. Hence we can drop the uid. */ -            pa_drop_root(); +        /* Drop all capabilities except CAP_SYS_NICE  */ +        pa_limit_caps(); + +        /* Drop priviliges, but keep CAP_SYS_NICE */ +        pa_drop_root(); + +        /* After dropping root, the effective set is reset, hence, +         * let's raise it again */ +        pa_limit_caps(); + +        /* When capabilities are not supported we will not be able to +         * aquire RT sched anymore. But yes, that's the way it is. It +         * is just too risky tun let PA run as root all the time. */      }      setlocale(LC_ALL, ""); -    if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0 || gid >= 1000)) { -        pa_log_warn("WARNING: called SUID root, but not in group '"PA_REALTIME_GROUP"'."); +    if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0)) { +        pa_log_info("Warning: Called SUID root, but not in group '"PA_REALTIME_GROUP"'. " +                    "For enabling real-time scheduling please become a member of '"PA_REALTIME_GROUP"' , or increase the RLIMIT_RTPRIO user limit.");          pa_drop_caps();          pa_drop_root();          suid_root = real_root = 0; @@ -353,8 +380,7 @@ int main(int argc, char *argv[]) {      LTDL_SET_PRELOADED_SYMBOLS(); -    r = lt_dlinit(); -    assert(r == 0); +    pa_ltdl_init();  #ifdef OS_IS_WIN32      { @@ -386,7 +412,7 @@ int main(int argc, char *argv[]) {      if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)          pa_raise_priority(); -    if (suid_root) { +    if (suid_root && (conf->cmd != PA_CMD_DAEMON || !conf->high_priority)) {          pa_drop_caps();          pa_drop_root();      } @@ -408,6 +434,16 @@ int main(int argc, char *argv[]) {              goto finish;          } +        case PA_CMD_DUMP_RESAMPLE_METHODS: { +            int i; + +            for (i = 0; i < PA_RESAMPLER_MAX; i++) +                if (pa_resample_method_supported(i)) +                    printf("%s\n", pa_resample_method_to_string(i)); + +            goto finish; +        } +          case PA_CMD_HELP :              pa_cmdline_help(argv[0]);              retval = 0; @@ -440,8 +476,15 @@ int main(int argc, char *argv[]) {              goto finish; +        case PA_CMD_CLEANUP_SHM: + +            if (pa_shm_cleanup() >= 0) +                retval = 0; + +            goto finish; +          default: -            assert(conf->cmd == PA_CMD_DAEMON); +            pa_assert(conf->cmd == PA_CMD_DAEMON);      }      if (real_root && !conf->system_instance) { @@ -474,7 +517,7 @@ int main(int argc, char *argv[]) {          if (child != 0) {              /* Father */ -            close(daemon_pipe[1]); +            pa_assert_se(pa_close(daemon_pipe[1]) == 0);              daemon_pipe[1] = -1;              if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) { @@ -490,7 +533,7 @@ int main(int argc, char *argv[]) {              goto finish;          } -        close(daemon_pipe[0]); +        pa_assert_se(pa_close(daemon_pipe[0]) == 0);          daemon_pipe[0] = -1;  #endif @@ -505,9 +548,9 @@ int main(int argc, char *argv[]) {  #endif  #ifndef OS_IS_WIN32 -        close(0); -        close(1); -        close(2); +        pa_close(0); +        pa_close(1); +        pa_close(2);          open("/dev/null", O_RDONLY);          open("/dev/null", O_WRONLY); @@ -529,12 +572,12 @@ int main(int argc, char *argv[]) {  #ifdef TIOCNOTTY          if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {              ioctl(tty_fd, TIOCNOTTY, (char*) 0); -            close(tty_fd); +            pa_assert_se(pa_close(tty_fd) == 0);          }  #endif      } -    chdir("/"); +    pa_assert_se(chdir("/") == 0);      umask(0022);      if (conf->system_instance) { @@ -564,18 +607,37 @@ int main(int argc, char *argv[]) {      signal(SIGPIPE, SIG_IGN);  #endif -    mainloop = pa_mainloop_new(); -    assert(mainloop); +    pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE); + +    if (pa_rtclock_hrtimer()) +        pa_log_info("Fresh high-resolution timers available! Bon appetit!"); +    else +        pa_log_info("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"); + +#ifdef SIGRTMIN +    /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */ +    pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1); +#endif + +    pa_assert_se(mainloop = pa_mainloop_new());      if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm))) { -    	pa_log("pa_core_new() failed."); +        pa_log("pa_core_new() failed.");          goto finish;      }      c->is_system_instance = !!conf->system_instance; - -    r = pa_signal_init(pa_mainloop_get_api(mainloop)); -    assert(r == 0); +    c->high_priority = !!conf->high_priority; +    c->default_sample_spec = conf->default_sample_spec; +    c->default_n_fragments = conf->default_n_fragments; +    c->default_fragment_size_msec = conf->default_fragment_size_msec; +    c->disallow_module_loading = conf->disallow_module_loading; +    c->exit_idle_time = conf->exit_idle_time; +    c->module_idle_time = conf->module_idle_time; +    c->scache_idle_time = conf->scache_idle_time; +    c->resample_method = conf->resample_method; + +    pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);      pa_signal_new(SIGINT, signal_callback, c);      pa_signal_new(SIGTERM, signal_callback, c); @@ -590,9 +652,7 @@ int main(int argc, char *argv[]) {  #endif  #ifdef OS_IS_WIN32 -    timer = pa_mainloop_get_api(mainloop)->time_new( -        pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL); -    assert(timer); +    pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));  #endif      if (conf->daemonize) @@ -600,10 +660,8 @@ int main(int argc, char *argv[]) {      oil_init(); -    if (!conf->no_cpu_limit) { -        r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop)); -        assert(r == 0); -    } +    if (!conf->no_cpu_limit) +        pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);      buf = pa_strbuf_new();      if (conf->default_script_file) @@ -634,12 +692,6 @@ int main(int argc, char *argv[]) {              pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);  #endif -        c->disallow_module_loading = conf->disallow_module_loading; -        c->exit_idle_time = conf->exit_idle_time; -        c->module_idle_time = conf->module_idle_time; -        c->scache_idle_time = conf->scache_idle_time; -        c->resample_method = conf->resample_method; -          if (c->default_sink_name &&              pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {              pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name); @@ -656,7 +708,7 @@ int main(int argc, char *argv[]) {      pa_mainloop_get_api(mainloop)->time_free(timer);  #endif -    pa_core_free(c); +    pa_core_unref(c);      if (!conf->no_cpu_limit)          pa_cpu_limit_done(); @@ -676,13 +728,17 @@ finish:      if (valid_pid_file)          pa_pid_file_remove(); -    close_pipe(daemon_pipe); +    pa_close_pipe(daemon_pipe);  #ifdef OS_IS_WIN32      WSACleanup();  #endif -    lt_dlexit(); +    pa_ltdl_done(); + +#ifdef HAVE_DBUS +    dbus_shutdown(); +#endif      return retval;  } diff --git a/src/daemon/pulseaudio-module-xsmp.desktop b/src/daemon/pulseaudio-module-xsmp.desktop new file mode 100644 index 00000000..fa719a73 --- /dev/null +++ b/src/daemon/pulseaudio-module-xsmp.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Encoding=UTF-8 +Name=PulseAudio Session Management +Comment=Load module-x11-xsmp into PulseAudio +Exec=pactl load-module module-x11-xsmp +Terminal=false +Type=Application +Categories= +GenericName= diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 40be5311..906de58d 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -33,6 +33,7 @@  #include <pulse/xmalloc.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "alsa-util.h" @@ -42,7 +43,6 @@ struct pa_alsa_fdlist {      /* This is a temporary buffer used to avoid lots of mallocs */      struct pollfd *work_fds; -    snd_pcm_t *pcm;      snd_mixer_t *mixer;      pa_mainloop_api *m; @@ -56,11 +56,16 @@ struct pa_alsa_fdlist {  };  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) { -    struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata; + +    struct pa_alsa_fdlist *fdl = userdata;      int err, i;      unsigned short revents; -    assert(a && fdl && (fdl->pcm || fdl->mixer) && fdl->fds && fdl->work_fds); +    pa_assert(a); +    pa_assert(fdl); +    pa_assert(fdl->mixer); +    pa_assert(fdl->fds); +    pa_assert(fdl->work_fds);      if (fdl->polled)          return; @@ -69,7 +74,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io      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; @@ -83,63 +88,46 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io          }      } -    assert(i != fdl->num_fds); - -    if (fdl->pcm) -        err = snd_pcm_poll_descriptors_revents(fdl->pcm, fdl->work_fds, fdl->num_fds, &revents); -    else -        err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents); +    pa_assert(i != fdl->num_fds); -    if (err < 0) { -        pa_log_error("Unable to get poll revent: %s", -            snd_strerror(err)); +    if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) { +        pa_log_error("Unable to get poll revent: %s", snd_strerror(err));          return;      }      a->defer_enable(fdl->defer, 1); -    if (revents) { -        if (fdl->pcm) -            fdl->cb(fdl->userdata); -        else -            snd_mixer_handle_events(fdl->mixer); -    } +    if (revents) +        snd_mixer_handle_events(fdl->mixer);  }  static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) { -    struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata; +    struct pa_alsa_fdlist *fdl = userdata;      int num_fds, i, err;      struct pollfd *temp; -    assert(a && fdl && (fdl->pcm || fdl->mixer)); +    pa_assert(a); +    pa_assert(fdl); +    pa_assert(fdl->mixer);      a->defer_enable(fdl->defer, 0); -    if (fdl->pcm) -        num_fds = snd_pcm_poll_descriptors_count(fdl->pcm); -    else -        num_fds = snd_mixer_poll_descriptors_count(fdl->mixer); -    assert(num_fds > 0); +    num_fds = snd_mixer_poll_descriptors_count(fdl->mixer); +    pa_assert(num_fds > 0);      if (num_fds != fdl->num_fds) {          if (fdl->fds)              pa_xfree(fdl->fds);          if (fdl->work_fds)              pa_xfree(fdl->work_fds); -        fdl->fds = pa_xmalloc0(sizeof(struct pollfd) * num_fds); -        fdl->work_fds = pa_xmalloc(sizeof(struct pollfd) * num_fds); +        fdl->fds = pa_xnew0(struct pollfd, num_fds); +        fdl->work_fds = pa_xnew(struct pollfd, num_fds);      }      memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds); -    if (fdl->pcm) -        err = snd_pcm_poll_descriptors(fdl->pcm, fdl->work_fds, num_fds); -    else -        err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds); - -    if (err < 0) { -        pa_log_error("Unable to get poll descriptors: %s", -            snd_strerror(err)); +    if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) { +        pa_log_error("Unable to get poll descriptors: %s", snd_strerror(err));          return;      } @@ -149,18 +137,18 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u          return;      if (fdl->ios) { -        for (i = 0;i < fdl->num_fds;i++) +        for (i = 0; i < fdl->num_fds; i++)              a->io_free(fdl->ios[i]); +          if (num_fds != fdl->num_fds) {              pa_xfree(fdl->ios); -            fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds); -            assert(fdl->ios); +            fdl->ios = NULL;          } -    } else { -        fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds); -        assert(fdl->ios);      } +    if (!fdl->ios) +        fdl->ios = pa_xnew(pa_io_event*, num_fds); +      /* Swap pointers */      temp = fdl->work_fds;      fdl->work_fds = fdl->fds; @@ -168,47 +156,41 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u      fdl->num_fds = num_fds; -    for (i = 0;i < num_fds;i++) { +    for (i = 0;i < num_fds;i++)          fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,              ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |              ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),              io_cb, fdl); -        assert(fdl->ios[i]); -    }  }  struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {      struct pa_alsa_fdlist *fdl; -    fdl = pa_xmalloc(sizeof(struct pa_alsa_fdlist)); +    fdl = pa_xnew0(struct pa_alsa_fdlist, 1);      fdl->num_fds = 0;      fdl->fds = NULL;      fdl->work_fds = NULL; - -    fdl->pcm = NULL;      fdl->mixer = NULL; -      fdl->m = NULL;      fdl->defer = NULL;      fdl->ios = NULL; -      fdl->polled = 0;      return fdl;  }  void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) { -    assert(fdl); +    pa_assert(fdl);      if (fdl->defer) { -        assert(fdl->m); +        pa_assert(fdl->m);          fdl->m->defer_free(fdl->defer);      }      if (fdl->ios) {          int i; -        assert(fdl->m); +        pa_assert(fdl->m);          for (i = 0;i < fdl->num_fds;i++)              fdl->m->io_free(fdl->ios[i]);          pa_xfree(fdl->ios); @@ -222,29 +204,15 @@ void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {      pa_xfree(fdl);  } -int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata) { -    assert(fdl && pcm_handle && m && !fdl->m && cb); - -    fdl->pcm = pcm_handle; -    fdl->m = m; - -    fdl->defer = m->defer_new(m, defer_cb, fdl); -    assert(fdl->defer); - -    fdl->cb = cb; -    fdl->userdata = userdata; - -    return 0; -} - -int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) { -    assert(fdl && mixer_handle && m && !fdl->m); +int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) { +    pa_assert(fdl); +    pa_assert(mixer_handle); +    pa_assert(m); +    pa_assert(!fdl->m);      fdl->mixer = mixer_handle;      fdl->m = m; -      fdl->defer = m->defer_new(m, defer_cb, fdl); -    assert(fdl->defer);      return 0;  } @@ -274,8 +242,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s      int i, ret; -    assert(pcm_handle); -    assert(f); +    pa_assert(pcm_handle); +    pa_assert(f);      if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)          return ret; @@ -308,7 +276,7 @@ try_auto:  /* Set the hardware parameters of the given ALSA device. Returns the   * selected fragment settings in *period and *period_size */ -int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) { +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap) {      int ret = -1;      snd_pcm_uframes_t buffer_size;      unsigned int r = ss->rate; @@ -316,17 +284,32 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p      pa_sample_format_t f = ss->format;      snd_pcm_hw_params_t *hwparams; -    assert(pcm_handle); -    assert(ss); -    assert(periods); -    assert(period_size); +    pa_assert(pcm_handle); +    pa_assert(ss); +    pa_assert(periods); +    pa_assert(period_size); + +    snd_pcm_hw_params_alloca(&hwparams);      buffer_size = *periods * *period_size; -    if ((ret = snd_pcm_hw_params_malloc(&hwparams)) < 0 || -        (ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 || -        (ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0 || -    	(ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) +    if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 || +        (ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) +        goto finish; + +    if (use_mmap && *use_mmap) { +        if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { + +            /* mmap() didn't work, fall back to interleaved */ + +            if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) +                goto finish; + +            if (use_mmap) +                *use_mmap = 0; +        } + +    } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)          goto finish;      if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) @@ -346,7 +329,7 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p          goto finish;      if (ss->rate != r) { -        pa_log_warn("device doesn't support %u Hz, changed to %u Hz.", ss->rate, r); +        pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);          /* If the sample rate deviates too much, we need to resample */          if (r < ss->rate*.95 || r > ss->rate*1.05) @@ -354,12 +337,12 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p      }      if (ss->channels != c) { -        pa_log_warn("device doesn't support %u channels, changed to %u.", ss->channels, c); +        pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);          ss->channels = c;      }      if (ss->format != f) { -        pa_log_warn("device doesn't support sample format %s, changed to %s.", pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); +        pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));          ss->format = f;      } @@ -370,24 +353,54 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p          (ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)          goto finish; -    assert(buffer_size > 0); -    assert(*period_size > 0); +    pa_assert(buffer_size > 0); +    pa_assert(*period_size > 0);      *periods = buffer_size / *period_size; -    assert(*periods > 0); +    pa_assert(*periods > 0);      ret = 0;  finish: -    if (hwparams) -        snd_pcm_hw_params_free(hwparams);      return ret;  } +int pa_alsa_set_sw_params(snd_pcm_t *pcm) { +    snd_pcm_sw_params_t *swparams; +    int err; + +    pa_assert(pcm); + +    snd_pcm_sw_params_alloca(&swparams); + +    if ((err = snd_pcm_sw_params_current(pcm, swparams) < 0)) { +        pa_log_warn("Unable to determine current swparams: %s\n", snd_strerror(err)); +        return err; +    } + +    if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) { +        pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err)); +        return err; +    } + +    if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) { +        pa_log_warn("Unable to set start threshold: %s\n", snd_strerror(err)); +        return err; +    } + +    if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) { +        pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err)); +        return err; +    } + +    return 0; +} +  int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {      int err; -    assert(mixer && dev); +    pa_assert(mixer); +    pa_assert(dev);      if ((err = snd_mixer_attach(mixer, dev)) < 0) {          pa_log_warn("Unable to attach to mixer %s: %s", dev, snd_strerror(err)); @@ -410,10 +423,11 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {  snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {      snd_mixer_elem_t *elem;      snd_mixer_selem_id_t *sid = NULL; +      snd_mixer_selem_id_alloca(&sid); -    assert(mixer); -    assert(name); +    pa_assert(mixer); +    pa_assert(name);      snd_mixer_selem_id_set_name(sid, name); diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index ea6c7e1d..6f1f927e 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -32,15 +32,14 @@  #include <pulse/channelmap.h> -struct pa_alsa_fdlist; +typedef struct pa_alsa_fdlist pa_alsa_fdlist;  struct pa_alsa_fdlist *pa_alsa_fdlist_new(void);  void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl); +int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m); -int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata); -int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m); - -int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size); +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap); +int pa_alsa_set_sw_params(snd_pcm_t *pcm);  int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);  snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback); diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c index 48a45174..fc1e91ea 100644 --- a/src/modules/dbus-util.c +++ b/src/modules/dbus-util.c @@ -26,25 +26,25 @@  #include <config.h>  #endif -#include <assert.h> -#include <pulsecore/log.h> -#include <pulsecore/props.h>  #include <pulse/xmalloc.h>  #include <pulse/timeval.h> +#include <pulsecore/log.h> +#include <pulsecore/props.h>  #include "dbus-util.h"  struct pa_dbus_connection { -    int refcount; +    PA_REFCNT_DECLARE; +      pa_core *core;      DBusConnection *connection;      const char *property_name;      pa_defer_event* dispatch_event;  }; -static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) -{ -    DBusConnection *conn = (DBusConnection *) userdata; +static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) { +    DBusConnection *conn = userdata; +      if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {          /* no more data to process, disable the deferred */          ea->defer_enable(ev, 0); @@ -52,14 +52,17 @@ static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)  }  /* DBusDispatchStatusFunction callback for the pa mainloop */ -static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, -                            void *userdata) -{ -    pa_dbus_connection *c = (pa_dbus_connection*) userdata; +static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) { +    pa_dbus_connection *c = userdata; + +    pa_assert(c); +      switch(status) { +          case DBUS_DISPATCH_COMPLETE:              c->core->mainloop->defer_enable(c->dispatch_event, 0);              break; +          case DBUS_DISPATCH_DATA_REMAINS:          case DBUS_DISPATCH_NEED_MEMORY:          default: @@ -68,11 +71,13 @@ static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,      }  } -static pa_io_event_flags_t -get_watch_flags(DBusWatch *watch) -{ -    unsigned int flags = dbus_watch_get_flags(watch); -    pa_io_event_flags_t events = PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { +    unsigned int flags; +    pa_io_event_flags_t events = 0; + +    pa_assert(watch); + +    flags = dbus_watch_get_flags(watch);      /* no watch flags for disabled watches */      if (!dbus_watch_get_enabled(watch)) @@ -83,21 +88,22 @@ get_watch_flags(DBusWatch *watch)      if (flags & DBUS_WATCH_WRITABLE)          events |= PA_IO_EVENT_OUTPUT; -    return events; +    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;  }  /* 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_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {      unsigned int flags = 0; -    DBusWatch *watch = (DBusWatch*) userdata; +    DBusWatch *watch = userdata; -    assert(fd == dbus_watch_get_fd(watch)); +#if HAVE_DBUS_WATCH_GET_UNIX_FD +    pa_assert(fd == dbus_watch_get_unix_fd(watch)); +#else +    pa_assert(fd == dbus_watch_get_fd(watch)); +#endif      if (!dbus_watch_get_enabled(watch)) { -        pa_log_warn("Asked to handle disabled watch: %p %i", -                    (void *) watch, fd); +        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);          return;      } @@ -114,10 +120,8 @@ static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,  }  /* pa_time_event_cb_t timer event handler */ -static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, -                              const struct timeval *tv, void *userdata) -{ -    DBusTimeout *timeout = (DBusTimeout*) userdata; +static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) { +    DBusTimeout *timeout = userdata;      if (dbus_timeout_get_enabled(timeout)) {          struct timeval next = *tv; @@ -130,218 +134,195 @@ static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,  }  /* DBusAddWatchFunction callback for pa mainloop */ -static dbus_bool_t add_watch(DBusWatch *watch, void *data) -{ +static dbus_bool_t add_watch(DBusWatch *watch, void *data) { +    pa_core *c = PA_CORE(data);      pa_io_event *ev; -    pa_core *c = (pa_core*) data; -    ev = c->mainloop->io_new(c->mainloop, dbus_watch_get_fd(watch), -                             get_watch_flags(watch), -                             handle_io_event, (void*) watch); -    if (NULL == ev) -        return FALSE; +    pa_assert(watch); +    pa_assert(c); -    /* dbus_watch_set_data(watch, (void*) ev, c->mainloop->io_free); */ -    dbus_watch_set_data(watch, (void*) ev, NULL); +    ev = c->mainloop->io_new( +            c->mainloop, +#if HAVE_DBUS_WATCH_GET_UNIX_FD +            dbus_watch_get_unix_fd(watch), +#else +            dbus_watch_get_fd(watch), +#endif +            get_watch_flags(watch), handle_io_event, watch); + +    dbus_watch_set_data(watch, ev, NULL);      return TRUE;  }  /* DBusRemoveWatchFunction callback for pa mainloop */ -static void remove_watch(DBusWatch *watch, void *data) -{ -    pa_core *c = (pa_core*) data; -    pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch); +static void remove_watch(DBusWatch *watch, void *data) { +    pa_core *c = PA_CORE(data); +    pa_io_event *ev; -    /* free the event */ -    if (NULL != ev) +    pa_assert(watch); +    pa_assert(c); + +    if ((ev = dbus_watch_get_data(watch)))          c->mainloop->io_free(ev);  }  /* DBusWatchToggledFunction callback for pa mainloop */ -static void toggle_watch(DBusWatch *watch, void *data) -{ -    pa_core *c = (pa_core*) data; -    pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch); +static void toggle_watch(DBusWatch *watch, void *data) { +    pa_core *c = PA_CORE(data); +    pa_io_event *ev; + +    pa_assert(watch); +    pa_core_assert_ref(c); + +    pa_assert_se(ev = dbus_watch_get_data(watch));      /* get_watch_flags() checks if the watch is enabled */      c->mainloop->io_enable(ev, get_watch_flags(watch));  }  /* DBusAddTimeoutFunction callback for pa mainloop */ -static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) -{ -    struct timeval tv; +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { +    pa_core *c = PA_CORE(data);      pa_time_event *ev; -    pa_core *c = (pa_core*) data; +    struct timeval tv; + +    pa_assert(timeout); +    pa_assert(c);      if (!dbus_timeout_get_enabled(timeout))          return FALSE; -    if (!pa_gettimeofday(&tv)) -        return -1; - +    pa_gettimeofday(&tv);      pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000); -    ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, -                               (void*) timeout); -    if (NULL == ev) -        return FALSE; +    ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout); -    /* dbus_timeout_set_data(timeout, (void*) ev, c->mainloop->time_free); */ -    dbus_timeout_set_data(timeout, (void*) ev, NULL); +    dbus_timeout_set_data(timeout, ev, NULL);      return TRUE;  }  /* DBusRemoveTimeoutFunction callback for pa mainloop */ -static void remove_timeout(DBusTimeout *timeout, void *data) -{ -    pa_core *c = (pa_core*) data; -    pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout); +static void remove_timeout(DBusTimeout *timeout, void *data) { +    pa_core *c = PA_CORE(data); +    pa_time_event *ev; + +    pa_assert(timeout); +    pa_assert(c); -    /* free the event */ -    if (NULL != ev) +    if ((ev = dbus_timeout_get_data(timeout)))          c->mainloop->time_free(ev);  }  /* DBusTimeoutToggledFunction callback for pa mainloop */ -static void toggle_timeout(DBusTimeout *timeout, void *data) -{ -    struct timeval tv; -    pa_core *c = (pa_core*) data; -    pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout); +static void toggle_timeout(DBusTimeout *timeout, void *data) { +    pa_core *c = PA_CORE(data); +    pa_time_event *ev; + +    pa_assert(timeout); +    pa_assert(c); + +    pa_assert_se(ev = dbus_timeout_get_data(timeout));      if (dbus_timeout_get_enabled(timeout)) { +        struct timeval tv; +          pa_gettimeofday(&tv);          pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000); +          c->mainloop->time_restart(ev, &tv); -    } else { -        /* disable the timeout */ +    } else          c->mainloop->time_restart(ev, NULL); -    }  } -static void -pa_dbus_connection_free(pa_dbus_connection *c) -{ -    assert(c); -    assert(!dbus_connection_get_is_connected(c->connection)); +static void wakeup_main(void *userdata) { +    pa_dbus_connection *c = userdata; -    /* already disconnected, just free */ -    pa_property_remove(c->core, c->property_name); -    c->core->mainloop->defer_free(c->dispatch_event); -    dbus_connection_unref(c->connection); -    pa_xfree(c); -} +    pa_assert(c); -static void -wakeup_main(void *userdata) -{ -    pa_dbus_connection *c = (pa_dbus_connection*) userdata;      /* this will wakeup the mainloop and dispatch events, although       * it may not be the cleanest way of accomplishing it */      c->core->mainloop->defer_enable(c->dispatch_event, 1);  } -static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) -{ -    pa_dbus_connection *pconn = pa_xnew(pa_dbus_connection, 1); +static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) { +    pa_dbus_connection *pconn; -    pconn->refcount = 1; +    pconn = pa_xnew(pa_dbus_connection, 1); +    PA_REFCNT_INIT(pconn);      pconn->core = c;      pconn->property_name = name;      pconn->connection = conn; -    pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, -                                                   (void*) conn); +    pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);      pa_property_set(c, name, pconn);      return pconn;  } -DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c) -{ -    assert(c && c->connection); +DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){ +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); +    pa_assert(c->connection); +      return c->connection;  } -void pa_dbus_connection_unref(pa_dbus_connection *c) -{ -    assert(c); +void pa_dbus_connection_unref(pa_dbus_connection *c) { +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); -    /* non-zero refcount, still outstanding refs */ -    if (--(c->refcount)) +    if (PA_REFCNT_DEC(c) > 0)          return; -    /* refcount is zero */      if (dbus_connection_get_is_connected(c->connection)) { -        /* disconnect as we have no more internal references */          dbus_connection_close(c->connection); -        /* must process remaining messages, bit of a kludge to -         * handle both unload and shutdown */ -        while(dbus_connection_read_write_dispatch(c->connection, -1)); +         /* must process remaining messages, bit of a kludge to handle +         * both unload and shutdown */ +        while (dbus_connection_read_write_dispatch(c->connection, -1));      } -    pa_dbus_connection_free(c); + +    /* already disconnected, just free */ +    pa_property_remove(c->core, c->property_name); +    c->core->mainloop->defer_free(c->dispatch_event); +    dbus_connection_unref(c->connection); +    pa_xfree(c);  } -pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) -{ -    assert(c); +pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) { +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); -    ++(c->refcount); +    PA_REFCNT_INC(c);      return c;  } -pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, -                                    DBusError *error) -{ -    const char* name; +pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) { + +    static const char *const prop_name[] = { +        [DBUS_BUS_SESSION] = "dbus-connection-session", +        [DBUS_BUS_SYSTEM] = "dbus-connection-system", +        [DBUS_BUS_STARTER] = "dbus-connection-starter" +    };      DBusConnection *conn;      pa_dbus_connection *pconn; -    switch (type) { -        case DBUS_BUS_SYSTEM: -            name = "dbus-connection-system"; -            break; -        case DBUS_BUS_SESSION: -            name = "dbus-connection-session"; -            break; -        case DBUS_BUS_STARTER: -            name = "dbus-connection-starter"; -            break; -        default: -            assert(0); /* never reached */ -            break; -    } +    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); -    if ((pconn = pa_property_get(c, name))) +    if ((pconn = pa_property_get(c, prop_name[type])))          return pa_dbus_connection_ref(pconn); -    /* else */ -    conn = dbus_bus_get_private(type, error); -    if (conn == NULL || dbus_error_is_set(error)) { +    if (!(conn = dbus_bus_get_private(type, error)))          return NULL; -    } -    pconn = pa_dbus_connection_new(c, conn, name); +    pconn = pa_dbus_connection_new(c, conn, prop_name[type]); -    /* don't exit on disconnect */      dbus_connection_set_exit_on_disconnect(conn, FALSE); -    /* set up the DBUS call backs */ -    dbus_connection_set_dispatch_status_function(conn, dispatch_status, -                                                 (void*) pconn, NULL); -    dbus_connection_set_watch_functions(conn, -                                        add_watch, -                                        remove_watch, -                                        toggle_watch, -                                        (void*) c, NULL); -    dbus_connection_set_timeout_functions(conn, -                                          add_timeout, -                                          remove_timeout, -                                          toggle_timeout, -                                          (void*) c, NULL); +    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); +    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL); +    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);      dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);      return pconn; diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c index 3483b845..abd13287 100644 --- a/src/modules/gconf/gconf-helper.c +++ b/src/modules/gconf/gconf-helper.c @@ -32,6 +32,8 @@  #include <gconf/gconf-client.h>  #include <glib.h> +#include <pulsecore/core-util.h> +  #define PA_GCONF_ROOT "/system/pulseaudio"  #define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules" @@ -40,13 +42,13 @@ static void handle_module(GConfClient *client, const char *name) {      gboolean enabled, locked;      int i; -    snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name); +    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);      locked = gconf_client_get_bool(client, p, FALSE);      if (locked)          return; -    snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name); +    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);      enabled = gconf_client_get_bool(client, p, FALSE);      printf("%c%s%c", enabled ? '+' : '-', name, 0); @@ -56,11 +58,11 @@ static void handle_module(GConfClient *client, const char *name) {          for (i = 0; i < 10; i++) {              gchar *n, *a; -            snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i); +            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);              if (!(n = gconf_client_get_string(client, p, NULL)) || !*n)                  break; -            snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i); +            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);              a = gconf_client_get_string(client, p, NULL);              printf("%s%c%s%c", n, 0, a ? a : "", 0); diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c index cbe17d20..1c8866de 100644 --- a/src/modules/gconf/module-gconf.c +++ b/src/modules/gconf/module-gconf.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <string.h>  #include <unistd.h>  #include <stdlib.h> @@ -34,6 +33,7 @@  #include <sys/types.h>  #include <sys/wait.h>  #include <fcntl.h> +#include <dirent.h>  #ifdef HAVE_SYS_PRCTL_H  #include <sys/prctl.h> @@ -95,7 +95,7 @@ struct userdata {  static int fill_buf(struct userdata *u) {      ssize_t r; -    assert(u); +    pa_assert(u);      if (u->buf_fill >= BUF_MAX) {          pa_log("read buffer overflow"); @@ -111,21 +111,21 @@ static int fill_buf(struct userdata *u) {  static int read_byte(struct userdata *u) {      int ret; -    assert(u); +    pa_assert(u);      if (u->buf_fill < 1)          if (fill_buf(u) < 0)              return -1;      ret = u->buf[0]; -    assert(u->buf_fill > 0); +    pa_assert(u->buf_fill > 0);      u->buf_fill--;      memmove(u->buf, u->buf+1, u->buf_fill);      return ret;  }  static char *read_string(struct userdata *u) { -    assert(u); +    pa_assert(u);      for (;;) {          char *e; @@ -143,9 +143,9 @@ static char *read_string(struct userdata *u) {  }  static void unload_one_module(struct userdata *u, struct module_info*m, unsigned i) { -    assert(u); -    assert(m); -    assert(i < m->n_items); +    pa_assert(u); +    pa_assert(m); +    pa_assert(i < m->n_items);      if (m->items[i].index == PA_INVALID_INDEX)          return; @@ -161,8 +161,8 @@ static void unload_one_module(struct userdata *u, struct module_info*m, unsigned  static void unload_all_modules(struct userdata *u, struct module_info*m) {      unsigned i; -    assert(u); -    assert(m); +    pa_assert(u); +    pa_assert(m);      for (i = 0; i < m->n_items; i++)          unload_one_module(u, m, i); @@ -180,10 +180,10 @@ static void load_module(      pa_module *mod; -    assert(u); -    assert(m); -    assert(name); -    assert(args); +    pa_assert(u); +    pa_assert(m); +    pa_assert(name); +    pa_assert(args);      if (!is_new) {          if (m->items[i].index != PA_INVALID_INDEX && @@ -212,8 +212,8 @@ static void module_info_free(void *p, void *userdata) {      struct module_info *m = p;      struct userdata *u = userdata; -    assert(m); -    assert(u); +    pa_assert(m); +    pa_assert(u);      unload_all_modules(u, m);      pa_xfree(m->name); @@ -356,8 +356,10 @@ static int start_client(const char *n, pid_t *pid) {          return pipe_fds[0];      } else { +#ifdef __linux__ +        DIR* d; +#endif          int max_fd, i; -          /* child */          close(pipe_fds[0]); @@ -372,18 +374,48 @@ static int start_client(const char *n, pid_t *pid) {          close(2);          open("/dev/null", O_WRONLY); -        max_fd = 1024; +#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) +                    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; -        } +            { +                struct rlimit r; +                if (getrlimit(RLIMIT_NOFILE, &r) == 0) +                    max_fd = r.rlim_max; +            }  #endif -        for (i = 3; i < max_fd; i++) -            close(i); +            for (i = 3; i < max_fd; i++) +                close(i); +# +#ifdef __linux__ +        } +#endif  #ifdef PR_SET_PDEATHSIG          /* On Linux we can use PR_SET_PDEATHSIG to have the helper @@ -413,12 +445,12 @@ fail:      return -1;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u;      int r;      u = pa_xnew(struct userdata, 1); -    u->core = c; +    u->core = m->core;      u->module = m;      m->userdata = u;      u->module_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); @@ -431,8 +463,8 @@ int pa__init(pa_core *c, pa_module*m) {      if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0)          goto fail; -    u->io_event = c->mainloop->io_new( -            c->mainloop, +    u->io_event = m->core->mainloop->io_new( +            m->core->mainloop,              u->fd,              PA_IO_EVENT_INPUT,              io_event_cb, @@ -449,21 +481,20 @@ int pa__init(pa_core *c, pa_module*m) {      return 0;  fail: -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(u = m->userdata))          return;      if (u->io_event) -        c->mainloop->io_free(u->io_event); +        m->core->mainloop->io_free(u->io_event);      if (u->fd >= 0)          close(u->fd); diff --git a/src/modules/ladspa.h b/src/modules/ladspa.h new file mode 100644 index 00000000..b1a9c4e5 --- /dev/null +++ b/src/modules/ladspa.h @@ -0,0 +1,603 @@ +/* ladspa.h + +   Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. +   Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, +   Stefan Westerfeld. + +   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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +   USA. */ + +#ifndef LADSPA_INCLUDED +#define LADSPA_INCLUDED + +#define LADSPA_VERSION "1.1" +#define LADSPA_VERSION_MAJOR 1 +#define LADSPA_VERSION_MINOR 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Overview: + +   There is a large number of synthesis packages in use or development +   on the Linux platform at this time. This API (`The Linux Audio +   Developer's Simple Plugin API') attempts to give programmers the +   ability to write simple `plugin' audio processors in C/C++ and link +   them dynamically (`plug') into a range of these packages (`hosts'). +   It should be possible for any host and any plugin to communicate +   completely through this interface. + +   This API is deliberately short and simple. To achieve compatibility +   with a range of promising Linux sound synthesis packages it +   attempts to find the `greatest common divisor' in their logical +   behaviour. Having said this, certain limiting decisions are +   implicit, notably the use of a fixed type (LADSPA_Data) for all +   data transfer and absence of a parameterised `initialisation' +   phase. See below for the LADSPA_Data typedef. + +   Plugins are expected to distinguish between control and audio +   data. Plugins have `ports' that are inputs or outputs for audio or +   control data and each plugin is `run' for a `block' corresponding +   to a short time interval measured in samples. Audio data is +   communicated using arrays of LADSPA_Data, allowing a block of audio +   to be processed by the plugin in a single pass. Control data is +   communicated using single LADSPA_Data values. Control data has a +   single value at the start of a call to the `run()' or `run_adding()' +   function, and may be considered to remain this value for its +   duration. The plugin may assume that all its input and output ports +   have been connected to the relevant data location (see the +   `connect_port()' function below) before it is asked to run. + +   Plugins will reside in shared object files suitable for dynamic +   linking by dlopen() and family. The file will provide a number of +   `plugin types' that can be used to instantiate actual plugins +   (sometimes known as `plugin instances') that can be connected +   together to perform tasks. + +   This API contains very limited error-handling. */ + +/*****************************************************************************/ + +/* Fundamental data type passed in and out of plugin. This data type +   is used to communicate audio samples and control values. It is +   assumed that the plugin will work sensibly given any numeric input +   value although it may have a preferred range (see hints below). + +   For audio it is generally assumed that 1.0f is the `0dB' reference +   amplitude and is a `normal' signal level. */ + +typedef float LADSPA_Data; + +/*****************************************************************************/ + +/* Special Plugin Properties: + +   Optional features of the plugin type are encapsulated in the +   LADSPA_Properties type. This is assembled by ORing individual +   properties together. */ + +typedef int LADSPA_Properties; + +/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a +   real-time dependency (e.g. listens to a MIDI device) and so its +   output must not be cached or subject to significant latency. */ +#define LADSPA_PROPERTY_REALTIME        0x1 + +/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin +   may cease to work correctly if the host elects to use the same data +   location for both input and output (see connect_port()). This +   should be avoided as enabling this flag makes it impossible for +   hosts to use the plugin to process audio `in-place.' */ +#define LADSPA_PROPERTY_INPLACE_BROKEN  0x2 + +/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin +   is capable of running not only in a conventional host but also in a +   `hard real-time' environment. To qualify for this the plugin must +   satisfy all of the following: + +   (1) The plugin must not use malloc(), free() or other heap memory +   management within its run() or run_adding() functions. All new +   memory used in run() must be managed via the stack. These +   restrictions only apply to the run() function. + +   (2) The plugin will not attempt to make use of any library +   functions with the exceptions of functions in the ANSI standard C +   and C maths libraries, which the host is expected to provide. + +   (3) The plugin will not access files, devices, pipes, sockets, IPC +   or any other mechanism that might result in process or thread +   blocking. + +   (4) The plugin will take an amount of time to execute a run() or +   run_adding() call approximately of form (A+B*SampleCount) where A +   and B depend on the machine and host in use. This amount of time +   may not depend on input signals or plugin state. The host is left +   the responsibility to perform timings to estimate upper bounds for +   A and B. */ +#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 + +#define LADSPA_IS_REALTIME(x)        ((x) & LADSPA_PROPERTY_REALTIME) +#define LADSPA_IS_INPLACE_BROKEN(x)  ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) +#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) + +/*****************************************************************************/ + +/* Plugin Ports: + +   Plugins have `ports' that are inputs or outputs for audio or +   data. Ports can communicate arrays of LADSPA_Data (for audio +   inputs/outputs) or single LADSPA_Data values (for control +   input/outputs). This information is encapsulated in the +   LADSPA_PortDescriptor type which is assembled by ORing individual +   properties together. + +   Note that a port must be an input or an output port but not both +   and that a port must be a control or audio port but not both. */ + +typedef int LADSPA_PortDescriptor; + +/* Property LADSPA_PORT_INPUT indicates that the port is an input. */ +#define LADSPA_PORT_INPUT   0x1 + +/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ +#define LADSPA_PORT_OUTPUT  0x2 + +/* Property LADSPA_PORT_CONTROL indicates that the port is a control +   port. */ +#define LADSPA_PORT_CONTROL 0x4 + +/* Property LADSPA_PORT_AUDIO indicates that the port is a audio +   port. */ +#define LADSPA_PORT_AUDIO   0x8 + +#define LADSPA_IS_PORT_INPUT(x)   ((x) & LADSPA_PORT_INPUT) +#define LADSPA_IS_PORT_OUTPUT(x)  ((x) & LADSPA_PORT_OUTPUT) +#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) +#define LADSPA_IS_PORT_AUDIO(x)   ((x) & LADSPA_PORT_AUDIO) + +/*****************************************************************************/ + +/* Plugin Port Range Hints: + +   The host may wish to provide a representation of data entering or +   leaving a plugin (e.g. to generate a GUI automatically). To make +   this more meaningful, the plugin should provide `hints' to the host +   describing the usual values taken by the data. + +   Note that these are only hints. The host may ignore them and the +   plugin must not assume that data supplied to it is meaningful. If +   the plugin receives invalid input data it is expected to continue +   to run without failure and, where possible, produce a sensible +   output (e.g. a high-pass filter given a negative cutoff frequency +   might switch to an all-pass mode). + +   Hints are meaningful for all input and output ports but hints for +   input control ports are expected to be particularly useful. + +   More hint information is encapsulated in the +   LADSPA_PortRangeHintDescriptor type which is assembled by ORing +   individual hint types together. Hints may require further +   LowerBound and UpperBound information. + +   All the hint information for a particular port is aggregated in the +   LADSPA_PortRangeHint structure. */ + +typedef int LADSPA_PortRangeHintDescriptor; + +/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field +   of the LADSPA_PortRangeHint should be considered meaningful. The +   value in this field should be considered the (inclusive) lower +   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also +   specified then the value of LowerBound should be multiplied by the +   sample rate. */ +#define LADSPA_HINT_BOUNDED_BELOW   0x1 + +/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field +   of the LADSPA_PortRangeHint should be considered meaningful. The +   value in this field should be considered the (inclusive) upper +   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also +   specified then the value of UpperBound should be multiplied by the +   sample rate. */ +#define LADSPA_HINT_BOUNDED_ABOVE   0x2 + +/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be +   considered a Boolean toggle. Data less than or equal to zero should +   be considered `off' or `false,' and data above zero should be +   considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in +   conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or +   LADSPA_HINT_DEFAULT_1. */ +#define LADSPA_HINT_TOGGLED         0x4 + +/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified +   should be interpreted as multiples of the sample rate. For +   instance, a frequency range from 0Hz to the Nyquist frequency (half +   the sample rate) could be requested by this hint in conjunction +   with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds +   at all must support this hint to retain meaning. */ +#define LADSPA_HINT_SAMPLE_RATE     0x8 + +/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the +   user will find it more intuitive to view values using a logarithmic +   scale. This is particularly useful for frequencies and gains. */ +#define LADSPA_HINT_LOGARITHMIC     0x10 + +/* Hint LADSPA_HINT_INTEGER indicates that a user interface would +   probably wish to provide a stepped control taking only integer +   values. Any bounds set should be slightly wider than the actual +   integer range required to avoid floating point rounding errors. For +   instance, the integer set {0,1,2,3} might be described as [-0.1, +   3.1]. */ +#define LADSPA_HINT_INTEGER         0x20 + +/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' +   value for the port that is sensible as a default. For instance, +   this value is suitable for use as an initial value in a user +   interface or as a value the host might assign to a control port +   when the user has not provided one. Defaults are encoded using a +   mask so only one default may be specified for a port. Some of the +   hints make use of lower and upper bounds, in which case the +   relevant bound or bounds must be available and +   LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting +   default must be rounded if LADSPA_HINT_INTEGER is present. Default +   values were introduced in LADSPA v1.1. */ +#define LADSPA_HINT_DEFAULT_MASK    0x3C0 + +/* This default values indicates that no default is provided. */ +#define LADSPA_HINT_DEFAULT_NONE    0x0 + +/* This default hint indicates that the suggested lower bound for the +   port should be used. */ +#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 + +/* This default hint indicates that a low value between the suggested +   lower and upper bounds should be chosen. For ports with +   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + +   log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper +   * 0.25). */ +#define LADSPA_HINT_DEFAULT_LOW     0x80 + +/* This default hint indicates that a middle value between the +   suggested lower and upper bounds should be chosen. For ports with +   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + +   log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * +   0.5). */ +#define LADSPA_HINT_DEFAULT_MIDDLE  0xC0 + +/* This default hint indicates that a high value between the suggested +   lower and upper bounds should be chosen. For ports with +   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + +   log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper +   * 0.75). */ +#define LADSPA_HINT_DEFAULT_HIGH    0x100 + +/* This default hint indicates that the suggested upper bound for the +   port should be used. */ +#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 + +/* This default hint indicates that the number 0 should be used. Note +   that this default may be used in conjunction with +   LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_0       0x200 + +/* This default hint indicates that the number 1 should be used. Note +   that this default may be used in conjunction with +   LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_1       0x240 + +/* This default hint indicates that the number 100 should be used. */ +#define LADSPA_HINT_DEFAULT_100     0x280 + +/* This default hint indicates that the Hz frequency of `concert A' +   should be used. This will be 440 unless the host uses an unusual +   tuning convention, in which case it may be within a few Hz. */ +#define LADSPA_HINT_DEFAULT_440     0x2C0 + +#define LADSPA_IS_HINT_BOUNDED_BELOW(x)   ((x) & LADSPA_HINT_BOUNDED_BELOW) +#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)   ((x) & LADSPA_HINT_BOUNDED_ABOVE) +#define LADSPA_IS_HINT_TOGGLED(x)         ((x) & LADSPA_HINT_TOGGLED) +#define LADSPA_IS_HINT_SAMPLE_RATE(x)     ((x) & LADSPA_HINT_SAMPLE_RATE) +#define LADSPA_IS_HINT_LOGARITHMIC(x)     ((x) & LADSPA_HINT_LOGARITHMIC) +#define LADSPA_IS_HINT_INTEGER(x)         ((x) & LADSPA_HINT_INTEGER) + +#define LADSPA_IS_HINT_HAS_DEFAULT(x)     ((x) & LADSPA_HINT_DEFAULT_MASK) +#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_MINIMUM) +#define LADSPA_IS_HINT_DEFAULT_LOW(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_LOW) +#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x)  (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_MIDDLE) +#define LADSPA_IS_HINT_DEFAULT_HIGH(x)    (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_HIGH) +#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_MAXIMUM) +#define LADSPA_IS_HINT_DEFAULT_0(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_0) +#define LADSPA_IS_HINT_DEFAULT_1(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_1) +#define LADSPA_IS_HINT_DEFAULT_100(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                           == LADSPA_HINT_DEFAULT_100) +#define LADSPA_IS_HINT_DEFAULT_440(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \ +                                            == LADSPA_HINT_DEFAULT_440) + +typedef struct _LADSPA_PortRangeHint { + +  /* Hints about the port. */ +  LADSPA_PortRangeHintDescriptor HintDescriptor; + +  /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When +     LADSPA_HINT_SAMPLE_RATE is also active then this value should be +     multiplied by the relevant sample rate. */ +  LADSPA_Data LowerBound; + +  /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When +     LADSPA_HINT_SAMPLE_RATE is also active then this value should be +     multiplied by the relevant sample rate. */ +  LADSPA_Data UpperBound; + +} LADSPA_PortRangeHint; + +/*****************************************************************************/ + +/* Plugin Handles: + +   This plugin handle indicates a particular instance of the plugin +   concerned. It is valid to compare this to NULL (0 for C++) but +   otherwise the host should not attempt to interpret it. The plugin +   may use it to reference internal instance data. */ + +typedef void * LADSPA_Handle; + +/*****************************************************************************/ + +/* Descriptor for a Type of Plugin: + +   This structure is used to describe a plugin type. It provides a +   number of functions to examine the type, instantiate it, link it to +   buffers and workspaces and to run it. */ + +typedef struct _LADSPA_Descriptor { + +  /* This numeric identifier indicates the plugin type +     uniquely. Plugin programmers may reserve ranges of IDs from a +     central body to avoid clashes. Hosts may assume that IDs are +     below 0x1000000. */ +  unsigned long UniqueID; + +  /* This identifier can be used as a unique, case-sensitive +     identifier for the plugin type within the plugin file. Plugin +     types should be identified by file and label rather than by index +     or plugin name, which may be changed in new plugin +     versions. Labels must not contain white-space characters. */ +  const char * Label; + +  /* This indicates a number of properties of the plugin. */ +  LADSPA_Properties Properties; + +  /* This member points to the null-terminated name of the plugin +     (e.g. "Sine Oscillator"). */ +  const char * Name; + +  /* This member points to the null-terminated string indicating the +     maker of the plugin. This can be an empty string but not NULL. */ +  const char * Maker; + +  /* This member points to the null-terminated string indicating any +     copyright applying to the plugin. If no Copyright applies the +     string "None" should be used. */ +  const char * Copyright; + +  /* This indicates the number of ports (input AND output) present on +     the plugin. */ +  unsigned long PortCount; + +  /* This member indicates an array of port descriptors. Valid indices +     vary from 0 to PortCount-1. */ +  const LADSPA_PortDescriptor * PortDescriptors; + +  /* This member indicates an array of null-terminated strings +     describing ports (e.g. "Frequency (Hz)"). Valid indices vary from +     0 to PortCount-1. */ +  const char * const * PortNames; + +  /* This member indicates an array of range hints for each port (see +     above). Valid indices vary from 0 to PortCount-1. */ +  const LADSPA_PortRangeHint * PortRangeHints; + +  /* This may be used by the plugin developer to pass any custom +     implementation data into an instantiate call. It must not be used +     or interpreted by the host. It is expected that most plugin +     writers will not use this facility as LADSPA_Handle should be +     used to hold instance data. */ +  void * ImplementationData; + +  /* This member is a function pointer that instantiates a plugin. A +     handle is returned indicating the new plugin instance. The +     instantiation function accepts a sample rate as a parameter. The +     plugin descriptor from which this instantiate function was found +     must also be passed. This function must return NULL if +     instantiation fails. + +     Note that instance initialisation should generally occur in +     activate() rather than here. */ +  LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, +                               unsigned long                     SampleRate); + +  /* This member is a function pointer that connects a port on an +     instantiated plugin to a memory location at which a block of data +     for the port will be read/written. The data location is expected +     to be an array of LADSPA_Data for audio ports or a single +     LADSPA_Data value for control ports. Memory issues will be +     managed by the host. The plugin must read/write the data at these +     locations every time run() or run_adding() is called and the data +     present at the time of this connection call should not be +     considered meaningful. + +     connect_port() may be called more than once for a plugin instance +     to allow the host to change the buffers that the plugin is +     reading or writing. These calls may be made before or after +     activate() or deactivate() calls. + +     connect_port() must be called at least once for each port before +     run() or run_adding() is called. When working with blocks of +     LADSPA_Data the plugin should pay careful attention to the block +     size passed to the run function as the block allocated may only +     just be large enough to contain the block of samples. + +     Plugin writers should be aware that the host may elect to use the +     same buffer for more than one port and even use the same buffer +     for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). +     However, overlapped buffers or use of a single buffer for both +     audio and control data may result in unexpected behaviour. */ +   void (*connect_port)(LADSPA_Handle Instance, +                        unsigned long Port, +                        LADSPA_Data * DataLocation); + +  /* This member is a function pointer that initialises a plugin +     instance and activates it for use. This is separated from +     instantiate() to aid real-time support and so that hosts can +     reinitialise a plugin instance by calling deactivate() and then +     activate(). In this case the plugin instance must reset all state +     information dependent on the history of the plugin instance +     except for any data locations provided by connect_port() and any +     gain set by set_run_adding_gain(). If there is nothing for +     activate() to do then the plugin writer may provide a NULL rather +     than an empty function. + +     When present, hosts must call this function once before run() (or +     run_adding()) is called for the first time. This call should be +     made as close to the run() call as possible and indicates to +     real-time plugins that they are now live. Plugins should not rely +     on a prompt call to run() after activate(). activate() may not be +     called again unless deactivate() is called first. Note that +     connect_port() may be called before or after a call to +     activate(). */ +  void (*activate)(LADSPA_Handle Instance); + +  /* This method is a function pointer that runs an instance of a +     plugin for a block. Two parameters are required: the first is a +     handle to the particular instance to be run and the second +     indicates the block size (in samples) for which the plugin +     instance may run. + +     Note that if an activate() function exists then it must be called +     before run() or run_adding(). If deactivate() is called for a +     plugin instance then the plugin instance may not be reused until +     activate() has been called again. + +     If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE +     then there are various things that the plugin should not do +     within the run() or run_adding() functions (see above). */ +  void (*run)(LADSPA_Handle Instance, +              unsigned long SampleCount); + +  /* This method is a function pointer that runs an instance of a +     plugin for a block. This has identical behaviour to run() except +     in the way data is output from the plugin. When run() is used, +     values are written directly to the memory areas associated with +     the output ports. However when run_adding() is called, values +     must be added to the values already present in the memory +     areas. Furthermore, output values written must be scaled by the +     current gain set by set_run_adding_gain() (see below) before +     addition. + +     run_adding() is optional. When it is not provided by a plugin, +     this function pointer must be set to NULL. When it is provided, +     the function set_run_adding_gain() must be provided also. */ +  void (*run_adding)(LADSPA_Handle Instance, +                     unsigned long SampleCount); + +  /* This method is a function pointer that sets the output gain for +     use when run_adding() is called (see above). If this function is +     never called the gain is assumed to default to 1. Gain +     information should be retained when activate() or deactivate() +     are called. + +     This function should be provided by the plugin if and only if the +     run_adding() function is provided. When it is absent this +     function pointer must be set to NULL. */ +  void (*set_run_adding_gain)(LADSPA_Handle Instance, +                              LADSPA_Data   Gain); + +  /* This is the counterpart to activate() (see above). If there is +     nothing for deactivate() to do then the plugin writer may provide +     a NULL rather than an empty function. + +     Hosts must deactivate all activated units after they have been +     run() (or run_adding()) for the last time. This call should be +     made as close to the last run() call as possible and indicates to +     real-time plugins that they are no longer live. Plugins should +     not rely on prompt deactivation. Note that connect_port() may be +     called before or after a call to deactivate(). + +     Deactivation is not similar to pausing as the plugin instance +     will be reinitialised when activate() is called to reuse it. */ +  void (*deactivate)(LADSPA_Handle Instance); + +  /* Once an instance of a plugin has been finished with it can be +     deleted using the following function. The instance handle passed +     ceases to be valid after this call. + +     If activate() was called for a plugin instance then a +     corresponding call to deactivate() must be made before cleanup() +     is called. */ +  void (*cleanup)(LADSPA_Handle Instance); + +} LADSPA_Descriptor; + +/**********************************************************************/ + +/* Accessing a Plugin: */ + +/* The exact mechanism by which plugins are loaded is host-dependent, +   however all most hosts will need to know is the name of shared +   object file containing the plugin types. To allow multiple hosts to +   share plugin types, hosts may wish to check for environment +   variable LADSPA_PATH. If present, this should contain a +   colon-separated path indicating directories that should be searched +   (in order) when loading plugin types. + +   A plugin programmer must include a function called +   "ladspa_descriptor" with the following function prototype within +   the shared object file. This function will have C-style linkage (if +   you are using C++ this is taken care of by the `extern "C"' clause +   at the top of the file). + +   A host will find the plugin shared object file by one means or +   another, find the ladspa_descriptor() function, call it, and +   proceed from there. + +   Plugin types are accessed by index (not ID) using values from 0 +   upwards. Out of range indexes must result in this function +   returning NULL, so the plugin count can be determined by checking +   for the least index that results in NULL being returned. */ + +const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); + +/* Datatype corresponding to the ladspa_descriptor() function. */ +typedef const LADSPA_Descriptor * +(*LADSPA_Descriptor_Function)(unsigned long Index); + +/**********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* LADSPA_INCLUDED */ + +/* EOF */ diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 3d9f7577..a09247fe 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -26,18 +26,12 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h> -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> -#else -#include "poll.h" -#endif -  #include <asoundlib.h>  #include <pulse/xmalloc.h> +#include <pulse/util.h>  #include <pulsecore/core.h>  #include <pulsecore/module.h> @@ -47,6 +41,11 @@  #include <pulsecore/core-util.h>  #include <pulsecore/sample-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread.h> +#include <pulsecore/core-error.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h>  #include "alsa-util.h"  #include "module-alsa-sink-symdef.h" @@ -62,20 +61,38 @@ PA_MODULE_USAGE(          "rate=<sample rate> "          "fragments=<number of fragments> "          "fragment_size=<fragment size> " -        "channel_map=<channel map>") +        "channel_map=<channel map> " +        "mmap=<enable memory mapping?>") + +#define DEFAULT_DEVICE "default"  struct userdata { +    pa_core *core; +    pa_module *module; +    pa_sink *sink; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +      snd_pcm_t *pcm_handle; + +    pa_alsa_fdlist *mixer_fdl;      snd_mixer_t *mixer_handle;      snd_mixer_elem_t *mixer_elem; -    pa_sink *sink; -    struct pa_alsa_fdlist *pcm_fdl; -    struct pa_alsa_fdlist *mixer_fdl;      long hw_volume_max, hw_volume_min; -    size_t frame_size, fragment_size; -    pa_memchunk memchunk, silence; -    pa_module *module; +    size_t frame_size, fragment_size, hwbuf_size; +    unsigned nfragments; +    pa_memchunk memchunk; + +    char *device_name; + +    int use_mmap; + +    int first; + +    pa_rtpoll_item *alsa_rtpoll_item;  };  static const char* const valid_modargs[] = { @@ -87,260 +104,440 @@ static const char* const valid_modargs[] = {      "fragments",      "fragment_size",      "channel_map", +    "mmap",      NULL  }; -#define DEFAULT_DEVICE "default" +static int mmap_write(struct userdata *u) { +    int work_done = 0; -static void update_usage(struct userdata *u) { -    pa_module_set_used(u->module, u->sink ? pa_sink_used_by(u->sink) : 0); -} +    pa_assert(u); +    pa_sink_assert_ref(u->sink); -static void clear_up(struct userdata *u) { -    assert(u); +    for (;;) { +        pa_memchunk chunk; +        void *p; +        snd_pcm_sframes_t n; +        int err; +        const snd_pcm_channel_area_t *areas; +        snd_pcm_uframes_t offset, frames; + +        if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) { + +            if (n == -EPIPE) { +                pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); +                u->first = 1; +            } -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); -        u->sink = NULL; -    } +            if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) +                continue; -    if (u->pcm_fdl) -        pa_alsa_fdlist_free(u->pcm_fdl); -    if (u->mixer_fdl) -        pa_alsa_fdlist_free(u->mixer_fdl); +            if (err == -EAGAIN) +                return work_done; -    u->pcm_fdl = u->mixer_fdl = NULL; +            pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); +            return -1; +        } -    if (u->mixer_handle) { -        snd_mixer_close(u->mixer_handle); -        u->mixer_handle = NULL; -    } +/*         pa_log("Got request for %i samples", (int) n); */ -    if (u->pcm_handle) { -        snd_pcm_drop(u->pcm_handle); -        snd_pcm_close(u->pcm_handle); -        u->pcm_handle = NULL; -    } -} +        if (n <= 0) +            return work_done; -static int xrun_recovery(struct userdata *u) { -    int ret; -    assert(u); +        frames = n; -    pa_log_info("*** ALSA-XRUN (playback) ***"); +        if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) { -    if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) { -        pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret)); +            if (err == -EPIPE) { +                pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); +                u->first = 1; +            } -        clear_up(u); -        pa_module_unload_request(u->module); -        return -1; -    } +            if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) +                continue; -    return ret; -} +            if (err == -EAGAIN) +                return work_done; -static int suspend_recovery(struct userdata *u) { -    int ret; -    assert(u); +            pa_log("Failed to write data to DSP: %s", snd_strerror(err)); +            return -1; +        } -    pa_log_info("*** ALSA-SUSPEND (playback) ***"); +        /* Check these are multiples of 8 bit */ +        pa_assert((areas[0].first & 7) == 0); +        pa_assert((areas[0].step & 7)== 0); -    if ((ret = snd_pcm_resume(u->pcm_handle)) < 0) { -        if (ret == -EAGAIN) -            return -1; +        /* We assume a single interleaved memory buffer */ +        pa_assert((areas[0].first >> 3) == 0); +        pa_assert((areas[0].step >> 3) == u->frame_size); -        if (ret != -ENOSYS) -            pa_log("snd_pcm_resume() failed: %s", snd_strerror(-ret)); -        else { -            if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) -                pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret)); -        } +        p = (uint8_t*) areas[0].addr + (offset * u->frame_size); + +        chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1); +        chunk.length = pa_memblock_get_length(chunk.memblock); +        chunk.index = 0; + +        pa_sink_render_into_full(u->sink, &chunk); + +        /* FIXME: Maybe we can do something to keep this memory block +         * a little bit longer around? */ +        pa_memblock_unref_fixed(chunk.memblock); + +        if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) { + +            if (err == -EPIPE) { +                pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); +                u->first = 1; +            } + +            if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) +                continue; -        if (ret < 0) { -            clear_up(u); -            pa_module_unload_request(u->module); +            if (err == -EAGAIN) +                return work_done; + +            pa_log("Failed to write data to DSP: %s", snd_strerror(err));              return -1;          } -    } -    return ret; +        work_done = 1; + +        if (frames >= (snd_pcm_uframes_t) n) +            return work_done; + +/*         pa_log("wrote %i samples", (int) frames); */ +    }  } -static void do_write(struct userdata *u) { -    assert(u); +static int unix_write(struct userdata *u) { +    snd_pcm_status_t *status; +    int work_done = 0; -    update_usage(u); +    snd_pcm_status_alloca(&status); + +    pa_assert(u); +    pa_sink_assert_ref(u->sink);      for (;;) { -        pa_memchunk *memchunk = NULL; -        snd_pcm_sframes_t frames; - -        if (u->memchunk.memblock) -            memchunk = &u->memchunk; -        else { -            if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0) -                memchunk = &u->silence; -            else -                memchunk = &u->memchunk; +        void *p; +        snd_pcm_sframes_t t; +        ssize_t l; +        int err; + +        if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) { +            pa_log("Failed to query DSP status data: %s", snd_strerror(err)); +            return -1;          } -        assert(memchunk->memblock); -        assert(memchunk->memblock->data); -        assert(memchunk->length); -        assert(memchunk->memblock->length); -        assert((memchunk->length % u->frame_size) == 0); +        if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size) +            pa_log_debug("Buffer underrun!"); -        if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { -            if (frames == -EAGAIN) -                return; +        l = snd_pcm_status_get_avail(status) * u->frame_size; -            if (frames == -EPIPE) { -                if (xrun_recovery(u) < 0) -                    return; +/*         pa_log("%u bytes to write", l); */ -                continue; -            } +        if (l <= 0) +            return work_done; + +        if (u->memchunk.length <= 0) +            pa_sink_render(u->sink, l, &u->memchunk); + +        pa_assert(u->memchunk.length > 0); -            if (frames == -ESTRPIPE) { -                if (suspend_recovery(u) < 0) -                    return; +        p = pa_memblock_acquire(u->memchunk.memblock); +        t = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size); +        pa_memblock_release(u->memchunk.memblock); +/*         pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */ + +        pa_assert(t != 0); + +        if (t < 0) { + +            if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)                  continue; + +            if (t == -EAGAIN) { +                pa_log_debug("EAGAIN"); +                return work_done; +            } else { +                pa_log("Failed to write data to DSP: %s", snd_strerror(t)); +                return -1;              } +        } -            pa_log("snd_pcm_writei() failed: %s", snd_strerror(-frames)); +        u->memchunk.index += t * u->frame_size; +        u->memchunk.length -= t * u->frame_size; -            clear_up(u); -            pa_module_unload_request(u->module); -            return; +        if (u->memchunk.length <= 0) { +            pa_memblock_unref(u->memchunk.memblock); +            pa_memchunk_reset(&u->memchunk);          } -        if (memchunk == &u->memchunk) { -            size_t l = frames * u->frame_size; -            memchunk->index += l; -            memchunk->length -= l; +        work_done = 1; -            if (memchunk->length == 0) { -                pa_memblock_unref(memchunk->memblock); -                memchunk->memblock = NULL; -                memchunk->index = memchunk->length = 0; -            } -        } +        if (t * u->frame_size >= (unsigned) l) +            return work_done; +    } +} -        break; +static pa_usec_t sink_get_latency(struct userdata *u) { +    pa_usec_t r = 0; +    snd_pcm_status_t *status; +    snd_pcm_sframes_t frames = 0; +    int err; + +    snd_pcm_status_alloca(&status); + +    pa_assert(u); +    pa_assert(u->pcm_handle); + +    if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) +        pa_log("Failed to get delay: %s", snd_strerror(err)); +    else +        frames = snd_pcm_status_get_delay(status); + +    if (frames > 0) +        r = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); + +    if (u->memchunk.memblock) +        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); + +    return r; +} + +static int build_pollfd(struct userdata *u) { +    int err; +    struct pollfd *pollfd; +    int n; + +    pa_assert(u); +    pa_assert(u->pcm_handle); + +    if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) { +        pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); +        return -1; +    } + +    if (u->alsa_rtpoll_item) +        pa_rtpoll_item_free(u->alsa_rtpoll_item); + +    u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n); +    pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL); + +    if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) { +        pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); +        return -1;      } + +    return 0;  } -static void fdl_callback(void *userdata) { -    struct userdata *u = userdata; -    assert(u); +static int suspend(struct userdata *u) { +    pa_assert(u); +    pa_assert(u->pcm_handle); -    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) -        if (xrun_recovery(u) < 0) -            return; +    /* Let's suspend */ +    snd_pcm_drain(u->pcm_handle); +    snd_pcm_close(u->pcm_handle); +    u->pcm_handle = NULL; -    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_SUSPENDED) -        if (suspend_recovery(u) < 0) -            return; +    if (u->alsa_rtpoll_item) { +        pa_rtpoll_item_free(u->alsa_rtpoll_item); +        u->alsa_rtpoll_item = NULL; +    } + +    pa_log_info("Device suspended..."); -    do_write(u); +    return 0;  } -static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { -    struct userdata *u = snd_mixer_elem_get_callback_private(elem); +static int unsuspend(struct userdata *u) { +    pa_sample_spec ss; +    int err, b; +    unsigned nfrags; +    snd_pcm_uframes_t period_size; -    assert(u && u->mixer_handle); +    pa_assert(u); +    pa_assert(!u->pcm_handle); -    if (mask == SND_CTL_EVENT_MASK_REMOVE) -        return 0; +    pa_log_info("Trying resume..."); -    if (mask & SND_CTL_EVENT_MASK_VALUE) { -        if (u->sink->get_hw_volume) -            u->sink->get_hw_volume(u->sink); -        if (u->sink->get_hw_mute) -            u->sink->get_hw_mute(u->sink); -        pa_subscription_post(u->sink->core, -            PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, -            u->sink->index); +    snd_config_update_free_global(); +    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { +        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err)); +        goto fail; +    } + +    ss = u->sink->sample_spec; +    nfrags = u->nfragments; +    period_size = u->fragment_size / u->frame_size; +    b = u->use_mmap; + +    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) { +        pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +        goto fail; +    } + +    if (b != u->use_mmap) { +        pa_log_warn("Resume failed, couldn't get original access mode."); +        goto fail; +    } + +    if (!pa_sample_spec_equal(&ss, &u->sink->sample_spec)) { +        pa_log_warn("Resume failed, couldn't restore original sample settings."); +        goto fail; +    } + +    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { +        pa_log_warn("Resume failed, couldn't restore original fragment settings."); +        goto fail; +    } + +    if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { +        pa_log("Failed to set software parameters: %s", snd_strerror(err)); +        goto fail;      } +    if (build_pollfd(u) < 0) +        goto fail; + +    /* FIXME: We need to reload the volume somehow */ + +    u->first = 1; + +    pa_log_info("Resumed successfully..."); +      return 0; + +fail: +    if (u->pcm_handle) { +        snd_pcm_close(u->pcm_handle); +        u->pcm_handle = NULL; +    } + +    return -1;  } -static pa_usec_t sink_get_latency_cb(pa_sink *s) { -    pa_usec_t r = 0; -    struct userdata *u = s->userdata; -    snd_pcm_sframes_t frames; -    int err; +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; -    assert(s && u && u->sink); +    switch (code) { -    if ((err = snd_pcm_delay(u->pcm_handle, &frames)) < 0) { -        pa_log("failed to get delay: %s", snd_strerror(err)); -        s->get_latency = NULL; -        return 0; +        case PA_SINK_MESSAGE_GET_LATENCY: { +            pa_usec_t r = 0; + +            if (u->pcm_handle) +                r = sink_get_latency(u); + +            *((pa_usec_t*) data) = r; + +            return 0; +        } + +        case PA_SINK_MESSAGE_SET_STATE: + +            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + +                case PA_SINK_SUSPENDED: +                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + +                    if (suspend(u) < 0) +                        return -1; + +                    break; + +                case PA_SINK_IDLE: +                case PA_SINK_RUNNING: + +                    if (u->sink->thread_info.state == PA_SINK_INIT) { +                        if (build_pollfd(u) < 0) +                            return -1; +                    } + +                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { +                        if (unsuspend(u) < 0) +                            return -1; +                    } + +                    break; + +                case PA_SINK_UNLINKED: +                case PA_SINK_INIT: +                    ; +            } + +            break;      } -    if (frames < 0) -        frames = 0; +    return pa_sink_process_msg(o, code, data, offset, chunk); +} -    r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); +static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { +    struct userdata *u = snd_mixer_elem_get_callback_private(elem); -    if (u->memchunk.memblock) -        r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); +    pa_assert(u); +    pa_assert(u->mixer_handle); -    return r; +    if (mask == SND_CTL_EVENT_MASK_REMOVE) +        return 0; + +    if (mask & SND_CTL_EVENT_MASK_VALUE) { +        pa_sink_get_volume(u->sink); +        pa_sink_get_mute(u->sink); +    } + +    return 0;  } -static int sink_get_hw_volume_cb(pa_sink *s) { +static int sink_get_volume_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err;      int i; -    assert(u); -    assert(u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    for (i = 0; i < s->hw_volume.channels; i++) { +    for (i = 0; i < s->sample_spec.channels; i++) {          long set_vol, vol; -        assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i)); +        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));          if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, i, &vol)) < 0)              goto fail; -        set_vol = (long) roundf(((float) s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; +        set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;          /* Try to avoid superfluous volume changes */          if (set_vol != vol) -            s->hw_volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +            s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));      }      return 0;  fail:      pa_log_error("Unable to read volume: %s", snd_strerror(err)); -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; + +    s->get_volume = NULL; +    s->set_volume = NULL;      return -1;  } -static int sink_set_hw_volume_cb(pa_sink *s) { +static int sink_set_volume_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err;      int i; -    pa_volume_t vol; -    assert(u); -    assert(u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    for (i = 0; i < s->hw_volume.channels; i++) { +    for (i = 0; i < s->sample_spec.channels; i++) {          long alsa_vol; +        pa_volume_t vol; -        assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i)); +        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i)); -        vol = s->hw_volume.values[i]; +        vol = s->volume.values[i];          if (vol > PA_VOLUME_NORM)              vol = PA_VOLUME_NORM; @@ -355,55 +552,166 @@ static int sink_set_hw_volume_cb(pa_sink *s) {  fail:      pa_log_error("Unable to set volume: %s", snd_strerror(err)); -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; + +    s->get_volume = NULL; +    s->set_volume = NULL;      return -1;  } -static int sink_get_hw_mute_cb(pa_sink *s) { +static int sink_get_mute_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err, sw; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw); -    if (err) { +    if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {          pa_log_error("Unable to get switch: %s", snd_strerror(err)); -        s->get_hw_mute = NULL; -        s->set_hw_mute = NULL; + +        s->get_mute = NULL; +        s->set_mute = NULL;          return -1;      } -    s->hw_muted = !sw; +    s->muted = !sw;      return 0;  } -static int sink_set_hw_mute_cb(pa_sink *s) { +static int sink_set_mute_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->hw_muted); -    if (err) { +    if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {          pa_log_error("Unable to set switch: %s", snd_strerror(err)); -        s->get_hw_mute = NULL; -        s->set_hw_mute = NULL; + +        s->get_mute = NULL; +        s->set_mute = NULL;          return -1;      }      return 0;  } -int pa__init(pa_core *c, pa_module*m) { +static void thread_func(void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    if (u->core->high_priority) +        pa_make_realtime(); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    for (;;) { +        int ret; + +        /* Render some data and write it to the dsp */ +        if (PA_SINK_OPENED(u->sink->thread_info.state)) { +            int work_done = 0; + +            if (u->use_mmap) { +                if ((work_done = mmap_write(u)) < 0) +                    goto fail; +            } else { +                if ((work_done = unix_write(u)) < 0) +                    goto fail; +            } + +            if (work_done && u->first) { +                pa_log_info("Starting playback."); +                snd_pcm_start(u->pcm_handle); +                u->first = 0; +                continue; +            } +        } + +        /* Hmm, nothing to do. Let's sleep */ +        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; + +        /* Tell ALSA about this and process its response */ +        if (PA_SINK_OPENED(u->sink->thread_info.state)) { +            struct pollfd *pollfd; +            unsigned short revents = 0; +            int err; +            unsigned n; + +            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n); + +            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) { +                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err)); +                goto fail; +            } + +            if (revents & (POLLERR|POLLNVAL|POLLHUP)) { + +                if (revents & POLLERR) +                    pa_log_warn("Got POLLERR from ALSA"); +                if (revents & POLLNVAL) +                    pa_log_warn("Got POLLNVAL from ALSA"); +                if (revents & POLLHUP) +                    pa_log_warn("Got POLLHUP from ALSA"); + +                /* Try to recover from this error */ + +                switch (snd_pcm_state(u->pcm_handle)) { + +                    case SND_PCM_STATE_XRUN: +                        if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; + +                    case SND_PCM_STATE_SUSPENDED: +                        if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; + +                    default: + +                        snd_pcm_drop(u->pcm_handle); + +                        if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; +                } +            } +        } +    } + +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: +    pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { +      pa_modargs *ma = NULL; -    int ret = -1;      struct userdata *u = NULL; -    const char *dev; +    char *dev;      pa_sample_spec ss;      pa_channel_map map; -    uint32_t periods, fragsize; +    uint32_t nfrags, frag_size;      snd_pcm_uframes_t period_size;      size_t frame_size;      snd_pcm_info_t *pcm_info = NULL; @@ -412,48 +720,107 @@ int pa__init(pa_core *c, pa_module*m) {      const char *name;      char *name_buf = NULL;      int namereg_fail; +    int use_mmap = 1, b; + +    snd_pcm_info_alloca(&pcm_info); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("Failed to parse module arguments");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) { -        pa_log("failed to parse sample specification and channel map"); +        pa_log("Failed to parse sample specification and channel map");          goto fail;      }      frame_size = pa_frame_size(&ss); -    /* Fix latency to 100ms */ -    periods = 8; -    fragsize = pa_bytes_per_second(&ss)/128; +    nfrags = m->core->default_n_fragments; +    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); +    if (frag_size <= 0) +        frag_size = frame_size; + +    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) { +        pa_log("Failed to parse buffer metrics"); +        goto fail; +    } +    period_size = frag_size/frame_size; -    if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { -        pa_log("failed to parse buffer metrics"); +    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { +        pa_log("Failed to parse mmap argument.");          goto fail;      } -    period_size = fragsize/frame_size;      u = pa_xnew0(struct userdata, 1); -    m->userdata = u; +    u->core = m->core;      u->module = m; +    m->userdata = u; +    u->use_mmap = use_mmap; +    u->first = 1; +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    u->alsa_rtpoll_item = NULL; +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);      snd_config_update_free_global(); -    if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { -        pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err)); -        goto fail; + +    dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + +    for (;;) { + +        if ((err = snd_pcm_open(&u->pcm_handle, dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { +            pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err)); +            pa_xfree(dev); +            goto fail; +        } + +        b = use_mmap; +        if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) { + +            if (err == -EPERM) { +                /* Hmm, some hw is very exotic, so we retry with plughw, if hw didn't work */ + +                if (pa_startswith(dev, "hw:")) { +                    char *d = pa_sprintf_malloc("plughw:%s", dev+3); +                    pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", dev, d); +                    pa_xfree(dev); +                    dev = d; + +                    snd_pcm_close(u->pcm_handle); +                    u->pcm_handle = NULL; +                    continue; +                } +            } + +            pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +            pa_xfree(dev); +            goto fail; +        } + +        break;      } -    if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 || -        (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) { +    u->device_name = dev; + +    if (use_mmap && !b) { +        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); +        u->use_mmap = use_mmap = b; +    } + +    if (u->use_mmap) +        pa_log_info("Successfully enabled mmap() mode."); + +    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {          pa_log("Error fetching PCM info: %s", snd_strerror(err));          goto fail;      } -    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { -        pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +    if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { +        pa_log("Failed to set software parameters: %s", snd_strerror(err));          goto fail;      } @@ -464,15 +831,16 @@ int pa__init(pa_core *c, pa_module*m) {          /* Seems ALSA didn't like the channel number, so let's fix the channel map */          pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); -    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) { -        pa_log("Error opening mixer: %s", snd_strerror(err)); -        goto fail; -    } +    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) +        pa_log_warn("Error opening mixer: %s", snd_strerror(err)); +    else { -    if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) || -        !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "PCM", "Master"))) { -        snd_mixer_close(u->mixer_handle); -        u->mixer_handle = NULL; +        if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) || +            !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM"))) { + +            snd_mixer_close(u->mixer_handle); +            u->mixer_handle = NULL; +        }      }      if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) @@ -482,114 +850,146 @@ int pa__init(pa_core *c, pa_module*m) {          namereg_fail = 0;      } -    if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map))) { +    u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map); +    pa_xfree(name_buf); + +    if (!u->sink) {          pa_log("Failed to create sink object");          goto fail;      } -    u->sink->is_hardware = 1; -    u->sink->get_latency = sink_get_latency_cb; +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->userdata = u; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc( +                                    "ALSA PCM on %s (%s)%s", +                                    dev, +                                    snd_pcm_info_get_name(pcm_info), +                                    use_mmap ? " via DMA" : "")); +    pa_xfree(t); + +    u->sink->flags = PA_SINK_HARDWARE|PA_SINK_HW_VOLUME_CTRL|PA_SINK_LATENCY; + +    u->frame_size = frame_size; +    u->fragment_size = frag_size = period_size * frame_size; +    u->nfragments = nfrags; +    u->hwbuf_size = u->fragment_size * nfrags; + +    pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size); + +    pa_memchunk_reset(&u->memchunk); +      if (u->mixer_handle) { -        assert(u->mixer_elem); +        /* Initialize mixer code */ + +        pa_assert(u->mixer_elem); +          if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {              int i; -            for (i = 0;i < ss.channels;i++) { +            for (i = 0; i < ss.channels; i++)                  if (!snd_mixer_selem_has_playback_channel(u->mixer_elem, i))                      break; -            }              if (i == ss.channels) { -                u->sink->get_hw_volume = sink_get_hw_volume_cb; -                u->sink->set_hw_volume = sink_set_hw_volume_cb; -                snd_mixer_selem_get_playback_volume_range( -                    u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max); -            } +                pa_log_debug("ALSA device has separate volumes controls for all %u channels.", ss.channels); +                u->sink->get_volume = sink_get_volume_cb; +                u->sink->set_volume = sink_set_volume_cb; +                snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max); +            } else +                pa_log_info("ALSA device lacks separate volumes controls for all %u channels (%u available), falling back to software volume control.", ss.channels, i+1);          } +          if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { -            u->sink->get_hw_mute = sink_get_hw_mute_cb; -            u->sink->set_hw_mute = sink_set_hw_mute_cb; +            u->sink->get_mute = sink_get_mute_cb; +            u->sink->set_mute = sink_set_mute_cb;          } -    } -    u->sink->userdata = u; -    pa_sink_set_owner(u->sink, m); -    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("ALSA PCM on %s (%s)", dev, snd_pcm_info_get_name(pcm_info))); -    pa_xfree(t); - -    u->pcm_fdl = pa_alsa_fdlist_new(); -    assert(u->pcm_fdl); -    if (pa_alsa_fdlist_init_pcm(u->pcm_fdl, u->pcm_handle, c->mainloop, fdl_callback, u) < 0) { -        pa_log("failed to initialise file descriptor monitoring"); -        goto fail; -    } -    if (u->mixer_handle) {          u->mixer_fdl = pa_alsa_fdlist_new(); -        assert(u->mixer_fdl); -        if (pa_alsa_fdlist_init_mixer(u->mixer_fdl, u->mixer_handle, c->mainloop) < 0) { + +        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {              pa_log("failed to initialise file descriptor monitoring");              goto fail;          } +          snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);          snd_mixer_elem_set_callback_private(u->mixer_elem, u);      } else          u->mixer_fdl = NULL; -    u->frame_size = frame_size; -    u->fragment_size = period_size * frame_size; - -    pa_log_info("using %u fragments of size %lu bytes.", periods, (long unsigned)u->fragment_size); - -    u->silence.memblock = pa_memblock_new(c->mempool, u->silence.length = u->fragment_size); -    assert(u->silence.memblock); -    pa_silence_memblock(u->silence.memblock, &ss); -    u->silence.index = 0; - -    u->memchunk.memblock = NULL; -    u->memchunk.index = u->memchunk.length = 0; - -    ret = 0; +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    }      /* Get initial mixer settings */ -    if (u->sink->get_hw_volume) -        u->sink->get_hw_volume(u->sink); -    if (u->sink->get_hw_mute) -        u->sink->get_hw_mute(u->sink); +    if (u->sink->get_volume) +        u->sink->get_volume(u->sink); +    if (u->sink->get_mute) +        u->sink->get_mute(u->sink); -finish: - -    pa_xfree(name_buf); +    pa_sink_put(u->sink); -     if (ma) -         pa_modargs_free(ma); +    pa_modargs_free(ma); -    if (pcm_info) -        snd_pcm_info_free(pcm_info); - -    return ret; +    return 0;  fail: -    if (u) -        pa__done(c, m); +    if (ma) +        pa_modargs_free(ma); -    goto finish; +    pa__done(m); + +    return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; -    clear_up(u); +    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->memchunk.memblock)          pa_memblock_unref(u->memchunk.memblock); -    if (u->silence.memblock) -        pa_memblock_unref(u->silence.memblock); +    if (u->alsa_rtpoll_item) +        pa_rtpoll_item_free(u->alsa_rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->mixer_fdl) +        pa_alsa_fdlist_free(u->mixer_fdl); + +    if (u->mixer_handle) +        snd_mixer_close(u->mixer_handle); + +    if (u->pcm_handle) { +        snd_pcm_drop(u->pcm_handle); +        snd_pcm_close(u->pcm_handle); +    } + +    pa_xfree(u->device_name);      pa_xfree(u); -} +    snd_config_update_free_global(); +} diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 4061d668..d840cac3 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -26,18 +26,12 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h> -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> -#else -#include "poll.h" -#endif -  #include <asoundlib.h>  #include <pulse/xmalloc.h> +#include <pulse/util.h>  #include <pulsecore/core-error.h>  #include <pulsecore/core.h> @@ -48,6 +42,11 @@  #include <pulsecore/core-util.h>  #include <pulsecore/sample-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread.h> +#include <pulsecore/core-error.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h>  #include "alsa-util.h"  #include "module-alsa-source-symdef.h" @@ -63,20 +62,35 @@ PA_MODULE_USAGE(          "rate=<sample rate> "          "fragments=<number of fragments> "          "fragment_size=<fragment size> " -        "channel_map=<channel map>") +        "channel_map=<channel map> " +        "mmap=<enable memory mapping?>") + +#define DEFAULT_DEVICE "default"  struct userdata { +    pa_core *core; +    pa_module *module; +    pa_source *source; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +      snd_pcm_t *pcm_handle; + +    pa_alsa_fdlist *mixer_fdl;      snd_mixer_t *mixer_handle;      snd_mixer_elem_t *mixer_elem; -    pa_source *source; -    struct pa_alsa_fdlist *pcm_fdl; -    struct pa_alsa_fdlist *mixer_fdl;      long hw_volume_max, hw_volume_min; -    size_t frame_size, fragment_size; -    pa_memchunk memchunk; -    pa_module *module; +    size_t frame_size, fragment_size, hwbuf_size; +    unsigned nfragments; + +    char *device_name; + +    int use_mmap; + +    pa_rtpoll_item *alsa_rtpoll_item;  };  static const char* const valid_modargs[] = { @@ -88,257 +102,438 @@ static const char* const valid_modargs[] = {      "fragments",      "fragment_size",      "channel_map", +    "mmap",      NULL  }; -#define DEFAULT_DEVICE "default" +static int mmap_read(struct userdata *u) { +    int work_done = 0; -static void update_usage(struct userdata *u) { -   pa_module_set_used(u->module, u->source ? pa_source_used_by(u->source) : 0); -} +    pa_assert(u); +    pa_source_assert_ref(u->source); -static void clear_up(struct userdata *u) { -    assert(u); +    for (;;) { +        snd_pcm_sframes_t n; +        int err; +        const snd_pcm_channel_area_t *areas; +        snd_pcm_uframes_t offset, frames; +        pa_memchunk chunk; +        void *p; -    if (u->source) { -        pa_source_disconnect(u->source); -        pa_source_unref(u->source); -        u->source = NULL; -    } +        if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) { -    if (u->pcm_fdl) -        pa_alsa_fdlist_free(u->pcm_fdl); -    if (u->mixer_fdl) -        pa_alsa_fdlist_free(u->mixer_fdl); +            if (n == -EPIPE) +                pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); -    u->pcm_fdl = u->mixer_fdl = NULL; +            if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) +                continue; -    if (u->mixer_handle) { -        snd_mixer_close(u->mixer_handle); -        u->mixer_handle = NULL; -    } +            if (err == -EAGAIN) +                return work_done; -    if (u->pcm_handle) { -        snd_pcm_drop(u->pcm_handle); -        snd_pcm_close(u->pcm_handle); -        u->pcm_handle = NULL; -    } -} +            pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); +            return -1; +        } -static int xrun_recovery(struct userdata *u) { -    int ret; -    assert(u); +/*         pa_log("Got request for %i samples", (int) n); */ -    pa_log_info("*** ALSA-XRUN (capture) ***"); +        if (n <= 0) +            return work_done; -    if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) { -        pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret)); +        frames = n; -        clear_up(u); -        pa_module_unload_request(u->module); +        if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) { -        return -1; -    } +            if (err == -EPIPE) +                pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); -    return 0; -} +            if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) +                continue; +            if (err == -EAGAIN) +                return work_done; -static int suspend_recovery(struct userdata *u) { -    int ret; -    assert(u); +            pa_log("Failed to write data to DSP: %s", snd_strerror(err)); +            return -1; +        } -    pa_log_info("*** ALSA-SUSPEND (capture) ***"); +        /* Check these are multiples of 8 bit */ +        pa_assert((areas[0].first & 7) == 0); +        pa_assert((areas[0].step & 7)== 0); -    if ((ret = snd_pcm_resume(u->pcm_handle)) < 0) { -        if (ret == -EAGAIN) -            return -1; +        /* We assume a single interleaved memory buffer */ +        pa_assert((areas[0].first >> 3) == 0); +        pa_assert((areas[0].step >> 3) == u->frame_size); -        if (ret != -ENOSYS) -            pa_log("snd_pcm_resume() failed: %s", snd_strerror(-ret)); -        else { -            if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) -                pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret)); -        } +        p = (uint8_t*) areas[0].addr + (offset * u->frame_size); + +        chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1); +        chunk.length = pa_memblock_get_length(chunk.memblock); +        chunk.index = 0; + +        pa_source_post(u->source, &chunk); + +        /* FIXME: Maybe we can do something to keep this memory block +         * a little bit longer around? */ +        pa_memblock_unref_fixed(chunk.memblock); + +        if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) { + +            if (err == -EPIPE) +                pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); -        if (ret < 0) { -            clear_up(u); -            pa_module_unload_request(u->module); +            if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) +                continue; + +            if (err == -EAGAIN) +                return work_done; + +            pa_log("Failed to write data to DSP: %s", snd_strerror(err));              return -1;          } -    } -    return ret; +        work_done = 1; + +/*         pa_log("wrote %i samples", (int) frames); */ +    }  } -static void do_read(struct userdata *u) { -    assert(u); +static int unix_read(struct userdata *u) { +    snd_pcm_status_t *status; +    int work_done = 0; -    update_usage(u); +    snd_pcm_status_alloca(&status); -    for (;;) { -        pa_memchunk post_memchunk; -        snd_pcm_sframes_t frames; -        size_t l; +    pa_assert(u); +    pa_source_assert_ref(u->source); -        if (!u->memchunk.memblock) { -            u->memchunk.memblock = pa_memblock_new(u->source->core->mempool, u->memchunk.length = u->fragment_size); -            u->memchunk.index = 0; +    for (;;) { +        void *p; +        snd_pcm_sframes_t t, k; +        ssize_t l; +        int err; +        pa_memchunk chunk; + +        if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) { +            pa_log("Failed to query DSP status data: %s", snd_strerror(err)); +            return -1;          } -        assert(u->memchunk.memblock); -        assert(u->memchunk.length); -        assert(u->memchunk.memblock->data); -        assert(u->memchunk.memblock->length); -        assert(u->memchunk.length % u->frame_size == 0); +        if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size) +            pa_log_debug("Buffer overrun!"); -        if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { -            if (frames == -EAGAIN) -                return; +        l = snd_pcm_status_get_avail(status) * u->frame_size; -            if (frames == -EPIPE) { -                if (xrun_recovery(u) < 0) -                    return; +        if (l <= 0) +            return work_done; -                continue; -            } +        chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); + +        k = pa_memblock_get_length(chunk.memblock); + +        if (k > l) +            k = l; + +        k = (k/u->frame_size)*u->frame_size; -            if (frames == -ESTRPIPE) { -                if (suspend_recovery(u) < 0) -                    return; +        p = pa_memblock_acquire(chunk.memblock); +        t = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, k / u->frame_size); +        pa_memblock_release(chunk.memblock); +/*                     pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l);   */ + +        pa_assert(t != 0); + +        if (t < 0) { +            pa_memblock_unref(chunk.memblock); + +            if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)                  continue; + +            if (t == -EAGAIN) { +                pa_log_debug("EAGAIN"); +                return work_done; +            } else { +                pa_log("Failed to read data from DSP: %s", snd_strerror(t)); +                return -1;              } +        } -            pa_log("snd_pcm_readi() failed: %s", snd_strerror(-frames)); +        chunk.index = 0; +        chunk.length = t * u->frame_size; -            clear_up(u); -            pa_module_unload_request(u->module); -            return; -        } +        pa_source_post(u->source, &chunk); +        pa_memblock_unref(chunk.memblock); -        l = frames * u->frame_size; +        work_done = 1; -        post_memchunk = u->memchunk; -        post_memchunk.length = l; +        if (t * u->frame_size >= (unsigned) l) +            return work_done; +    } +} -        pa_source_post(u->source, &post_memchunk); +static pa_usec_t source_get_latency(struct userdata *u) { +    pa_usec_t r = 0; +    snd_pcm_status_t *status; +    snd_pcm_sframes_t frames = 0; +    int err; -        u->memchunk.index += l; -        u->memchunk.length -= l; +    snd_pcm_status_alloca(&status); -        if (u->memchunk.length == 0) { -            pa_memblock_unref(u->memchunk.memblock); -            u->memchunk.memblock = NULL; -            u->memchunk.index = u->memchunk.length = 0; -        } +    pa_assert(u); +    pa_assert(u->pcm_handle); -        break; +    if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) +        pa_log("Failed to get delay: %s", snd_strerror(err)); +    else +        frames = snd_pcm_status_get_delay(status); + +    if (frames > 0) +        r = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); + +    return r; +} + +static int build_pollfd(struct userdata *u) { +    int err; +    struct pollfd *pollfd; +    int n; + +    pa_assert(u); +    pa_assert(u->pcm_handle); + +    if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) { +        pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); +        return -1;      } + +    if (u->alsa_rtpoll_item) +        pa_rtpoll_item_free(u->alsa_rtpoll_item); + +    u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n); +    pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL); + +    if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) { +        pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); +        return -1; +    } + +    return 0;  } -static void fdl_callback(void *userdata) { -    struct userdata *u = userdata; -    assert(u); +static int suspend(struct userdata *u) { +    pa_assert(u); +    pa_assert(u->pcm_handle); -    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) -        if (xrun_recovery(u) < 0) -            return; +    /* Let's suspend */ +    snd_pcm_close(u->pcm_handle); +    u->pcm_handle = NULL; -    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_SUSPENDED) -        if (suspend_recovery(u) < 0) -            return; +    if (u->alsa_rtpoll_item) { +        pa_rtpoll_item_free(u->alsa_rtpoll_item); +        u->alsa_rtpoll_item = NULL; +    } -    do_read(u); +    pa_log_info("Device suspended..."); + +    return 0;  } -static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { -    struct userdata *u = snd_mixer_elem_get_callback_private(elem); +static int unsuspend(struct userdata *u) { +    pa_sample_spec ss; +    int err, b; +    unsigned nfrags; +    snd_pcm_uframes_t period_size; -    assert(u && u->mixer_handle); +    pa_assert(u); +    pa_assert(!u->pcm_handle); -    if (mask == SND_CTL_EVENT_MASK_REMOVE) -        return 0; +    pa_log_info("Trying resume..."); -    if (mask & SND_CTL_EVENT_MASK_VALUE) { -        if (u->source->get_hw_volume) -            u->source->get_hw_volume(u->source); -        if (u->source->get_hw_mute) -            u->source->get_hw_mute(u->source); +    snd_config_update_free_global(); +    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { +        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err)); +        goto fail; +    } -        pa_subscription_post(u->source->core, -            PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, -            u->source->index); +    ss = u->source->sample_spec; +    nfrags = u->nfragments; +    period_size = u->fragment_size / u->frame_size; +    b = u->use_mmap; + +    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) { +        pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +        goto fail;      } +    if (b != u->use_mmap) { +        pa_log_warn("Resume failed, couldn't get original access mode."); +        goto fail; +    } + +    if (!pa_sample_spec_equal(&ss, &u->source->sample_spec)) { +        pa_log_warn("Resume failed, couldn't restore original sample settings."); +        goto fail; +    } + +    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { +        pa_log_warn("Resume failed, couldn't restore original fragment settings."); +        goto fail; +    } + +    if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { +        pa_log("Failed to set software parameters: %s", snd_strerror(err)); +        goto fail; +    } + +    if (build_pollfd(u) < 0) +        goto fail; + +    snd_pcm_start(u->pcm_handle); + +    /* FIXME: We need to reload the volume somehow */ + +    pa_log_info("Resumed successfully..."); +      return 0; + +fail: +    if (u->pcm_handle) { +        snd_pcm_close(u->pcm_handle); +        u->pcm_handle = NULL; +    } + +    return -1;  } -static pa_usec_t source_get_latency_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    snd_pcm_sframes_t frames; -    assert(s && u && u->source); +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SOURCE(o)->userdata; + +    switch (code) { + +        case PA_SOURCE_MESSAGE_GET_LATENCY: { +            pa_usec_t r = 0; + +            if (u->pcm_handle) +                r = source_get_latency(u); + +            *((pa_usec_t*) data) = r; + +            return 0; +        } + +        case PA_SOURCE_MESSAGE_SET_STATE: + +            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { + +                case PA_SOURCE_SUSPENDED: +                    pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state)); + +                    if (suspend(u) < 0) +                        return -1; + +                    break; + +                case PA_SOURCE_IDLE: +                case PA_SOURCE_RUNNING: + +                    if (u->source->thread_info.state == PA_SOURCE_INIT) { +                        if (build_pollfd(u) < 0) +                            return -1; + +                        snd_pcm_start(u->pcm_handle); +                    } + +                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { +                        if (unsuspend(u) < 0) +                            return -1; +                    } + +                    break; -    if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { -        pa_log("failed to get delay"); -        s->get_latency = NULL; +                case PA_SOURCE_UNLINKED: +                case PA_SOURCE_INIT: +                    ; +            } + +            break; +    } + +    return pa_source_process_msg(o, code, data, offset, chunk); +} + +static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { +    struct userdata *u = snd_mixer_elem_get_callback_private(elem); + +    pa_assert(u); +    pa_assert(u->mixer_handle); + +    if (mask == SND_CTL_EVENT_MASK_REMOVE)          return 0; + +    if (mask & SND_CTL_EVENT_MASK_VALUE) { +        pa_source_get_volume(u->source); +        pa_source_get_mute(u->source);      } -    return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); +    return 0;  } -static int source_get_hw_volume_cb(pa_source *s) { +static int source_get_volume_cb(pa_source *s) {      struct userdata *u = s->userdata; -    long vol;      int err;      int i; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    for (i = 0;i < s->hw_volume.channels;i++) { -        long set_vol; +    for (i = 0; i < s->sample_spec.channels; i++) { +        long set_vol, vol; -        assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i)); +        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));          if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, i, &vol)) < 0)              goto fail; -        set_vol = (long) roundf(((float) s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; +        set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;          /* Try to avoid superfluous volume changes */          if (set_vol != vol) -            s->hw_volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +            s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));      }      return 0;  fail:      pa_log_error("Unable to read volume: %s", snd_strerror(err)); -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; + +    s->get_volume = NULL; +    s->set_volume = NULL;      return -1;  } -static int source_set_hw_volume_cb(pa_source *s) { +static int source_set_volume_cb(pa_source *s) {      struct userdata *u = s->userdata;      int err; -    pa_volume_t vol;      int i; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    for (i = 0;i < s->hw_volume.channels;i++) { -        assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i)); +    for (i = 0; i < s->sample_spec.channels; i++) { +        long alsa_vol; +        pa_volume_t vol; -        vol = s->hw_volume.values[i]; +        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i)); + +        vol = s->volume.values[i];          if (vol > PA_VOLUME_NORM)              vol = PA_VOLUME_NORM; -        vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; +        alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; -        if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, vol)) < 0) +        if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, alsa_vol)) < 0)              goto fail;      } @@ -346,55 +541,159 @@ static int source_set_hw_volume_cb(pa_source *s) {  fail:      pa_log_error("Unable to set volume: %s", snd_strerror(err)); -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; + +    s->get_volume = NULL; +    s->set_volume = NULL;      return -1;  } -static int source_get_hw_mute_cb(pa_source *s) { +static int source_get_mute_cb(pa_source *s) {      struct userdata *u = s->userdata;      int err, sw; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw); -    if (err) { +    if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {          pa_log_error("Unable to get switch: %s", snd_strerror(err)); -        s->get_hw_mute = NULL; -        s->set_hw_mute = NULL; + +        s->get_mute = NULL; +        s->set_mute = NULL;          return -1;      } -    s->hw_muted = !sw; +    s->muted = !sw;      return 0;  } -static int source_set_hw_mute_cb(pa_source *s) { +static int source_set_mute_cb(pa_source *s) {      struct userdata *u = s->userdata;      int err; -    assert(u && u->mixer_elem); +    pa_assert(u); +    pa_assert(u->mixer_elem); -    err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->hw_muted); -    if (err) { +    if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {          pa_log_error("Unable to set switch: %s", snd_strerror(err)); -        s->get_hw_mute = NULL; -        s->set_hw_mute = NULL; + +        s->get_mute = NULL; +        s->set_mute = NULL;          return -1;      }      return 0;  } -int pa__init(pa_core *c, pa_module*m) { +static void thread_func(void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    if (u->core->high_priority) +        pa_make_realtime(); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    for (;;) { +        int ret; + +        /* Read some data and pass it to the sources */ +        if (PA_SOURCE_OPENED(u->source->thread_info.state)) { + +            if (u->use_mmap) { +                if (mmap_read(u) < 0) +                    goto fail; + +            } else { +                if (unix_read(u) < 0) +                    goto fail; +            } +        } + +        /* Hmm, nothing to do. Let's sleep */ +        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; + +        /* Tell ALSA about this and process its response */ +        if (PA_SOURCE_OPENED(u->source->thread_info.state)) { +            struct pollfd *pollfd; +            unsigned short revents = 0; +            int err; +            unsigned n; + +            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n); + +            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) { +                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err)); +                goto fail; +            } + +            if (revents & (POLLERR|POLLNVAL|POLLHUP)) { + +                if (revents & POLLERR) +                    pa_log_warn("Got POLLERR from ALSA"); +                if (revents & POLLNVAL) +                    pa_log_warn("Got POLLNVAL from ALSA"); +                if (revents & POLLHUP) +                    pa_log_warn("Got POLLHUP from ALSA"); + +                /* Try to recover from this error */ + +                switch (snd_pcm_state(u->pcm_handle)) { + +                    case SND_PCM_STATE_XRUN: +                        if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; + +                    case SND_PCM_STATE_SUSPENDED: +                        if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; + +                    default: + +                        snd_pcm_drop(u->pcm_handle); + +                        if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) { +                            pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); +                            goto fail; +                        } +                        break; +                } +            } +        } +    } + +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: +    pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { +      pa_modargs *ma = NULL; -    int ret = -1;      struct userdata *u = NULL; -    const char *dev; +    char *dev;      pa_sample_spec ss;      pa_channel_map map; -    unsigned periods, fragsize; +    unsigned nfrags, frag_size;      snd_pcm_uframes_t period_size;      size_t frame_size;      snd_pcm_info_t *pcm_info = NULL; @@ -403,64 +702,125 @@ int pa__init(pa_core *c, pa_module*m) {      const char *name;      char *name_buf = NULL;      int namereg_fail; +    int use_mmap = 1, b; + +    snd_pcm_info_alloca(&pcm_info); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("Failed to parse module arguments");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) { -        pa_log("failed to parse sample specification"); +        pa_log("Failed to parse sample specification");          goto fail;      }      frame_size = pa_frame_size(&ss); -    /* Fix latency to 100ms */ -    periods = 12; -    fragsize = pa_bytes_per_second(&ss)/128; +    nfrags = m->core->default_n_fragments; +    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); +    if (frag_size <= 0) +        frag_size = frame_size; + +    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) { +        pa_log("Failed to parse buffer metrics"); +        goto fail; +    } +    period_size = frag_size/frame_size; -    if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { -        pa_log("failed to parse buffer metrics"); +    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { +        pa_log("Failed to parse mmap argument.");          goto fail;      } -    period_size = fragsize/frame_size;      u = pa_xnew0(struct userdata, 1); -    m->userdata = u; +    u->core = m->core;      u->module = m; +    m->userdata = u; +    u->use_mmap = use_mmap; +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    u->alsa_rtpoll_item = NULL; +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);      snd_config_update_free_global(); -    if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { -        pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err)); -        goto fail; + +    dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + +    for (;;) { + +        if ((err = snd_pcm_open(&u->pcm_handle, dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { +            pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err)); +            pa_xfree(dev); +            goto fail; +        } + +        b = use_mmap; +        if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) { + +            if (err == -EPERM) { +                /* Hmm, some hw is very exotic, so we retry with plughw, if hw didn't work */ + +                if (pa_startswith(dev, "hw:")) { +                    char *d = pa_sprintf_malloc("plughw:%s", dev+3); +                    pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", dev, d); +                    pa_xfree(dev); +                    dev = d; + +                    snd_pcm_close(u->pcm_handle); +                    u->pcm_handle = NULL; +                    continue; +                } +            } + +            pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +            pa_xfree(dev); +            goto fail; +        } + +        break;      } -    if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 || -        (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) { +    u->device_name = dev; + +    if (use_mmap && !b) { +        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); +        u->use_mmap = use_mmap = b; +    } + +    if (u->use_mmap) +        pa_log_info("Successfully enabled mmap() mode."); + +    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {          pa_log("Error fetching PCM info: %s", snd_strerror(err));          goto fail;      } -    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { -        pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); +    if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { +        pa_log("Failed to set software parameters: %s", snd_strerror(err));          goto fail;      } +    /* ALSA might tweak the sample spec, so recalculate the frame size */ +    frame_size = pa_frame_size(&ss); +      if (ss.channels != map.channels)          /* Seems ALSA didn't like the channel number, so let's fix the channel map */          pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); -    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) { +    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)          pa_log("Error opening mixer: %s", snd_strerror(err)); -        goto fail; -    } +    else { -    if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) || -        !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic"))) { -        snd_mixer_close(u->mixer_handle); -        u->mixer_handle = NULL; +        if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) || +            !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", NULL))) { +            snd_mixer_close(u->mixer_handle); +            u->mixer_handle = NULL; +        }      }      if ((name = pa_modargs_get_value(ma, "source_name", NULL))) @@ -470,16 +830,39 @@ int pa__init(pa_core *c, pa_module*m) {          namereg_fail = 0;      } -    if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map))) { +    u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map); +    pa_xfree(name_buf); + +    if (!u->source) {          pa_log("Failed to create source object");          goto fail;      } -    u->source->is_hardware = 1; +    u->source->parent.process_msg = source_process_msg;      u->source->userdata = u; -    u->source->get_latency = source_get_latency_cb; + +    pa_source_set_module(u->source, m); +    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +    pa_source_set_rtpoll(u->source, u->rtpoll); +    pa_source_set_description(u->source, t = pa_sprintf_malloc( +                                      "ALSA PCM on %s (%s)%s", +                                      dev, +                                      snd_pcm_info_get_name(pcm_info), +                                      use_mmap ? " via DMA" : "")); +    pa_xfree(t); + +    u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL; + +    u->frame_size = frame_size; +    u->fragment_size = frag_size = period_size * frame_size; +    u->nfragments = nfrags; +    u->hwbuf_size = u->fragment_size * nfrags; + +    pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size); +      if (u->mixer_handle) { -        assert(u->mixer_elem); +        pa_assert(u->mixer_elem); +          if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {              int i; @@ -489,89 +872,95 @@ int pa__init(pa_core *c, pa_module*m) {              }              if (i == ss.channels) { -                u->source->get_hw_volume = source_get_hw_volume_cb; -                u->source->set_hw_volume = source_set_hw_volume_cb; -                snd_mixer_selem_get_capture_volume_range( -                    u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max); +                u->source->get_volume = source_get_volume_cb; +                u->source->set_volume = source_set_volume_cb; +                snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);              }          } +          if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) { -            u->source->get_hw_mute = source_get_hw_mute_cb; -            u->source->set_hw_mute = source_set_hw_mute_cb; +            u->source->get_mute = source_get_mute_cb; +            u->source->set_mute = source_set_mute_cb;          } -    } -    pa_source_set_owner(u->source, m); -    pa_source_set_description(u->source, t = pa_sprintf_malloc("ALSA PCM on %s (%s)", dev, snd_pcm_info_get_name(pcm_info))); -    pa_xfree(t); -    u->pcm_fdl = pa_alsa_fdlist_new(); -    assert(u->pcm_fdl); -    if (pa_alsa_fdlist_init_pcm(u->pcm_fdl, u->pcm_handle, c->mainloop, fdl_callback, u) < 0) { -        pa_log("failed to initialise file descriptor monitoring"); -        goto fail; -    } - -    if (u->mixer_handle) {          u->mixer_fdl = pa_alsa_fdlist_new(); -        assert(u->mixer_fdl); -        if (pa_alsa_fdlist_init_mixer(u->mixer_fdl, u->mixer_handle, c->mainloop) < 0) { + +        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {              pa_log("failed to initialise file descriptor monitoring");              goto fail;          } +          snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);          snd_mixer_elem_set_callback_private(u->mixer_elem, u);      } else          u->mixer_fdl = NULL; -    u->frame_size = frame_size; -    u->fragment_size = period_size * frame_size; - -    pa_log_info("using %u fragments of size %lu bytes.", periods, (long unsigned) u->fragment_size); - -    u->memchunk.memblock = NULL; -    u->memchunk.index = u->memchunk.length = 0; - -    snd_pcm_start(u->pcm_handle); - -    ret = 0; - +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    }      /* Get initial mixer settings */ -    if (u->source->get_hw_volume) -        u->source->get_hw_volume(u->source); -    if (u->source->get_hw_mute) -        u->source->get_hw_mute(u->source); +    if (u->source->get_volume) +        u->source->get_volume(u->source); +    if (u->source->get_mute) +        u->source->get_mute(u->source); -finish: -    pa_xfree(name_buf); - -    if (ma) -         pa_modargs_free(ma); +    pa_source_put(u->source); -    if (pcm_info) -        snd_pcm_info_free(pcm_info); +    pa_modargs_free(ma); -    return ret; +    return 0;  fail: -    if (u) -        pa__done(c, m); +    if (ma) +        pa_modargs_free(ma); -    goto finish; +    pa__done(m); + +    return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; -    clear_up(u); +    if (u->source) +        pa_source_unlink(u->source); -    if (u->memchunk.memblock) -        pa_memblock_unref(u->memchunk.memblock); +    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->source) +        pa_source_unref(u->source); + +    if (u->alsa_rtpoll_item) +        pa_rtpoll_item_free(u->alsa_rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->mixer_fdl) +        pa_alsa_fdlist_free(u->mixer_fdl); + +    if (u->mixer_handle) +        snd_mixer_close(u->mixer_handle); + +    if (u->pcm_handle) { +        snd_pcm_drop(u->pcm_handle); +        snd_pcm_close(u->pcm_handle); +    } + +    pa_xfree(u->device_name);      pa_xfree(u); -} +    snd_config_update_free_global(); +} diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c index 19ac0c26..84125214 100644 --- a/src/modules/module-cli.c +++ b/src/modules/module-cli.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <pulsecore/module.h> @@ -35,6 +34,7 @@  #include <pulsecore/sioman.h>  #include <pulsecore/log.h>  #include <pulsecore/modargs.h> +#include <pulsecore/macro.h>  #include "module-cli-symdef.h" @@ -51,8 +51,8 @@ static const char* const valid_modargs[] = {  static void eof_and_unload_cb(pa_cli*c, void *userdata) {      pa_module *m = userdata; -    assert(c); -    assert(m); +    pa_assert(c); +    pa_assert(m);      pa_module_unload_request(m);  } @@ -60,21 +60,20 @@ static void eof_and_unload_cb(pa_cli*c, void *userdata) {  static void eof_and_exit_cb(pa_cli*c, void *userdata) {      pa_module *m = userdata; -    assert(c); -    assert(m); +    pa_assert(c); +    pa_assert(m);      m->core->mainloop->quit(m->core->mainloop, 0);  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_iochannel *io;      pa_modargs *ma;      int exit_on_eof = 0; -    assert(c); -    assert(m); +    pa_assert(m); -    if (c->running_as_daemon) { +    if (m->core->running_as_daemon) {          pa_log_info("Running as daemon, refusing to load this module.");          return 0;      } @@ -94,12 +93,10 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); -    assert(io); +    io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);      pa_iochannel_set_noclose(io, 1); -    m->userdata = pa_cli_new(c, io, m); -    assert(m->userdata); +    m->userdata = pa_cli_new(m->core, io, m);      pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m); @@ -115,11 +112,10 @@ fail:      return -1;  } -void pa__done(pa_core *c, pa_module*m) { -    assert(c); -    assert(m); +void pa__done(pa_module*m) { +    pa_assert(m); -    if (c->running_as_daemon == 0) { +    if (m->core->running_as_daemon == 0) {          pa_cli_free(m->userdata);          pa_stdio_release();      } diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 716c20b2..665bf9dd 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -25,12 +25,13 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h> +#include <errno.h>  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include <pulsecore/module.h>  #include <pulsecore/llist.h>  #include <pulsecore/sink.h> @@ -40,6 +41,12 @@  #include <pulsecore/core-util.h>  #include <pulsecore/modargs.h>  #include <pulsecore/namereg.h> +#include <pulsecore/mutex.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/core-error.h>  #include "module-combine-symdef.h" @@ -55,13 +62,12 @@ PA_MODULE_USAGE(          "format=<sample format> "          "channels=<number of channels> "          "rate=<sample rate> " -        "channel_map=<channel map> ") +        "channel_map=<channel map>")  #define DEFAULT_SINK_NAME "combined"  #define MEMBLOCKQ_MAXLENGTH (1024*170) -#define RENDER_SIZE (1024*10) -#define DEFAULT_ADJUST_TIME 20 +#define DEFAULT_ADJUST_TIME 10  static const char* const valid_modargs[] = {      "sink_name", @@ -78,170 +84,689 @@ static const char* const valid_modargs[] = {  struct output {      struct userdata *userdata; + +    pa_sink *sink;      pa_sink_input *sink_input; -    size_t counter; + +    pa_asyncmsgq *inq,    /* Message queue from the sink thread to this sink input */ +                 *outq;   /* Message queue from this sink input to the sink thread */ +    pa_rtpoll_item *inq_rtpoll_item, *outq_rtpoll_item; +      pa_memblockq *memblockq; +      pa_usec_t total_latency; +      PA_LLIST_FIELDS(struct output);  };  struct userdata { -    pa_module *module;      pa_core *core; +    pa_module *module;      pa_sink *sink; -    unsigned n_outputs; -    struct output *master; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +      pa_time_event *time_event;      uint32_t adjust_time; -    PA_LLIST_HEAD(struct output, outputs); +    pa_bool_t automatic; +    size_t block_size; + +    pa_hook_slot *sink_new_slot, *sink_unlink_slot, *sink_state_changed_slot; + +    pa_resample_method_t resample_method; + +    struct timeval adjust_timestamp; + +    struct output *master; +    pa_idxset* outputs; /* managed in main context */ + +    struct { +        PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */ +        pa_atomic_t running;  /* we cache that value here, so that every thread can query it cheaply */ +        struct timeval timestamp; +        pa_bool_t in_null_mode; +    } thread_info;  }; -static void output_free(struct output *o); -static void clear_up(struct userdata *u); +enum { +    SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX, +    SINK_MESSAGE_REMOVE_OUTPUT, +    SINK_MESSAGE_NEED +}; -static void update_usage(struct userdata *u) { -    pa_module_set_used(u->module, u->sink ? pa_sink_used_by(u->sink) : 0); -} +enum { +    SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX +}; + +static void output_free(struct output *o); +static int output_create_sink_input(struct output *o); +static void update_master(struct userdata *u, struct output *o); +static void pick_master(struct userdata *u, struct output *except);  static void adjust_rates(struct userdata *u) {      struct output *o;      pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency;      uint32_t base_rate; -    assert(u && u->sink); +    uint32_t idx; -    for (o = u->outputs; o; o = o->next) { -        uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0; +    pa_assert(u); +    pa_sink_assert_ref(u->sink); +    if (pa_idxset_size(u->outputs) <= 0) +        return; + +    if (!u->master) +        return; + +    if (!PA_SINK_OPENED(pa_sink_get_state(u->sink))) +        return; + +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { +        pa_usec_t sink_latency; + +        if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink))) +            continue; + +        sink_latency = pa_sink_get_latency(o->sink);          o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input);          if (sink_latency > max_sink_latency)              max_sink_latency = sink_latency; -        if (o->total_latency < min_total_latency) +        if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency)              min_total_latency = o->total_latency;      } -    assert(min_total_latency != (pa_usec_t) -1); +    if (min_total_latency == (pa_usec_t) -1) +        return;      target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency;      pa_log_info("[%s] target latency is %0.0f usec.", u->sink->name, (float) target_latency); +    pa_log_info("[%s] master %s latency %0.0f usec.", u->sink->name, u->master->sink->name, (float) u->master->total_latency);      base_rate = u->sink->sample_spec.rate; -    for (o = u->outputs; o; o = o->next) { +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {          uint32_t r = base_rate; +        if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink))) +            continue; +          if (o->total_latency < target_latency) -            r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000); +            r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*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/ 1000000); +            r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC); -        if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) +        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).", o->sink_input->name, base_rate, r); -        else { +            pa_sink_input_set_rate(o->sink_input, base_rate); +        } else {              pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency);              pa_sink_input_set_rate(o->sink_input, r);          }      }  } -static void request_memblock(struct userdata *u) { -    pa_memchunk chunk; -    struct output *o; -    assert(u && u->sink); +static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +    struct userdata *u = userdata; +    struct timeval n; + +    pa_assert(u); +    pa_assert(a); +    pa_assert(u->time_event == e); -    update_usage(u); +    adjust_rates(u); + +    pa_gettimeofday(&n); +    n.tv_sec += u->adjust_time; +    u->sink->core->mainloop->time_restart(e, &n); +} + +static void thread_func(void *userdata) { +    struct userdata *u = userdata; -    if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0) +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    if (u->core->high_priority) +        pa_make_realtime(); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    pa_rtclock_get(&u->thread_info.timestamp); +    u->thread_info.in_null_mode = FALSE; + +    for (;;) { +        int ret; + +        /* If no outputs are connected, render some data and drop it immediately. */ +        if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) { +            struct timeval now; + +            pa_rtclock_get(&now); + +            if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) { +                pa_sink_skip(u->sink, u->block_size); + +                if (!u->thread_info.in_null_mode) +                    u->thread_info.timestamp = now; + +                pa_timeval_add(&u->thread_info.timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec)); +            } + +            pa_rtpoll_set_timer_absolute(u->rtpoll, &u->thread_info.timestamp); +            u->thread_info.in_null_mode = TRUE; + +        } else { +            pa_rtpoll_set_timer_disabled(u->rtpoll); +            u->thread_info.in_null_mode = FALSE; +        } + +        /* Hmm, nothing to do. Let's sleep */ +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) { +            pa_log_info("pa_rtpoll_run() = %i", ret); +            goto fail; +        } + +        if (ret == 0) +            goto finish; +    } + +fail: +    /* If this was no regular exit from the loop we have to continue +     * processing messages until we received PA_MESSAGE_SHUTDOWN */ +    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: +    pa_log_debug("Thread shutting down"); +} + +/* Called from I/O thread context */ +static void render_memblock(struct userdata *u, struct output *o, size_t length) { +    pa_assert(u); +    pa_assert(o); + +    /* We are run by the sink thread, on behalf of an output (o). The +     * other output is waiting for us, hence it is safe to access its +     * mainblockq and asyncmsgq directly. */ + +    /* If we are not running, we cannot produce any data */ +    if (!pa_atomic_load(&u->thread_info.running))          return; -    for (o = u->outputs; o; o = o->next) -        pa_memblockq_push_align(o->memblockq, &chunk); +    /* Maybe there's some data in the requesting output's queue +     * now? */ +    while (pa_asyncmsgq_process_one(o->inq) > 0) +        ; + +    /* Ok, now let's prepare some data if we really have to */ +    while (!pa_memblockq_is_readable(o->memblockq)) { +        struct output *j; +        pa_memchunk chunk; + +        /* Render data! */ +        pa_sink_render(u->sink, length, &chunk); + +        /* OK, let's send this data to the other threads */ +        for (j = u->thread_info.active_outputs; j; j = j->next) -    pa_memblock_unref(chunk.memblock); +            /* Send to other outputs, which are not the requesting +             * one */ + +            if (j != o) +                pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL); + +        /* And place it directly into the requesting output's queue */ +        if (o) +            pa_memblockq_push_align(o->memblockq, &chunk); + +        pa_memblock_unref(chunk.memblock); +    }  } -static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { -    struct userdata *u = userdata; -    struct timeval n; -    assert(u && a && u->time_event == e); +/* Called from I/O thread context */ +static void request_memblock(struct output *o, size_t length) { +    pa_assert(o); +    pa_sink_input_assert_ref(o->sink_input); +    pa_sink_assert_ref(o->userdata->sink); -    adjust_rates(u); +    /* If another thread already prepared some data we received +     * the data over the asyncmsgq, hence let's first process +     * it. */ +    while (pa_asyncmsgq_process_one(o->inq) > 0) +        ; -    pa_gettimeofday(&n); -    n.tv_sec += u->adjust_time; -    u->sink->core->mainloop->time_restart(e, &n); +    /* Check whether we're now readable */ +    if (pa_memblockq_is_readable(o->memblockq)) +        return; + +    /* 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);  } -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { -    struct output *o = i->userdata; -    assert(i && o && o->sink_input && chunk); +/* Called from I/O thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    struct output *o; -    if (pa_memblockq_peek(o->memblockq, chunk) >= 0) -        return 0; +    pa_sink_input_assert_ref(i); +    pa_assert_se(o = i->userdata); -    /* Try harder */ -    request_memblock(o->userdata); +    /* If necessary, get some new data */ +    request_memblock(o, length);      return pa_memblockq_peek(o->memblockq, chunk);  } -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { -    struct output *o = i->userdata; -    assert(i && o && o->sink_input && chunk && length); +/* Called from I/O thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    struct output *o; + +    pa_sink_input_assert_ref(i); +    pa_assert(length > 0); +    pa_assert_se(o = i->userdata); + +    pa_memblockq_drop(o->memblockq, length); +} + +/* Called from I/O thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { +    struct output *o; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(o = i->userdata); + +    /* Set up the queue from the sink thread to us */ +    pa_assert(!o->inq_rtpoll_item); +    o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( +            i->sink->rtpoll, +            PA_RTPOLL_LATE,  /* This one is not that important, since we check for data in _peek() anyway. */ +            o->inq); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { +    struct output *o; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(o = i->userdata); -    pa_memblockq_drop(o->memblockq, chunk, length); -    o->counter += length; +    /* Shut down the queue from the sink thread to us */ +    pa_assert(o->inq_rtpoll_item); +    pa_rtpoll_item_free(o->inq_rtpoll_item); +    o->inq_rtpoll_item = NULL;  } +/* Called from main context */  static void sink_input_kill_cb(pa_sink_input *i) { -    struct output *o = i->userdata; -    assert(i && o && o->sink_input); +    struct output *o; + +    pa_sink_input_assert_ref(i); +    pa_assert(o = i->userdata); +      pa_module_unload_request(o->userdata->module); -    clear_up(o->userdata); +    output_free(o); +} + +/* Called from thread context */ +static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct output *o = PA_SINK_INPUT(obj)->userdata; + +    switch (code) { + +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { +             pa_usec_t *r = data; + +            *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +        } + +        case SINK_INPUT_MESSAGE_POST: + +            if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state)) +                pa_memblockq_push_align(o->memblockq, chunk); +            else +                pa_memblockq_flush(o->memblockq); + +            break; +    } + +    return pa_sink_input_process_msg(obj, code, data, offset, chunk);  } -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { -    struct output *o = i->userdata; -    assert(i && o && o->sink_input); +/* Called from main context */ +static void disable_output(struct output *o) { +    pa_assert(o); + +    if (!o->sink_input) +        return; + +    pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); +    pa_sink_input_unlink(o->sink_input); +    pa_sink_input_unref(o->sink_input); +    o->sink_input = NULL; -    return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec);  } -static pa_usec_t sink_get_latency_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    assert(s && u && u->sink && u->master); +/* Called from main context */ +static void enable_output(struct output *o) { +    pa_assert(o); + +    if (o->sink_input) +        return; + +    if (output_create_sink_input(o) >= 0) { + +        pa_memblockq_flush(o->memblockq); + +        pa_sink_input_put(o->sink_input); + +        if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink))) +            pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); +    } +} + +/* Called from main context */ +static void suspend(struct userdata *u) { +    struct output *o; +    uint32_t idx; + +    pa_assert(u); + +    /* Let's suspend by unlinking all streams */ +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) +        disable_output(o); + +    pick_master(u, NULL); + +    pa_log_info("Device suspended..."); +} + +/* Called from main context */ +static void unsuspend(struct userdata *u) { +    struct output *o; +    uint32_t idx; + +    pa_assert(u); + +    /* Let's resume */ +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { + +        pa_sink_suspend(o->sink, FALSE); + +        if (PA_SINK_OPENED(pa_sink_get_state(o->sink))) +            enable_output(o); +    } + +    pick_master(u, NULL); + +    pa_log_info("Resumed successfully..."); +} + +/* Called from main context */ +static int sink_set_state(pa_sink *sink, pa_sink_state_t state) { +    struct userdata *u; + +    pa_sink_assert_ref(sink); +    pa_assert_se(u = sink->userdata); + +    /* Please note that in contrast to the ALSA modules we call +     * suspend/unsuspend from main context here! */ + +    switch (state) { +        case PA_SINK_SUSPENDED: +            pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink))); + +            suspend(u); +            break; + +        case PA_SINK_IDLE: +        case PA_SINK_RUNNING: + +            if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED) +                unsuspend(u); + +            break; + +        case PA_SINK_UNLINKED: +        case PA_SINK_INIT: +            ; +    } + +    return 0; +} + +/* Called from thread context of the master */ +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: +            pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING); +            break; + +        case PA_SINK_MESSAGE_GET_LATENCY: + +            /* This code will only be called when running in NULL +             * mode, i.e. when no output is attached. See +             * sink_get_latency_cb() below */ + +            if (u->thread_info.in_null_mode) { +                struct timeval now; + +                if (pa_timeval_cmp(&u->thread_info.timestamp, pa_rtclock_get(&now)) > 0) { +                    *((pa_usec_t*) data) = pa_timeval_diff(&u->thread_info.timestamp, &now); +                    break; +                } +            } + +            *((pa_usec_t*) data) = 0; + +            break; + +        case SINK_MESSAGE_ADD_OUTPUT: { +            struct output *op = data; + +            PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op); + +            pa_assert(!op->outq_rtpoll_item); + +            /* Create pa_asyncmsgq to the sink thread */ + +            op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( +                    u->rtpoll, +                    PA_RTPOLL_EARLY-1,  /* This item is very important */ +                    op->outq); + +            return 0; +        } + +        case SINK_MESSAGE_REMOVE_OUTPUT: { +            struct output *op = data; -    return -        pa_sink_input_get_latency(u->master->sink_input) + -        pa_sink_get_latency(u->master->sink_input->sink); +            PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op); + +            /* Remove the q that leads from this output to the sink thread */ + +            pa_assert(op->outq_rtpoll_item); +            pa_rtpoll_item_free(op->outq_rtpoll_item); +            op->outq_rtpoll_item = NULL; + +            return 0; +        } + +        case SINK_MESSAGE_NEED: +            render_memblock(u, data, (size_t) offset); +            return 0; +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk);  } -static void sink_notify(pa_sink *s) { +/* Called from main context */ +static pa_usec_t sink_get_latency_cb(pa_sink *s) {      struct userdata *u; + +    pa_sink_assert_ref(s); +    pa_assert_se(u = s->userdata); + +    if (u->master) { +        /* If we have a master sink, we just return the latency of it +         * and add our own buffering on top */ + +        if (!u->master->sink_input) +            return 0; + +        return +            pa_sink_input_get_latency(u->master->sink_input) + +            pa_sink_get_latency(u->master->sink); + +    } else { +        pa_usec_t usec = 0; + +        /* We have no master, hence let's ask our own thread which +         * implements the NULL sink */ + +        if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) +            return 0; + +        return usec; +    } +} + +static void update_description(struct userdata *u) { +    int first = 1; +    char *t; +    struct output *o; +    uint32_t idx; + +    pa_assert(u); + +    if (pa_idxset_isempty(u->outputs)) { +        pa_sink_set_description(u->sink, "Simultaneous output"); +        return; +    } + +    t = pa_xstrdup("Simultaneous output to"); + +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { +        char *e; + +        if (first) { +            e = pa_sprintf_malloc("%s %s", t, o->sink->description); +            first = 0; +        } else +            e = pa_sprintf_malloc("%s, %s", t, o->sink->description); + +        pa_xfree(t); +        t = e; +    } + +    pa_sink_set_description(u->sink, t); +    pa_xfree(t); +} + +static void update_master(struct userdata *u, struct output *o) { +    pa_assert(u); + +    if (u->master == o) +        return; + +    if ((u->master = o)) +        pa_log_info("Master sink is now '%s'", o->sink_input->sink->name); +    else +        pa_log_info("No master selected, lacking suitable outputs."); +} + +static void pick_master(struct userdata *u, struct output *except) {      struct output *o; +    uint32_t idx; +    pa_assert(u); + +    if (u->master && +        u->master != except && +        u->master->sink_input && +        PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) { +        update_master(u, u->master); +        return; +    } -    assert(s); -    u = s->userdata; -    assert(u); +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) +        if (o != except && +            o->sink_input && +            PA_SINK_OPENED(pa_sink_get_state(o->sink))) { +            update_master(u, o); +            return; +        } -    for (o = u->outputs; o; o = o->next) -        pa_sink_notify(o->sink_input->sink); +    update_master(u, NULL);  } -static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) { -    struct output *o = NULL; -    char t[256]; +static int output_create_sink_input(struct output *o) {      pa_sink_input_new_data data; +    char *t; -    assert(u && sink && u->sink); +    pa_assert(o); -    o = pa_xmalloc(sizeof(struct output)); -    o->userdata = u; +    if (o->sink_input) +        return 0; + +    t = pa_sprintf_malloc("Simultaneous output on %s", o->sink->description); -    o->counter = 0; +    pa_sink_input_new_data_init(&data); +    data.sink = o->sink; +    data.driver = __FILE__; +    data.name = t; +    pa_sink_input_new_data_set_sample_spec(&data, &o->userdata->sink->sample_spec); +    pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map); +    data.module = o->userdata->module; +    data.resample_method = o->userdata->resample_method; + +    o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE); + +    pa_xfree(t); + +    if (!o->sink_input) +        return -1; + +    o->sink_input->parent.process_msg = sink_input_process_msg; +    o->sink_input->peek = sink_input_peek_cb; +    o->sink_input->drop = sink_input_drop_cb; +    o->sink_input->attach = sink_input_attach_cb; +    o->sink_input->detach = sink_input_detach_cb; +    o->sink_input->kill = sink_input_kill_cb; +    o->sink_input->userdata = o; + + +    return 0; +} + +static struct output *output_new(struct userdata *u, pa_sink *sink) { +    struct output *o; + +    pa_assert(u); +    pa_assert(sink); +    pa_assert(u->sink); + +    o = pa_xnew(struct output, 1); +    o->userdata = u; +    o->inq = pa_asyncmsgq_new(0); +    o->outq = pa_asyncmsgq_new(0); +    o->inq_rtpoll_item = NULL; +    o->outq_rtpoll_item = NULL; +    o->sink = sink; +    o->sink_input = NULL;      o->memblockq = pa_memblockq_new(              0,              MEMBLOCKQ_MAXLENGTH, @@ -251,90 +776,151 @@ static struct output *output_new(struct userdata *u, pa_sink *sink, int resample              0,              NULL); -    snprintf(t, sizeof(t), "Output stream #%u of sink %s", u->n_outputs+1, u->sink->name); +    pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); -    pa_sink_input_new_data_init(&data); -    data.sink = sink; -    data.driver = __FILE__; -    data.name = t; -    pa_sink_input_new_data_set_sample_spec(&data, &u->sink->sample_spec); -    pa_sink_input_new_data_set_channel_map(&data, &u->sink->channel_map); -    data.module = u->module; +    if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink))) +        pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); +    else { +        /* If the sink is not yet started, we need to do the activation ourselves */ +        PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o); -    if (!(o->sink_input = pa_sink_input_new(u->core, &data, PA_SINK_INPUT_VARIABLE_RATE))) -        goto fail; +        o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( +                u->rtpoll, +                PA_RTPOLL_EARLY-1,  /* This item is very important */ +                o->outq); +    } -    o->sink_input->get_latency = sink_input_get_latency_cb; -    o->sink_input->peek = sink_input_peek_cb; -    o->sink_input->drop = sink_input_drop_cb; -    o->sink_input->kill = sink_input_kill_cb; -    o->sink_input->userdata = o; +    if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) { +        pa_sink_suspend(sink, FALSE); + +        if (PA_SINK_OPENED(pa_sink_get_state(sink))) +            if (output_create_sink_input(o) < 0) +                goto fail; +    } + + +    update_description(u); -    PA_LLIST_PREPEND(struct output, u->outputs, o); -    u->n_outputs++;      return o;  fail:      if (o) { +        pa_idxset_remove_by_data(u->outputs, o, NULL); +          if (o->sink_input) { -            pa_sink_input_disconnect(o->sink_input); +            pa_sink_input_unlink(o->sink_input);              pa_sink_input_unref(o->sink_input);          }          if (o->memblockq)              pa_memblockq_free(o->memblockq); +        if (o->inq) +            pa_asyncmsgq_unref(o->inq); + +        if (o->outq) +            pa_asyncmsgq_unref(o->outq); +          pa_xfree(o);      }      return NULL;  } -static void output_free(struct output *o) { -    assert(o); -    PA_LLIST_REMOVE(struct output, o->userdata->outputs, o); -    o->userdata->n_outputs--; -    pa_memblockq_free(o->memblockq); -    pa_sink_input_disconnect(o->sink_input); -    pa_sink_input_unref(o->sink_input); -    pa_xfree(o); +static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { +    struct output *o; + +    pa_core_assert_ref(c); +    pa_sink_assert_ref(s); +    pa_assert(u); +    pa_assert(u->automatic); + +    if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink) +        return PA_HOOK_OK; + +    pa_log_info("Configuring new sink: %s", s->name); + +    if (!(o = output_new(u, s))) { +        pa_log("Failed to create sink input on sink '%s'.", s->name); +        return PA_HOOK_OK; +    } + +    if (o->sink_input) +        pa_sink_input_put(o->sink_input); + +    pick_master(u, NULL); + +    return PA_HOOK_OK;  } -static void clear_up(struct userdata *u) { +static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {      struct output *o; -    assert(u); +    uint32_t idx; -    if (u->time_event) { -        u->core->mainloop->time_free(u->time_event); -        u->time_event = NULL; -    } +    pa_assert(c); +    pa_sink_assert_ref(s); +    pa_assert(u); -    while ((o = u->outputs)) -        output_free(o); +    if (s == u->sink) +        return PA_HOOK_OK; -    u->master = NULL; +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) +        if (o->sink == s) +            break; -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); -        u->sink = NULL; +    if (!o) +        return PA_HOOK_OK; + +    pa_log_info("Unconfiguring sink: %s", s->name); + +    output_free(o); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { +    struct output *o; +    uint32_t idx; +    pa_sink_state_t state; + +    if (s == u->sink) +        return PA_HOOK_OK; + +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) +        if (o->sink == s) +            break; + +    if (!o) +        return PA_HOOK_OK; + +    state = pa_sink_get_state(s); + +    if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) { +        enable_output(o); +        pick_master(u, NULL); +    } + +    if (state == PA_SINK_SUSPENDED && o->sink_input) { +        disable_output(o); +        pick_master(u, o);      } + +    return PA_HOOK_OK;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u;      pa_modargs *ma = NULL;      const char *master_name, *slaves, *rm; -    pa_sink *master_sink; -    char *n = NULL; -    const char*split_state; -    struct timeval tv; -    int resample_method = -1; +    pa_sink *master_sink = NULL; +    int resample_method = PA_RESAMPLER_TRIVIAL;      pa_sample_spec ss;      pa_channel_map map; +    struct output *o; +    uint32_t idx; -    assert(c && m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("failed to parse module arguments"); @@ -349,116 +935,256 @@ int pa__init(pa_core *c, pa_module*m) {      }      u = pa_xnew(struct userdata, 1); +    u->core = m->core; +    u->module = m;      m->userdata = u;      u->sink = NULL; -    u->n_outputs = 0;      u->master = NULL; -    u->module = m; -    u->core = c;      u->time_event = NULL;      u->adjust_time = DEFAULT_ADJUST_TIME; -    PA_LLIST_HEAD_INIT(struct output, u->outputs); +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    u->thread = NULL; +    u->resample_method = resample_method; +    u->outputs = pa_idxset_new(NULL, NULL); +    memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp)); +    u->sink_new_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL; +    PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs); +    pa_atomic_store(&u->thread_info.running, FALSE); +    u->thread_info.in_null_mode = FALSE; +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);      if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { -        pa_log("failed to parse adjust_time value"); +        pa_log("Failed to parse adjust_time value");          goto fail;      } -    if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) { -        pa_log("no master or slave sinks specified"); +    master_name = pa_modargs_get_value(ma, "master", NULL); +    slaves = pa_modargs_get_value(ma, "slaves", NULL); +    if (!master_name != !slaves) { +        pa_log("No master or slave sinks specified");          goto fail;      } -    if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) { -        pa_log("invalid master sink '%s'", master_name); -        goto fail; +    if (master_name) { +        if (!(master_sink = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK, 1))) { +            pa_log("Invalid master sink '%s'", master_name); +            goto fail; +        } + +        ss = master_sink->sample_spec; +        u->automatic = FALSE; +    } else { +        master_sink = NULL; +        ss = m->core->default_sample_spec; +        u->automatic = TRUE;      } -    ss = master_sink->sample_spec;      if ((pa_modargs_get_sample_spec(ma, &ss) < 0)) { -        pa_log("invalid sample specification."); +        pa_log("Invalid sample specification.");          goto fail;      } -    if (ss.channels == master_sink->sample_spec.channels) +    if (master_sink && ss.channels == master_sink->sample_spec.channels)          map = master_sink->channel_map;      else          pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); -    if ((pa_modargs_get_channel_map(ma, &map) < 0)) { -        pa_log("invalid channel map."); +    if ((pa_modargs_get_channel_map(ma, NULL, &map) < 0)) { +        pa_log("Invalid channel map.");          goto fail;      }      if (ss.channels != map.channels) { -        pa_log("channel map and sample specification don't match."); +        pa_log("Channel map and sample specification don't match.");          goto fail;      } -    if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { -        pa_log("failed to create sink"); +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { +        pa_log("Failed to create sink");          goto fail;      } -    pa_sink_set_owner(u->sink, m); -    pa_sink_set_description(u->sink, "Combined Sink"); +    u->sink->parent.process_msg = sink_process_msg;      u->sink->get_latency = sink_get_latency_cb; -    u->sink->notify = sink_notify; +    u->sink->set_state = sink_set_state;      u->sink->userdata = u; -    if (!(u->master = output_new(u, master_sink, resample_method))) { -        pa_log("failed to create master sink input on sink '%s'.", u->sink->name); -        goto fail; -    } +    u->sink->flags = PA_SINK_LATENCY; +    pa_sink_set_module(u->sink, m); +    pa_sink_set_description(u->sink, "Simultaneous output"); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + +    u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ +    if (u->block_size <= 0) +        u->block_size = pa_frame_size(&ss); -    split_state = NULL; -    while ((n = pa_split(slaves, ",", &split_state))) { -        pa_sink *slave_sink; +    if (!u->automatic) { +        const char*split_state; +        char *n = NULL; +        pa_assert(slaves); -        if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { -            pa_log("invalid slave sink '%s'", n); +        /* The master and slaves have been specified manually */ + +        if (!(u->master = output_new(u, master_sink))) { +            pa_log("Failed to create master sink input on sink '%s'.", master_sink->name);              goto fail;          } -        pa_xfree(n); +        split_state = NULL; +        while ((n = pa_split(slaves, ",", &split_state))) { +            pa_sink *slave_sink; -        if (!output_new(u, slave_sink, resample_method)) { -            pa_log("failed to create slave sink input on sink '%s'.", slave_sink->name); -            goto fail; +            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, 1)) || slave_sink == u->sink) { +                pa_log("Invalid slave sink '%s'", n); +                pa_xfree(n); +                goto fail; +            } + +            pa_xfree(n); + +            if (!output_new(u, slave_sink)) { +                pa_log("Failed to create slave sink input on sink '%s'.", slave_sink->name); +                goto fail; +            }          } + +        if (pa_idxset_size(u->outputs) <= 1) +            pa_log_warn("No slave sinks specified."); + +        u->sink_new_slot = NULL; + +    } else { +        pa_sink *s; + +        /* We're in automatic mode, we elect one hw sink to the master +         * and attach all other hw sinks as slaves to it */ + +        for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) { + +            if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink) +                continue; + +            if (!output_new(u, s)) { +                pa_log("Failed to create sink input on sink '%s'.", s->name); +                goto fail; +            } +        } + +        u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) sink_new_hook_cb, u);      } -    if (u->n_outputs <= 1) -        pa_log_warn("WARNING: no slave sinks specified."); +    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_unlink_hook_cb, u); +    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) sink_state_changed_hook_cb, u); + +    pick_master(u, NULL); + +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    } + +    /* Activate the sink and the sink inputs */ +    pa_sink_put(u->sink); + +    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) +        if (o->sink_input) +            pa_sink_input_put(o->sink_input);      if (u->adjust_time > 0) { +        struct timeval tv;          pa_gettimeofday(&tv);          tv.tv_sec += u->adjust_time; -        u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); +        u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u);      }      pa_modargs_free(ma); +      return 0;  fail: -    pa_xfree(n);      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m); +      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +static void output_free(struct output *o) { +    pa_assert(o); + +    pick_master(o->userdata, o); + +    disable_output(o); + +    pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL)); + +    update_description(o->userdata); + +    if (o->inq_rtpoll_item) +        pa_rtpoll_item_free(o->inq_rtpoll_item); + +    if (o->outq_rtpoll_item) +        pa_rtpoll_item_free(o->outq_rtpoll_item); + +    if (o->inq) +        pa_asyncmsgq_unref(o->inq); + +    if (o->outq) +        pa_asyncmsgq_unref(o->outq); + +    if (o->memblockq) +        pa_memblockq_free(o->memblockq); + +    pa_xfree(o); +} + +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); +    struct output *o; + +    pa_assert(m);      if (!(u = m->userdata))          return; -    clear_up(u); -    pa_xfree(u); -} +    if (u->sink_new_slot) +        pa_hook_slot_free(u->sink_new_slot); + +    if (u->sink_unlink_slot) +        pa_hook_slot_free(u->sink_unlink_slot); + +    if (u->sink_state_changed_slot) +        pa_hook_slot_free(u->sink_state_changed_slot); + +    if (u->outputs) { +        while ((o = pa_idxset_first(u->outputs, NULL))) +            output_free(o); + +        pa_idxset_free(u->outputs, NULL, NULL); +    } +    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) +        pa_rtpoll_free(u->rtpoll); + +    if (u->time_event) +        u->core->mainloop->time_free(u->time_event); + +    pa_xfree(u); +} diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c new file mode 100644 index 00000000..a816eae8 --- /dev/null +++ b/src/modules/module-default-device-restore.c @@ -0,0 +1,103 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <pulsecore/core-util.h> +#include <pulsecore/module.h> +#include <pulsecore/log.h> +#include <pulsecore/namereg.h> + +#include "module-default-device-restore-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Automatically restore the default sink and source") +PA_MODULE_VERSION(PACKAGE_VERSION) + +#define DEFAULT_SINK_FILE "default-sink" +#define DEFAULT_SOURCE_FILE "default-source" + +int pa__init(pa_module *m) { +    FILE *f; + +    /* We never overwrite manually configured settings */ + +    if (m->core->default_sink_name) +        pa_log_info("Manually configured default sink, not overwriting."); +    else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) { +        char ln[256] = ""; + +        fgets(ln, sizeof(ln)-1, f); +        pa_strip_nl(ln); +        fclose(f); + +        if (!ln[0]) +            pa_log_debug("No previous default sink setting, ignoring."); +        else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) { +            pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK); +            pa_log_debug("Restored default sink '%s'.", ln); +        } else +            pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln); +    } + +    if (m->core->default_source_name) +        pa_log_info("Manually configured default source, not overwriting."); +    else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) { +        char ln[256] = ""; + +        fgets(ln, sizeof(ln)-1, f); +        pa_strip_nl(ln); +        fclose(f); + +        if (!ln[0]) +            pa_log_debug("No previous default source setting, ignoring."); +        else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) { +            pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE); +            pa_log_debug("Restored default source '%s'.", ln); +        } else +            pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln); +    } + +    return 0; +} + +void pa__done(pa_module*m) { +    FILE *f; + +    if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) { +        const char *n = pa_namereg_get_default_sink_name(m->core); +        fprintf(f, "%s\n", n ? n : ""); +        fclose(f); +    } + +    if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) { +        const char *n = pa_namereg_get_default_source_name(m->core); +        fprintf(f, "%s\n", n ? n : ""); +        fclose(f); +    } +} + + + diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4 index c961412d..5bff748e 100644 --- a/src/modules/module-defs.h.m4 +++ b/src/modules/module-defs.h.m4 @@ -18,8 +18,8 @@ gen_symbol(pa__get_description)  gen_symbol(pa__get_usage)  gen_symbol(pa__get_version) -int pa__init(struct pa_core *c, struct pa_module*m); -void pa__done(struct pa_core *c, struct pa_module*m); +int pa__init(pa_module*m); +void pa__done(pa_module*m);  const char* pa__get_author(void);  const char* pa__get_description(void); diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index 41b68ac3..7dc25243 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -28,7 +28,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <string.h>  #include <stdlib.h> @@ -44,6 +43,7 @@  #include <pulsecore/modargs.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "module-detect-symdef.h" @@ -52,6 +52,11 @@ PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers  PA_MODULE_VERSION(PACKAGE_VERSION)  PA_MODULE_USAGE("just-one=<boolean>") +static const char* const valid_modargs[] = { +    "just-one", +    NULL +}; +  #ifdef HAVE_ALSA  static int detect_alsa(pa_core *c, int just_one) { @@ -96,7 +101,7 @@ static int detect_alsa(pa_core *c, int just_one) {          if (subdevice != 0)              continue; -        snprintf(args, sizeof(args), "device=hw:%u", device); +        pa_snprintf(args, sizeof(args), "device=hw:%u", device);          if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))              continue; @@ -139,7 +144,7 @@ static int detect_oss(pa_core *c, int just_one) {          line[strcspn(line, "\r\n")] = 0;          if (!b) { -	     b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0; +             b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0;              continue;          } @@ -148,20 +153,20 @@ static int detect_oss(pa_core *c, int just_one) {          if (sscanf(line, "%u: ", &device) == 1) {              if (device == 0) -                snprintf(args, sizeof(args), "device=/dev/dsp"); +                pa_snprintf(args, sizeof(args), "device=/dev/dsp");              else -                snprintf(args, sizeof(args), "device=/dev/dsp%u", device); +                pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);              if (!pa_module_load(c, "module-oss", args))                  continue; -	} else if (sscanf(line, "pcm%u: ", &device) == 1) { +        } else if (sscanf(line, "pcm%u: ", &device) == 1) {              /* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */ -            snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device); +            pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);              if (!pa_module_load(c, "module-oss", args))                  continue; -	} +        }          n++; @@ -193,7 +198,7 @@ static int detect_solaris(pa_core *c, int just_one) {      if (!S_ISCHR(s.st_mode))          return 0; -    snprintf(args, sizeof(args), "device=%s", dev); +    pa_snprintf(args, sizeof(args), "device=%s", dev);      if (!pa_module_load(c, "module-solaris", args))          return 0; @@ -215,17 +220,11 @@ static int detect_waveout(pa_core *c, int just_one) {  }  #endif -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      int just_one = 0, n = 0;      pa_modargs *ma; -    static const char* const valid_modargs[] = { -        "just-one", -        NULL -    }; - -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments"); @@ -238,16 +237,16 @@ int pa__init(pa_core *c, pa_module*m) {      }  #if HAVE_ALSA -    if ((n = detect_alsa(c, just_one)) <= 0) +    if ((n = detect_alsa(m->core, just_one)) <= 0)  #endif  #if HAVE_OSS -    if ((n = detect_oss(c, just_one)) <= 0) +    if ((n = detect_oss(m->core, just_one)) <= 0)  #endif  #if HAVE_SOLARIS -    if ((n = detect_solaris(c, just_one)) <= 0) +    if ((n = detect_solaris(m->core, just_one)) <= 0)  #endif  #if OS_IS_WIN32 -    if ((n = detect_waveout(c, just_one)) <= 0) +    if ((n = detect_waveout(m->core, just_one)) <= 0)  #endif      {          pa_log_warn("failed to detect any sound hardware."); @@ -269,9 +268,3 @@ fail:      return -1;  } - - -void pa__done(PA_GCC_UNUSED pa_core *c, PA_GCC_UNUSED pa_module*m) { -    /* NOP */ -} - diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c index 1aecade5..f0f73fcf 100644 --- a/src/modules/module-esound-compat-spawnfd.c +++ b/src/modules/module-esound-compat-spawnfd.c @@ -26,7 +26,6 @@  #endif  #include <unistd.h> -#include <assert.h>  #include <string.h>  #include <errno.h> @@ -48,23 +47,25 @@ static const char* const valid_modargs[] = {      NULL,  }; -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      int ret = -1, fd = -1;      char x = 1; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||          pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||          fd < 0) { +          pa_log("Failed to parse module arguments");          goto finish;      }      if (pa_loop_write(fd, &x, sizeof(x), NULL) != sizeof(x)) -        pa_log("WARNING: write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno)); +        pa_log_warn("write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno)); -    close(fd); +    pa_assert_se(pa_close(fd) == 0);      pa_module_unload_request(m); @@ -76,9 +77,3 @@ finish:      return ret;  } - -void pa__done(pa_core *c, pa_module*m) { -    assert(c && m); -} - - diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c index a9fd166d..6562fe28 100644 --- a/src/modules/module-esound-compat-spawnpid.c +++ b/src/modules/module-esound-compat-spawnpid.c @@ -25,7 +25,6 @@  #endif  #include <unistd.h> -#include <assert.h>  #include <string.h>  #include <errno.h>  #include <signal.h> @@ -48,11 +47,12 @@ static const char* const valid_modargs[] = {      NULL,  }; -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      int ret = -1;      uint32_t pid = 0; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||          pa_modargs_get_value_u32(ma, "pid", &pid) < 0 || @@ -62,7 +62,7 @@ int pa__init(pa_core *c, pa_module*m) {      }      if (kill(pid, SIGUSR1) < 0) -        pa_log("WARNING: kill(%u) failed: %s", pid, pa_cstrerror(errno)); +        pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));      pa_module_unload_request(m); @@ -74,9 +74,3 @@ finish:      return ret;  } - -void pa__done(pa_core *c, pa_module*m) { -    assert(c && m); -} - - diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 26638d9d..8b46637e 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -28,14 +28,23 @@  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.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> @@ -47,27 +56,37 @@  #include <pulsecore/socket-client.h>  #include <pulsecore/esound.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-esound-sink-symdef.h"  PA_MODULE_AUTHOR("Lennart Poettering")  PA_MODULE_DESCRIPTION("ESOUND Sink")  PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE("sink_name=<name for the sink> server=<address> cookie=<filename>  format=<sample format> channels=<number of channels> rate=<sample rate>") +PA_MODULE_USAGE( +        "sink_name=<name for the sink> " +        "server=<address> cookie=<filename>  " +        "format=<sample format> " +        "channels=<number of channels> " +        "rate=<sample rate>") -#define DEFAULT_SINK_NAME "esound_output" +#define DEFAULT_SINK_NAME "esound_out"  struct userdata {      pa_core *core; - +    pa_module *module;      pa_sink *sink; -    pa_iochannel *io; -    pa_socket_client *client; -    pa_defer_event *defer_event; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +    pa_rtpoll_item *rtpoll_item; +    pa_thread *thread;      pa_memchunk memchunk; -    pa_module *module;      void *write_data;      size_t write_length, write_index; @@ -75,12 +94,28 @@ struct userdata {      void *read_data;      size_t read_length, read_index; -    enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state; +    enum { +        STATE_AUTH, +        STATE_LATENCY, +        STATE_PREPARE, +        STATE_RUNNING, +        STATE_DEAD +    } state;      pa_usec_t latency;      esd_format_t format;      int32_t rate; + +    pa_smoother *smoother; +    int fd; + +    int64_t offset; + +    pa_iochannel *io; +    pa_socket_client *client; + +    size_t block_size;  };  static const char* const valid_modargs[] = { @@ -93,42 +128,211 @@ static const char* const valid_modargs[] = {      NULL  }; -static void cancel(struct userdata *u) { -    assert(u); +enum { +    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX +}; -    u->state = STATE_DEAD; +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; -    if (u->io) { -        pa_iochannel_free(u->io); -        u->io = NULL; -    } +    switch (code) { -    if (u->defer_event) { -        u->core->mainloop->defer_free(u->defer_event); -        u->defer_event = NULL; -    } +        case PA_SINK_MESSAGE_SET_STATE: -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); -        u->sink = NULL; +            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + +                case PA_SINK_SUSPENDED: +                    pa_assert(PA_SINK_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 + u->memchunk.length, &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 = pollfd->revents = 0; + +            return 0; +        }      } -    if (u->module) { -        pa_module_unload_request(u->module); -        u->module = NULL; +    return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    int write_type = 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()); + +    for (;;) { +        int ret; + +        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_OPENED(u->sink->thread_info.state) && pollfd->revents) { +                pa_usec_t usec; +                int64_t n; + +                for (;;) { +                    ssize_t l; +                    void *p; + +                    if (u->memchunk.length <= 0) +                        pa_sink_render(u->sink, u->block_size, &u->memchunk); + +                    pa_assert(u->memchunk.length > 0); + +                    p = pa_memblock_acquire(u->memchunk.memblock); +                    l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); +                    pa_memblock_release(u->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->memchunk.index += l; +                        u->memchunk.length -= l; + +                        if (u->memchunk.length <= 0) { +                            pa_memblock_unref(u->memchunk.memblock); +                            pa_memchunk_reset(&u->memchunk); +                        } + +                        pollfd->revents = 0; + +                        if (u->memchunk.length > 0) + +                            /* 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; + +#ifdef SIOCOUTQ +                { +                    int l; +                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) +                        n -= l; +                } +#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 = PA_SINK_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) { +                pa_log("FIFO shutdown."); +                goto fail; +            } +        }      } + +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: +    pa_log_debug("Thread shutting down");  }  static int do_write(struct userdata *u) {      ssize_t r; -    assert(u); +    pa_assert(u);      if (!pa_iochannel_is_writable(u->io))          return 0;      if (u->write_data) { -        assert(u->write_index < u->write_length); +        pa_assert(u->write_index < u->write_length);          if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {              pa_log("write() failed: %s", pa_cstrerror(errno)); @@ -136,45 +340,44 @@ static int do_write(struct userdata *u) {          }          u->write_index += r; -        assert(u->write_index <= u->write_length); +        pa_assert(u->write_index <= u->write_length);          if (u->write_index == u->write_length) { -            free(u->write_data); +            pa_xfree(u->write_data);              u->write_data = NULL;              u->write_index = u->write_length = 0;          } -    } else if (u->state == STATE_RUNNING) { -        pa_module_set_used(u->module, pa_sink_used_by(u->sink)); +    } -        if (!u->memchunk.length) -            if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0) -                return 0; +    if (!u->write_data && u->state == STATE_PREPARE) { +        /* OK, we're done with sending all control data we need to, so +         * let's hand the socket over to the IO thread now */ -        assert(u->memchunk.memblock && u->memchunk.length); +        pa_assert(u->fd < 0); +        u->fd = pa_iochannel_get_send_fd(u->io); -        if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { -            pa_log("write() failed: %s", pa_cstrerror(errno)); -            return -1; -        } +        pa_iochannel_set_noclose(u->io, TRUE); +        pa_iochannel_free(u->io); +        u->io = NULL; -        u->memchunk.index += r; -        u->memchunk.length -= r; +        pa_make_tcp_socket_low_delay(u->fd); -        if (u->memchunk.length <= 0) { -            pa_memblock_unref(u->memchunk.memblock); -            u->memchunk.memblock = NULL; -        } +        pa_log_info("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); +        u->state = STATE_RUNNING;      }      return 0;  }  static int handle_response(struct userdata *u) { -    assert(u); +    pa_assert(u);      switch (u->state) { +          case STATE_AUTH: -            assert(u->read_length == sizeof(int32_t)); +            pa_assert(u->read_length == sizeof(int32_t));              /* Process auth data */              if (!*(int32_t*) u->read_data) { @@ -183,14 +386,14 @@ static int handle_response(struct userdata *u) {              }              /* Request latency data */ -            assert(!u->write_data); +            pa_assert(!u->write_data);              *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;              u->write_index = 0;              u->state = STATE_LATENCY;              /* Space for next response */ -            assert(u->read_length >= sizeof(int32_t)); +            pa_assert(u->read_length >= sizeof(int32_t));              u->read_index = 0;              u->read_length = sizeof(int32_t); @@ -198,17 +401,17 @@ static int handle_response(struct userdata *u) {          case STATE_LATENCY: {              int32_t *p; -            assert(u->read_length == sizeof(int32_t)); +            pa_assert(u->read_length == sizeof(int32_t));              /* Process latency info */              u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);              if (u->latency > 10000000) { -                pa_log("WARNING! Invalid latency information received from server"); +                pa_log_warn("Invalid latency information received from server");                  u->latency = 0;              }              /* Create stream */ -            assert(!u->write_data); +            pa_assert(!u->write_data);              p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);              *(p++) = ESD_PROTO_STREAM_PLAY;              *(p++) = u->format; @@ -216,7 +419,7 @@ static int handle_response(struct userdata *u) {              pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);              u->write_index = 0; -            u->state = STATE_RUNNING; +            u->state = STATE_PREPARE;              /* Don't read any further */              pa_xfree(u->read_data); @@ -227,14 +430,14 @@ static int handle_response(struct userdata *u) {          }          default: -            abort(); +            pa_assert_not_reached();      }      return 0;  }  static int do_read(struct userdata *u) { -    assert(u); +    pa_assert(u);      if (!pa_iochannel_is_readable(u->io))          return 0; @@ -245,16 +448,15 @@ static int do_read(struct userdata *u) {          if (!u->read_data)              return 0; -        assert(u->read_index < u->read_length); +        pa_assert(u->read_index < u->read_length);          if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {              pa_log("read() failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF"); -            cancel(u);              return -1;          }          u->read_index += r; -        assert(u->read_index <= u->read_length); +        pa_assert(u->read_index <= u->read_length);          if (u->read_index == u->read_length)              return handle_response(u); @@ -263,42 +465,19 @@ static int do_read(struct userdata *u) {      return 0;  } -static void do_work(struct userdata *u) { -    assert(u); - -    u->core->mainloop->defer_enable(u->defer_event, 0); - -    if (do_read(u) < 0 || do_write(u) < 0) -        cancel(u); -} - -static void notify_cb(pa_sink*s) { -    struct userdata *u = s->userdata; -    assert(s && u); - -    if (pa_iochannel_is_writable(u->io)) -        u->core->mainloop->defer_enable(u->defer_event, 1); -} - -static pa_usec_t get_latency_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    assert(s && u); +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { +    struct userdata *u = userdata; +    pa_assert(u); -    return -        u->latency + -        (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0); -} +    if (do_read(u) < 0 || do_write(u) < 0) { -static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_work(u); -} +        if (u->io) { +            pa_iochannel_free(u->io); +            u->io = NULL; +        } -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_work(u); +       pa_module_unload_request(u->module); +    }  }  static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { @@ -308,30 +487,34 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo      u->client = NULL;      if (!io) { -        pa_log("connection failed: %s", pa_cstrerror(errno)); -        cancel(u); +        pa_log("Connection failed: %s", pa_cstrerror(errno)); +        pa_module_unload_request(u->module);          return;      } +    pa_assert(!u->io);      u->io = io;      pa_iochannel_set_callback(u->io, io_callback, u); + +    pa_log_info("Connection established, authenticating ...");  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u = NULL;      const char *p;      pa_sample_spec ss;      pa_modargs *ma = NULL;      char *t; +    const char *espeaker; -    assert(c && m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("failed to parse module arguments");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec(ma, &ss) < 0) {          pa_log("invalid sample format specification");          goto fail; @@ -343,37 +526,62 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    u = pa_xmalloc0(sizeof(struct userdata)); -    u->core = c; +    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); +    pa_memchunk_reset(&u->memchunk); +    u->offset = 0; + +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); +    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->sink = NULL; -    u->client = NULL; -    u->io = NULL; +    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 (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {          pa_log("failed to create sink.");          goto fail;      } -    if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) { -        pa_log("failed to connect to server."); +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->userdata = u; +    u->sink->flags = PA_SINK_LATENCY; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); + +    if (!(espeaker = getenv("ESPEAKER"))) +        espeaker = ESD_UNIX_SOCKET_NAME; + +    if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", espeaker), ESD_DEFAULT_PORT))) { +        pa_log("Failed to connect to server.");          goto fail;      } + +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p)); +    pa_xfree(t); +      pa_socket_client_set_callback(u->client, on_connection, u);      /* Prepare the initial request */      u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));      if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) { -        pa_log("failed to load cookie"); +        pa_log("Failed to load cookie");          goto fail;      }      *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY; @@ -381,19 +589,12 @@ int pa__init(pa_core *c, pa_module*m) {      /* Reserve space for the response */      u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t)); -    u->sink->notify = notify_cb; -    u->sink->get_latency = get_latency_cb; -    u->sink->userdata = u; -    pa_sink_set_owner(u->sink, m); -    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p)); -    pa_xfree(t); - -    u->memchunk.memblock = NULL; -    u->memchunk.length = 0; - -    u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); -    c->mainloop->defer_enable(u->defer_event, 0); +    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); @@ -403,20 +604,39 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); +    pa_assert(m);      if (!(u = m->userdata))          return; -    u->module = NULL; -    cancel(u); +    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->io) +        pa_iochannel_free(u->io); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll);      if (u->memchunk.memblock)          pa_memblock_unref(u->memchunk.memblock); @@ -427,8 +647,11 @@ void pa__done(pa_core *c, pa_module*m) {      pa_xfree(u->read_data);      pa_xfree(u->write_data); -    pa_xfree(u); -} - +    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-hal-detect.c b/src/modules/module-hal-detect.c index 1f48a452..a8ca7df3 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -1,25 +1,25 @@  /* $Id$ */  /*** -  This file is part of PulseAudio. +    This file is part of PulseAudio. -  Copyright 2006 Lennart Poettering -  Copyright 2006 Shams E. King +    Copyright 2006 Lennart Poettering +    Copyright 2006 Shams E. King -  PulseAudio is free software; you can redistribute it and/or modify -  it under the terms of the GNU Lesser General Public License as published -  by the Free Software Foundation; either version 2 of the License, -  or (at your option) any later version. +    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. +    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. +    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 @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <string.h>  #include <stdlib.h> @@ -45,6 +44,9 @@  #include <pulsecore/hashmap.h>  #include <pulsecore/idxset.h>  #include <pulsecore/core-util.h> +#include <pulsecore/namereg.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/modargs.h>  #include <hal/libhal.h> @@ -54,40 +56,27 @@  PA_MODULE_AUTHOR("Shahms King")  PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")  PA_MODULE_VERSION(PACKAGE_VERSION) - -typedef enum { -#ifdef HAVE_ALSA -    CAP_ALSA, -#endif -#ifdef HAVE_OSS -    CAP_OSS, -#endif -    CAP_MAX -} capability_t; - -static const char* const capabilities[CAP_MAX] = { -#ifdef HAVE_ALSA -    [CAP_ALSA] = "alsa", -#endif -#ifdef HAVE_OSS -    [CAP_OSS] = "oss", +#if defined(HAVE_ALSA) && defined(HAVE_OSS) +PA_MODULE_USAGE("api=<alsa or oss>") +#elif defined(HAVE_ALSA) +PA_MODULE_USAGE("api=<alsa>") +#elif defined(HAVE_OSS) +PA_MODULE_USAGE("api=<oss>")  #endif -};  struct device {      uint32_t index;      char *udi; +    char *sink_name, *source_name; +    int acl_race_fix;  };  struct userdata {      pa_core *core; -    LibHalContext *ctx; -    capability_t capability; -    pa_dbus_connection *conn; +    LibHalContext *context; +    pa_dbus_connection *connection;      pa_hashmap *devices; -#if defined(HAVE_ALSA) && defined(HAVE_OSS) -    int use_oss; -#endif +    const char *capability;  };  struct timerdata { @@ -95,23 +84,30 @@ struct timerdata {      char *udi;  }; -static const char* get_capability_name(capability_t cap) { -    if (cap >= CAP_MAX) -        return NULL; -    return capabilities[cap]; -} +#define CAPABILITY_ALSA "alsa" +#define CAPABILITY_OSS "oss" + +static const char* const valid_modargs[] = { +    "api", +    NULL +};  static void hal_device_free(struct device* d) { +    pa_assert(d); +      pa_xfree(d->udi); +    pa_xfree(d->sink_name); +    pa_xfree(d->source_name);      pa_xfree(d);  }  static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) { -    hal_device_free((struct device*) d); +    hal_device_free(d);  }  static const char *strip_udi(const char *udi) {      const char *slash; +      if ((slash = strrchr(udi, '/')))          return slash+1; @@ -119,6 +115,7 @@ static const char *strip_udi(const char *udi) {  }  #ifdef HAVE_ALSA +  typedef enum {      ALSA_TYPE_SINK,      ALSA_TYPE_SOURCE, @@ -126,234 +123,297 @@ typedef enum {      ALSA_TYPE_MAX  } alsa_type_t; -static alsa_type_t hal_device_get_alsa_type(LibHalContext *ctx, const char *udi, -                                            DBusError *error) -{ +static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {      char *type;      alsa_type_t t; -    type = libhal_device_get_property_string(ctx, udi, "alsa.type", error); -    if (!type || dbus_error_is_set(error)) -        return FALSE; +    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error))) +        return ALSA_TYPE_OTHER; -    if (!strcmp(type, "playback")) { +    if (!strcmp(type, "playback"))          t = ALSA_TYPE_SINK; -    } else if (!strcmp(type, "capture")) { +    else if (!strcmp(type, "capture"))          t = ALSA_TYPE_SOURCE; -    } else { +    else          t = ALSA_TYPE_OTHER; -    } +      libhal_free_string(type);      return t;  } -static int hal_device_get_alsa_card(LibHalContext *ctx, const char *udi, -                                    DBusError *error) -{ -    return libhal_device_get_property_int(ctx, udi, "alsa.card", error); -} +static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) { +    char *class; +    int r; + +    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error))) +        return 0; + +    r = strcmp(class, "modem") == 0; +    pa_xfree(class); -static int hal_device_get_alsa_device(LibHalContext *ctx, const char *udi, -                                      DBusError *error) -{ -    return libhal_device_get_property_int(ctx, udi, "alsa.device", error); +    return r;  } -static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, -                                       DBusError  *error) -{ -    char args[128]; +static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) { +    char *args;      alsa_type_t type;      int device, card;      const char *module_name; +    DBusError error; +    pa_module *m; -    type = hal_device_get_alsa_type(u->ctx, udi, error); -    if (dbus_error_is_set(error) || type == ALSA_TYPE_OTHER) -        return NULL; +    dbus_error_init(&error); -    device = hal_device_get_alsa_device(u->ctx, udi, error); -    if (dbus_error_is_set(error) || device != 0) -        return NULL; +    pa_assert(u); +    pa_assert(sink_name); +    pa_assert(source_name); -    card = hal_device_get_alsa_card(u->ctx, udi, error); -    if (dbus_error_is_set(error)) -        return NULL; +    *sink_name = *source_name = NULL; + +    type = hal_alsa_device_get_type(u->context, udi, &error); +    if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER) +        goto fail; + +    device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error); +    if (dbus_error_is_set(&error) || device != 0) +        goto fail; + +    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error); +    if (dbus_error_is_set(&error)) +        goto fail; + +    if (hal_alsa_device_is_modem(u->context, udi, &error)) +        goto fail;      if (type == ALSA_TYPE_SINK) { +        *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi)); +          module_name = "module-alsa-sink"; -        snprintf(args, sizeof(args), "device=hw:%u sink_name=alsa_output.%s", card, strip_udi(udi)); +        args = pa_sprintf_malloc("device=hw:%u sink_name=%s", card, *sink_name);      } else { +        *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi)); +          module_name = "module-alsa-source"; -        snprintf(args, sizeof(args), "device=hw:%u source_name=alsa_input.%s", card, strip_udi(udi)); +        args = pa_sprintf_malloc("device=hw:%u source_name=%s", card, *source_name);      }      pa_log_debug("Loading %s with arguments '%s'", module_name, args); -    return pa_module_load(u->core, module_name, args); +    m = pa_module_load(u->core, module_name, args); + +    pa_xfree(args); + +    if (!m) { +        pa_xfree(*sink_name); +        pa_xfree(*source_name); +        *sink_name = *source_name = NULL; +    } + +    return m; + +fail: +    if (dbus_error_is_set(&error)) { +        pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message); +        dbus_error_free(&error); +    } + +    return NULL;  }  #endif  #ifdef HAVE_OSS -static dbus_bool_t hal_device_is_oss_pcm(LibHalContext *ctx, const char *udi, -                                         DBusError *error) -{ -    dbus_bool_t rv = FALSE; -    char* type, *device_file = NULL; + +static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) { +    char *class = NULL, *dev = NULL, *e;      int device; +    int r = 0; -    type = libhal_device_get_property_string(ctx, udi, "oss.type", error); -    if (!type || dbus_error_is_set(error)) -        return FALSE; +    class = libhal_device_get_property_string(context, udi, "oss.type", error); +    if (dbus_error_is_set(error) || !class) +        goto finish; -    if (!strcmp(type, "pcm")) { -        char *e; +    if (strcmp(class, "pcm")) +        goto finish; -        device = libhal_device_get_property_int(ctx, udi, "oss.device", error); -        if (dbus_error_is_set(error) || device != 0) -            goto exit; +    dev = libhal_device_get_property_string(context, udi, "oss.device_file", error); +    if (dbus_error_is_set(error) || !dev) +        goto finish; -        device_file = libhal_device_get_property_string(ctx, udi, "oss.device_file", -                                                   error); -        if (!device_file || dbus_error_is_set(error)) -            goto exit; +    if ((e = strrchr(dev, '/'))) +        if (pa_startswith(e + 1, "audio")) +            goto finish; -        /* hack to ignore /dev/audio style devices */ -        if ((e = strrchr(device_file, '/'))) -            rv = !pa_startswith(e + 1, "audio"); -    } +    device = libhal_device_get_property_int(context, udi, "oss.device", error); +    if (dbus_error_is_set(error) || device != 0) +        goto finish; -exit: -    libhal_free_string(type); -    libhal_free_string(device_file); -    return rv; +    r = 1; + +finish: + +    libhal_free_string(class); +    libhal_free_string(dev); + +    return r;  } -static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, -                                      DBusError  *error) -{ -    char args[256]; +static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) { +    char* args;      char* device; +    DBusError error; +    pa_module *m; -    if (!hal_device_is_oss_pcm(u->ctx, udi, error) || dbus_error_is_set(error)) -        return NULL; +    dbus_error_init(&error); -    device = libhal_device_get_property_string(u->ctx, udi, "oss.device_file", -                                               error); -    if (!device || dbus_error_is_set(error)) -        return NULL; +    pa_assert(u); +    pa_assert(sink_name); +    pa_assert(source_name); + +    *sink_name = *source_name = NULL; -    snprintf(args, sizeof(args), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device, strip_udi(udi), strip_udi(udi)); +    if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error)) +        goto fail; + +    device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error); +    if (!device || dbus_error_is_set(&error)) +        goto fail; + +    *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi)); +    *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi)); + +    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);      libhal_free_string(device);      pa_log_debug("Loading module-oss with arguments '%s'", args); +    m = pa_module_load(u->core, "module-oss", args); +    pa_xfree(args); + +    if (!m) { +        pa_xfree(*sink_name); +        pa_xfree(*source_name); +        *sink_name = *source_name = NULL; +    } + +    return m; + +fail: +    if (dbus_error_is_set(&error)) { +        pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message); +        dbus_error_free(&error); +    } -    return pa_module_load(u->core, "module-oss", args); +    return NULL;  }  #endif -static dbus_bool_t hal_device_add(struct userdata *u, const char *udi, -                                  DBusError *error) -{ +static struct device* hal_device_add(struct userdata *u, const char *udi) {      pa_module* m = NULL;      struct device *d; +    char *sink_name = NULL, *source_name = NULL; + +    pa_assert(u); +    pa_assert(u->capability); +    pa_assert(!pa_hashmap_get(u->devices, udi)); -    switch(u->capability) {  #ifdef HAVE_ALSA -        case CAP_ALSA: -            m = hal_device_load_alsa(u, udi, error); -            break; +    if (strcmp(u->capability, CAPABILITY_ALSA) == 0) +        m = hal_device_load_alsa(u, udi, &sink_name, &source_name);  #endif  #ifdef HAVE_OSS -        case CAP_OSS: -#ifdef HAVE_ALSA -            if (u->use_oss) -#endif -                m = hal_device_load_oss(u, udi, error); -            break; +    if (strcmp(u->capability, CAPABILITY_OSS) == 0) +        m = hal_device_load_oss(u, udi, &sink_name, &source_name);  #endif -        default: -            assert(FALSE); /* never reached */ -            break; -    } -    if (!m || dbus_error_is_set(error)) -        return FALSE; +    if (!m) +        return NULL;      d = pa_xnew(struct device, 1); +    d->acl_race_fix = 0;      d->udi = pa_xstrdup(udi);      d->index = m->index; - +    d->sink_name = sink_name; +    d->source_name = source_name;      pa_hashmap_put(u->devices, d->udi, d); -    return TRUE; +    return d;  } -static int hal_device_add_all(struct userdata *u, capability_t capability) -{ +static int hal_device_add_all(struct userdata *u, const char *capability) {      DBusError error; -    int i,n,count; -    dbus_bool_t r; +    int i, n, count = 0;      char** udis; -    const char* cap = get_capability_name(capability); -    assert(capability < CAP_MAX); +    pa_assert(u); -    pa_log_info("Trying capability %u (%s)", capability, cap);      dbus_error_init(&error); -    udis = libhal_find_device_by_capability(u->ctx, cap, &n, &error); + +    if (u->capability && strcmp(u->capability, capability) != 0) +        return 0; + +    pa_log_info("Trying capability %s", capability); + +    udis = libhal_find_device_by_capability(u->context, capability, &n, &error);      if (dbus_error_is_set(&error)) { -        pa_log_error("Error finding devices: %s: %s", error.name, -                     error.message); +        pa_log_error("Error finding devices: %s: %s", error.name, error.message);          dbus_error_free(&error);          return -1;      } -    count = 0; -    u->capability = capability; -    for (i = 0; i < n; ++i) { -        r = hal_device_add(u, udis[i], &error); -        if (dbus_error_is_set(&error)) { -            pa_log_error("Error adding device: %s: %s", error.name, -                         error.message); -            dbus_error_free(&error); -            count = -1; -            break; + +    if (n > 0) { +        u->capability = capability; + +        for (i = 0; i < n; i++) { +            struct device *d; + +            if (!(d = hal_device_add(u, udis[i]))) +                pa_log_debug("Not loaded device %s", udis[i]); +            else { +                if (d->sink_name) +                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0); +                count++; +            }          } -        if (r) -            ++count;      }      libhal_free_string_array(udis);      return count;  } -static dbus_bool_t device_has_capability(LibHalContext *ctx, const char *udi, -                                         const char* cap, DBusError *error) -{ +static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){      dbus_bool_t has_prop; -    has_prop = libhal_device_property_exists(ctx, udi, "info.capabilities", -                                             error); + +    has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);      if (!has_prop || dbus_error_is_set(error))          return FALSE; -    return libhal_device_query_capability(ctx, udi, cap, error); +    return libhal_device_query_capability(context, udi, cap, error);  } -static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, -                                 const struct timeval *tv, void *userdata) -{ +static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {      DBusError error; -    struct timerdata *td = (struct timerdata*) userdata; +    struct timerdata *td = userdata;      dbus_error_init(&error); -    if (libhal_device_exists(td->u->ctx, td->udi, &error)) -        hal_device_add(td->u, td->udi, &error); -    if (dbus_error_is_set(&error)) { -        pa_log_error("Error adding device: %s: %s", error.name, -                     error.message); -        dbus_error_free(&error); +    if (!pa_hashmap_get(td->u->devices, td->udi)) { +        int b; +        struct device *d; + +        b = libhal_device_exists(td->u->context, td->udi, &error); + +        if (dbus_error_is_set(&error)) { +            pa_log_error("Error adding device: %s: %s", error.name, error.message); +            dbus_error_free(&error); +        } else if (b) { +            if (!(d = hal_device_add(td->u, td->udi))) +                pa_log_debug("Not loaded device %s", td->udi); +            else { +                if (d->sink_name) +                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0); +            } +        }      }      pa_xfree(td->udi); @@ -361,28 +421,68 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev,      ea->time_free(ev);  } -static void device_added_cb(LibHalContext *ctx, const char *udi) -{ +static void device_added_cb(LibHalContext *context, const char *udi) {      DBusError error;      struct timeval tv; -    dbus_bool_t has_cap;      struct timerdata *t; -    struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx); -    const char* cap = get_capability_name(u->capability); +    struct userdata *u; +    int good = 0; + +    pa_assert_se(u = libhal_ctx_get_user_data(context)); + +    if (pa_hashmap_get(u->devices, udi)) +        return;      pa_log_debug("HAL Device added: %s", udi);      dbus_error_init(&error); -    has_cap = device_has_capability(ctx, udi, cap, &error); -    if (dbus_error_is_set(&error)) { -        pa_log_error("Error getting capability: %s: %s", error.name, -                     error.message); -        dbus_error_free(&error); -        return; + +    if (u->capability) { + +        good = device_has_capability(context, udi, u->capability, &error); + +        if (dbus_error_is_set(&error)) { +            pa_log_error("Error getting capability: %s: %s", error.name, error.message); +            dbus_error_free(&error); +            return; +        } + +    } else { + +#ifdef HAVE_ALSA +        good = device_has_capability(context, udi, CAPABILITY_ALSA, &error); + +        if (dbus_error_is_set(&error)) { +            pa_log_error("Error getting capability: %s: %s", error.name, error.message); +            dbus_error_free(&error); +            return; +        } + +        if (good) +            u->capability = CAPABILITY_ALSA; +#endif +#if defined(HAVE_OSS) && defined(HAVE_ALSA) +        if (!good) { +#endif +#ifdef HAS_OSS +            good = device_has_capability(context, udi, CAPABILITY_OSS, &error); + +            if (dbus_error_is_set(&error)) { +                pa_log_error("Error getting capability: %s: %s", error.name, error.message); +                dbus_error_free(&error); +                return; +            } + +            if (good) +                u->capability = CAPABILITY_OSS; + +#endif +#if defined(HAVE_OSS) && defined(HAVE_ALSA) +        } +#endif      } -    /* skip it */ -    if (!has_cap) +    if (!good)          return;      /* actually add the device 1/2 second later */ @@ -392,187 +492,359 @@ static void device_added_cb(LibHalContext *ctx, const char *udi)      pa_gettimeofday(&tv);      pa_timeval_add(&tv, 500000); -    u->core->mainloop->time_new(u->core->mainloop, &tv, -                                device_added_time_cb, t); +    u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);  } -static void device_removed_cb(LibHalContext* ctx, const char *udi) -{ +static void device_removed_cb(LibHalContext* context, const char *udi) {      struct device *d; -    struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx); +    struct userdata *u; + +    pa_assert_se(u = libhal_ctx_get_user_data(context));      pa_log_debug("Device removed: %s", udi); +      if ((d = pa_hashmap_remove(u->devices, udi))) {          pa_module_unload_by_index(u->core, d->index);          hal_device_free(d);      }  } -static void new_capability_cb(LibHalContext *ctx, const char *udi, -                              const char* capability) -{ -    struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx); -    const char* capname = get_capability_name(u->capability); +static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) { +    struct userdata *u; + +    pa_assert_se(u = libhal_ctx_get_user_data(context)); -    if (capname && !strcmp(capname, capability)) { +    if (!u->capability || strcmp(u->capability, capability) == 0)          /* capability we care about, pretend it's a new device */ -        device_added_cb(ctx, udi); -    } +        device_added_cb(context, udi);  } -static void lost_capability_cb(LibHalContext *ctx, const char *udi, -                               const char* capability) -{ -    struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx); -    const char* capname = get_capability_name(u->capability); +static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) { +    struct userdata *u; -    if (capname && !strcmp(capname, capability)) { -        /* capability we care about, pretend it was removed */ -        device_removed_cb(ctx, udi); -    } -} +    pa_assert_se(u = libhal_ctx_get_user_data(context)); -#if 0 -static void property_modified_cb(LibHalContext *ctx, const char *udi, -                                 const char* key, -                                 dbus_bool_t is_removed, -                                 dbus_bool_t is_added) -{ +    if (u->capability && strcmp(u->capability, capability) == 0) +        /* capability we care about, pretend it was removed */ +        device_removed_cb(context, udi);  } -#endif -static void pa_hal_context_free(LibHalContext* hal_ctx) -{ +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) { +    struct userdata*u = userdata;      DBusError error; +    pa_assert(bus); +    pa_assert(message); +    pa_assert(u); +      dbus_error_init(&error); -    libhal_ctx_shutdown(hal_ctx, &error); -    libhal_ctx_free(hal_ctx); -    if (dbus_error_is_set(&error)) { -        dbus_error_free(&error); +    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", +                 dbus_message_get_interface(message), +                 dbus_message_get_path(message), +                 dbus_message_get_member(message)); + +    if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") || +        dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) { +        uint32_t uid; +        int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0; + +        if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) { +            pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message); +            goto finish; +        } + +        if (uid == getuid() || uid == geteuid()) { +            struct device *d; +            const char *udi; + +            udi = dbus_message_get_path(message); + +            if ((d = pa_hashmap_get(u->devices, udi))) { +                int send_acl_race_fix_message = 0; + +                d->acl_race_fix = 0; + +                if (d->sink_name) { +                    pa_sink *sink; + +                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) { +                        int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED; + +                        if (prev_suspended && !suspend) { +                            /* resume */ +                            if (pa_sink_suspend(sink, 0) >= 0) +                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0); +                            else +                                d->acl_race_fix = 1; + +                        } else if (!prev_suspended && suspend) { +                            /* suspend */ +                            if (pa_sink_suspend(sink, 1) >= 0) +                                send_acl_race_fix_message = 1; +                        } +                    } +                } + +                if (d->source_name) { +                    pa_source *source; + +                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) { +                        int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED; + +                        if (prev_suspended && !suspend) { +                            /* resume */ +                            if (pa_source_suspend(source, 0) < 0) +                                d->acl_race_fix = 1; + +                        } else if (!prev_suspended && suspend) { +                            /* suspend */ +                            if (pa_source_suspend(source, 0) >= 0) +                                send_acl_race_fix_message = 1; +                        } +                    } +                } + +                if (send_acl_race_fix_message) { +                    DBusMessage *msg; +                    msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage"); +                    dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL); +                    dbus_message_unref(msg); +                } + +            } else if (!suspend) +                device_added_cb(u->context, udi); +        } + +    } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) { +        /* We use this message to avoid a dirty race condition when we +           get an ACLAdded message before the previously owning PA +           sever has closed the device. We can remove this as +           soon as HAL learns frevoke() */ + +        const char *udi; +        struct device *d; + +        udi = dbus_message_get_path(message); + +        if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) { +            pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi); + +            d->acl_race_fix = 0; + +            if (d->sink_name) { +                pa_sink *sink; + +                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) { + +                    int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED; + +                    if (prev_suspended) { +                        /* resume */ +                        if (pa_sink_suspend(sink, 0) >= 0) +                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0); +                    } +                } +            } + +            if (d->source_name) { +                pa_source *source; + +                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) { + +                    int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED; + +                    if (prev_suspended) +                        pa_source_suspend(source, 0); +                } +            } + +        } else +            /* Yes, we don't check the UDI for validity, but hopefully HAL will */ +            device_added_cb(u->context, udi);      } + +finish: +    dbus_error_free(&error); + +    return DBUS_HANDLER_RESULT_HANDLED;  } -static void userdata_free(struct userdata *u) { -    pa_hal_context_free(u->ctx); -    /* free the devices with the hashmap */ -    pa_hashmap_free(u->devices, hal_device_free_cb, NULL); -    pa_dbus_connection_unref(u->conn); -    pa_xfree(u); +static void hal_context_free(LibHalContext* hal_context) { +    DBusError error; + +    dbus_error_init(&error); + +    libhal_ctx_shutdown(hal_context, &error); +    libhal_ctx_free(hal_context); + +    dbus_error_free(&error);  } -static LibHalContext* pa_hal_context_new(pa_core* c, DBusConnection *conn) -{ +static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {      DBusError error; -    LibHalContext *hal_ctx = NULL; +    LibHalContext *hal_context = NULL;      dbus_error_init(&error); -    if (!(hal_ctx = libhal_ctx_new())) { + +    if (!(hal_context = libhal_ctx_new())) {          pa_log_error("libhal_ctx_new() failed");          goto fail;      } -    if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) { -        pa_log_error("Error establishing DBUS connection: %s: %s", -                     error.name, error.message); +    if (!libhal_ctx_set_dbus_connection(hal_context, conn)) { +        pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);          goto fail;      } -    if (!libhal_ctx_init(hal_ctx, &error)) { -        pa_log_error("Couldn't connect to hald: %s: %s", -                     error.name, error.message); +    if (!libhal_ctx_init(hal_context, &error)) { +        pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);          goto fail;      } -    return hal_ctx; +    return hal_context;  fail: -    if (hal_ctx) -        pa_hal_context_free(hal_ctx); +    if (hal_context) +        hal_context_free(hal_context); -    if (dbus_error_is_set(&error)) -        dbus_error_free(&error); +    dbus_error_free(&error);      return NULL;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      DBusError error;      pa_dbus_connection *conn;      struct userdata *u = NULL; -    LibHalContext *hal_ctx = NULL; +    LibHalContext *hal_context = NULL;      int n = 0; +    pa_modargs *ma; +    const char *api; -    assert(c); -    assert(m); +    pa_assert(m);      dbus_error_init(&error); -    if (!(conn = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &error))) { -        pa_log_error("Unable to contact DBUS system bus: %s: %s", -                     error.name, error.message); -        dbus_error_free(&error); -        return -1; + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments"); +        goto fail;      } -    if (!(hal_ctx = pa_hal_context_new(c, pa_dbus_connection_get(conn)))) { +    if ((api = pa_modargs_get_value(ma, "api", NULL))) { +        int good = 0; + +#ifdef HAVE_ALSA +        if (strcmp(api, CAPABILITY_ALSA) == 0) { +            good = 1; +            api = CAPABILITY_ALSA; +        } +#endif +#ifdef HAVE_OSS +        if (strcmp(api, CAPABILITY_OSS) == 0) { +            good = 1; +            api = CAPABILITY_OSS; +        } +#endif + +        if (!good) { +            pa_log_error("Invalid API specification."); +            goto fail; +        } +    } + +    if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) { +        if (conn) +            pa_dbus_connection_unref(conn); +        pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message); +        goto fail; +    } + +    if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {          /* pa_hal_context_new() logs appropriate errors */ -        return -1; +        pa_dbus_connection_unref(conn); +        goto fail;      }      u = pa_xnew(struct userdata, 1); -    u->core = c; -    u->ctx = hal_ctx; -    u->conn = conn; -    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, -                                pa_idxset_string_compare_func); -    m->userdata = (void*) u; +    u->core = m->core; +    u->context = hal_context; +    u->connection = conn; +    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); +    u->capability = api; +    m->userdata = u;  #ifdef HAVE_ALSA -    n = hal_device_add_all(u, CAP_ALSA); +    n = hal_device_add_all(u, CAPABILITY_ALSA);  #endif  #if defined(HAVE_ALSA) && defined(HAVE_OSS) -    u->use_oss = 0; - -    if (n <= 0) { +    if (n <= 0)  #endif  #ifdef HAVE_OSS -        n += hal_device_add_all(u, CAP_OSS); +        n += hal_device_add_all(u, CAPABILITY_OSS);  #endif -#if defined(HAVE_ALSA) && defined(HAVE_OSS) -        /* We found something with OSS, but didn't find anything with -         * ALSA. Then let's use only OSS from now on. */ -        if (n > 0) -            u->use_oss = 1; +    libhal_ctx_set_user_data(hal_context, u); +    libhal_ctx_set_device_added(hal_context, device_added_cb); +    libhal_ctx_set_device_removed(hal_context, device_removed_cb); +    libhal_ctx_set_device_new_capability(hal_context, new_capability_cb); +    libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb); + +    if (!libhal_device_property_watch_all(hal_context, &error)) { +        pa_log_error("Error monitoring device list: %s: %s", error.name, error.message); +        goto fail;      } -#endif -    libhal_ctx_set_user_data(hal_ctx, u); -    libhal_ctx_set_device_added(hal_ctx, device_added_cb); -    libhal_ctx_set_device_removed(hal_ctx, device_removed_cb); -    libhal_ctx_set_device_new_capability(hal_ctx, new_capability_cb); -    libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability_cb); -    /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/ +    if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) { +        pa_log_error("Failed to add filter function"); +        goto fail; +    } -    dbus_error_init(&error); -    if (!libhal_device_property_watch_all(hal_ctx, &error)) { -        pa_log_error("error monitoring device list: %s: %s", -                     error.name, error.message); -        dbus_error_free(&error); -        userdata_free(u); -        return -1; +    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error); +    if (dbus_error_is_set(&error)) { +        pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message); +        goto fail; +    } + +    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error); +    if (dbus_error_is_set(&error)) { +        pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message); +        goto fail;      } -    pa_log_info("loaded %i modules.", n); +    pa_log_info("Loaded %i modules.", n); + +    pa_modargs_free(ma);      return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    dbus_error_free(&error); +    pa__done(m); + +    return -1;  } -void pa__done(PA_GCC_UNUSED pa_core *c, pa_module *m) { -    assert (c && m); +void pa__done(pa_module *m) { +    struct userdata *u; + +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->context) +        hal_context_free(u->context); -    /* free the user data */ -    userdata_free(m->userdata); +    if (u->devices) +        pa_hashmap_free(u->devices, hal_device_free_cb, NULL); + +    if (u->connection) +        pa_dbus_connection_unref(u->connection); + +    pa_xfree(u);  } diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index a40ebe29..5019d656 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -3,7 +3,7 @@  /***    This file is part of PulseAudio. -  Copyright 2006, 2007 Lennart Poettering and Tanu Kaskinen +  Copyright 2006 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 @@ -25,858 +25,431 @@  #include <config.h>  #endif -#include <pthread.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <stdio.h>  #include <assert.h>  #include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h>  #include <jack/jack.h> -#include <jack/ringbuffer.h> -#include <jack/types.h> -#include <pulse/mainloop-api.h> -#include <pulse/sample.h> -#include <pulse/channelmap.h>  #include <pulse/xmalloc.h> +#include <pulsecore/core-error.h>  #include <pulsecore/sink.h>  #include <pulsecore/module.h> -#include <pulsecore/core.h> -#include <pulsecore/log.h>  #include <pulsecore/core-util.h> -#include <pulsecore/core-error.h> -#include <pulsecore/pipe.h>  #include <pulsecore/modargs.h> -#include <pulsecore/strbuf.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/sample-util.h>  #include "module-jack-sink-symdef.h" -PA_MODULE_AUTHOR("Lennart Poettering & Tanu Kaskinen") -PA_MODULE_DESCRIPTION("Jack Sink") +/* General overview: + * + * Because JACK has a very unflexible event loop management, which + * doesn't allow us to add our own event sources to the event thread + * we cannot use the JACK real-time thread for dispatching our PA + * work. Instead, we run an additional RT thread which does most of + * the PA handling, and have the JACK RT thread request data from it + * via pa_asyncmsgq. The cost is an additional context switch which + * should hopefully not be that expensive if RT scheduling is + * enabled. A better fix would only be possible with additional event + * source support in JACK. + */ + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("JACK Sink")  PA_MODULE_VERSION(PACKAGE_VERSION)  PA_MODULE_USAGE(          "sink_name=<name of sink> "          "server_name=<jack server name> "          "client_name=<jack client name> "          "channels=<number of channels> " -        "connect=<connect ports automatically?> " -        "buffersize=<intermediate buffering in frames> " +        "connect=<connect ports?> "          "channel_map=<channel map>")  #define DEFAULT_SINK_NAME "jack_out" -#define DEFAULT_CLIENT_NAME "PulseAudio(output)" -#define DEFAULT_RINGBUFFER_SIZE 4096 -  struct userdata { +    pa_core *core; +    pa_module *module;      pa_sink *sink;      unsigned channels; -    unsigned frame_size; -    jack_port_t* j_ports[PA_CHANNELS_MAX]; -    jack_client_t *j_client; +    jack_port_t* port[PA_CHANNELS_MAX]; +    jack_client_t *client; -    jack_nframes_t j_buffersize; +    void *buffer[PA_CHANNELS_MAX]; -    /* For avoiding j_buffersize changes at a wrong moment. */ -    pthread_mutex_t buffersize_mutex; +    pa_thread_mq thread_mq; +    pa_asyncmsgq *jack_msgq; +    pa_rtpoll *rtpoll; +    pa_rtpoll_item *rtpoll_item; -    /* The intermediate store where the pulse side writes to and the jack side -       reads from. */ -    jack_ringbuffer_t* ringbuffer; -     -    /* For signaling when there's room in the ringbuffer. */ -    pthread_mutex_t cond_mutex; -    pthread_cond_t ringbuffer_cond; +    pa_thread *thread; -    pthread_t filler_thread; /* Keeps the ringbuffer filled. */ +    jack_nframes_t frames_in_buffer; +    jack_nframes_t saved_frame_time; +    pa_bool_t saved_frame_time_valid; +}; -    int ringbuffer_is_full; -    int filler_thread_is_running; -    int quit_requested; +static const char* const valid_modargs[] = { +    "sink_name", +    "server_name", +    "client_name", +    "channels", +    "connect", +    "channel_map", +    NULL +}; -    int pipe_fd_type; -    int pipe_fds[2]; -    pa_io_event *io_event; +enum { +    SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX, +    SINK_MESSAGE_ON_SHUTDOWN  }; +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { +    struct userdata *u = PA_SINK(o)->userdata; -struct options { -    char* sink_name; -    int sink_name_given; +    switch (code) { -    char* server_name; /* May be NULL */ -    int server_name_given; +        case SINK_MESSAGE_RENDER: -    char* client_name; -    int client_name_given; +            /* Handle the request from the JACK thread */ -    unsigned channels; -    int channels_given; +            if (u->sink->thread_info.state == PA_SINK_RUNNING) { +                pa_memchunk chunk; +                size_t nbytes; +                void *p; -    int connect; -    int connect_given; +                pa_assert(offset > 0); +                nbytes = offset * pa_frame_size(&u->sink->sample_spec); -    unsigned buffersize; -    int buffersize_given; +                pa_sink_render_full(u->sink, nbytes, &chunk); -    pa_channel_map map; -    int map_given; -}; +                p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; +                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), offset); +                pa_memblock_release(chunk.memblock); +                pa_memblock_unref(chunk.memblock); +            } else { +                unsigned c; +                pa_sample_spec ss; -static const char* const valid_modargs[] = { -    "sink_name", -    "server_name", -    "client_name", -    "channels", -    "connect", -    "buffersize", -    "channel_map", -    NULL -}; +                /* Humm, we're not RUNNING, hence let's write some silence */ +                ss = u->sink->sample_spec; +                ss.channels = 1; -/* Initialization functions. */ -static int parse_options(struct options* o, const char* argument); -static void set_default_channels(pa_module* self, struct options* o); -static int create_sink(pa_module* self, struct options *o); -static void connect_ports(pa_module* self); -static int start_filling_ringbuffer(pa_module* self); +                for (c = 0; c < u->channels; c++) +                    pa_silence_memory(u->buffer[c], offset * pa_sample_size(&ss), &ss); +            } -/* Various callbacks. */ -static void jack_error_func(const char* t); -static pa_usec_t sink_get_latency_cb(pa_sink* s); -static int jack_process(jack_nframes_t nframes, void* arg); -static int jack_blocksize_cb(jack_nframes_t nframes, void* arg); -static void jack_shutdown(void* arg); -static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd, -                        pa_io_event_flags_t flags, void* userdata); +            u->frames_in_buffer = offset; +            u->saved_frame_time = * (jack_nframes_t*) data; +            u->saved_frame_time_valid = TRUE; -/* The ringbuffer filler thread runs in this function. */ -static void* fill_ringbuffer(void* arg); +            return 0; -/* request_render asks asynchronously the mainloop to call io_event_cb. */ -static void request_render(struct userdata* u); +        case SINK_MESSAGE_ON_SHUTDOWN: +            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +            return 0; +        case PA_SINK_MESSAGE_GET_LATENCY: { +            jack_nframes_t l, ft, d; +            size_t n; -int pa__init(pa_core* c, pa_module* self) { -    struct userdata* u = NULL; -    struct options o; -    unsigned i; -     -    assert(c); -    assert(self); - -    o.sink_name = NULL; -    o.server_name = NULL; -    o.client_name = NULL; -     -    self->userdata = pa_xnew0(struct userdata, 1); -    u = self->userdata; -     -    u->pipe_fds[0] = u->pipe_fds[1] = -1; -    u->pipe_fd_type = 0; -    u->ringbuffer_is_full = 0; -    u->filler_thread_is_running = 0; -    u->quit_requested = 0; -    pthread_mutex_init(&u->buffersize_mutex, NULL); -    pthread_mutex_init(&u->cond_mutex, NULL); -    pthread_cond_init(&u->ringbuffer_cond, NULL); -     -    if (parse_options(&o, self->argument) != 0) -        goto fail; -     -    jack_set_error_function(jack_error_func); -     -    if (!(u->j_client = jack_client_open( -                          o.client_name, -                          o.server_name ? JackServerName : JackNullOption, -                          NULL, o.server_name))) { -        pa_log_error("jack_client_open() failed."); -        goto fail; -    } -    pa_log_info("Successfully connected as '%s'", -                jack_get_client_name(u->j_client)); -     -    if (!o.channels_given) -        set_default_channels(self, &o); -     -    u->channels = o.channels; -     -    if (!o.map_given) -        pa_channel_map_init_auto(&o.map, u->channels, PA_CHANNEL_MAP_ALSA); -     -    for (i = 0; i < u->channels; i++) { -        char* port_name = pa_sprintf_malloc( -                              "out_%i:%s", i+1, -                              pa_channel_position_to_string(o.map.map[i])); -         -        if (!(u->j_ports[i] = jack_port_register( -                                  u->j_client, port_name, -                                  JACK_DEFAULT_AUDIO_TYPE, -                                  JackPortIsOutput|JackPortIsTerminal, 0))) { -            pa_log("jack_port_register() failed."); -            goto fail; +            /* This is the "worst-case" latency */ +            l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer; + +            if (u->saved_frame_time_valid) { +                /* Adjust the worst case latency by the time that +                 * passed since we last handed data to JACK */ + +                ft = jack_frame_time(u->client); +                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; +                l = l > d ? l - d : 0; +            } + +            /* Convert it to usec */ +            n = l * pa_frame_size(&u->sink->sample_spec); +            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); + +            return 0;          } -         -        pa_xfree(port_name);      } -     -    if (pipe(u->pipe_fds) < 0) { -        pa_log("pipe() failed: %s", pa_cstrerror(errno)); -        goto fail; -    } -    pa_make_nonblock_fd(u->pipe_fds[1]); -     -    if (create_sink(self, &o) != 0) -        goto fail; -    u->frame_size = pa_frame_size(&u->sink->sample_spec); -    u->j_buffersize = jack_get_buffer_size(u->j_client); -     -    /* If the ringbuffer size were equal to the jack buffer size, a full block -       would never fit in the ringbuffer, because the ringbuffer can never be -       totally full: one slot is always wasted. */ -    if (o.buffersize <= u->j_buffersize) { -        o.buffersize = u->j_buffersize + 1; -    } -    /* The actual ringbuffer size will be rounded up to the nearest power of -       two. */ -    if (!(u->ringbuffer = jack_ringbuffer_create( -                              o.buffersize * u->frame_size))) { -        pa_log("jack_ringbuffer_create() failed."); -        goto fail; -    } -    assert((u->ringbuffer->size % sizeof(float)) == 0); -    pa_log_info("buffersize is %u frames (%u samples, %u bytes).", -                u->ringbuffer->size / u->frame_size, -                u->ringbuffer->size / sizeof(float), -                u->ringbuffer->size); -     -    jack_set_process_callback(u->j_client, jack_process, u); -    jack_set_buffer_size_callback(u->j_client, jack_blocksize_cb, u); -    jack_on_shutdown(u->j_client, jack_shutdown, u); -     -    if (jack_activate(u->j_client)) { -        pa_log("jack_activate() failed."); -        goto fail; -    } +    return pa_sink_process_msg(o, code, data, offset, memchunk); +} -    if (o.connect) -        connect_ports(self); +static int jack_process(jack_nframes_t nframes, void *arg) { +    struct userdata *u = arg; +    unsigned c; +    jack_nframes_t frame_time; +    pa_assert(u); -    u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], -                                      PA_IO_EVENT_INPUT, io_event_cb, self); -     -    if (start_filling_ringbuffer(self) != 0) -        goto fail; +    /* We just forward the request to our other RT thread */ -    pa_xfree(o.sink_name); -    pa_xfree(o.server_name); -    pa_xfree(o.client_name); -     -    return 0; +    for (c = 0; c < u->channels; c++) +        pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes)); -fail: -    pa_xfree(o.sink_name); -    pa_xfree(o.server_name); -    pa_xfree(o.client_name); -    pa__done(c, self); +    frame_time = jack_frame_time(u->client); -    return -1; +    pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0); +    return 0;  } +static void thread_func(void *userdata) { +    struct userdata *u = userdata; -static int parse_options(struct options* o, const char* argument) { -    pa_modargs *ma = NULL; -    const char* arg_val; -    pa_strbuf* strbuf; -     -    assert(o); +    pa_assert(u); -    if (!(ma = pa_modargs_new(argument, valid_modargs))) { -        pa_log_error("Failed to parse module arguments."); -        goto fail; -    } +    pa_log_debug("Thread starting up"); -    strbuf = pa_strbuf_new(); -    if ((arg_val = pa_modargs_get_value(ma, "sink_name", NULL))) { -        pa_strbuf_puts(strbuf, arg_val); -        o->sink_name = pa_strbuf_tostring(strbuf); -        o->sink_name_given = 1; -    } else { -        pa_strbuf_puts(strbuf, DEFAULT_SINK_NAME); -        o->sink_name = pa_strbuf_tostring(strbuf); -        o->sink_name_given = 0; -    } -    pa_strbuf_free(strbuf); - -    strbuf = pa_strbuf_new(); -    if ((arg_val = pa_modargs_get_value(ma, "server_name", NULL))) { -        pa_strbuf_puts(strbuf, arg_val); -        o->server_name = pa_strbuf_tostring(strbuf); -        o->server_name_given = 1; -    } else { -        o->server_name = NULL; -        o->server_name_given = 0; -    } -    pa_strbuf_free(strbuf); - -    strbuf = pa_strbuf_new(); -    if ((arg_val = pa_modargs_get_value(ma, "client_name", NULL))) { -        pa_strbuf_puts(strbuf, arg_val); -        o->client_name = pa_strbuf_tostring(strbuf); -        o->client_name_given = 1; -    } else { -        pa_strbuf_puts(strbuf, DEFAULT_CLIENT_NAME); -        o->client_name = pa_strbuf_tostring(strbuf); -        o->client_name_given = 1; -    } -    pa_strbuf_free(strbuf); - -    if (pa_modargs_get_value(ma, "channels", NULL)) { -        o->channels_given = 1; -        if (pa_modargs_get_value_u32(ma, "channels", &o->channels) < 0 || -            o->channels == 0 || -            o->channels >= PA_CHANNELS_MAX) { -            pa_log_error("Failed to parse the \"channels\" argument."); -            goto fail; -        } -    } else { -        o->channels = 0; /* The actual default value is the number of physical -                            input ports in jack (unknown at the moment), or if -                            that's zero, then the default_sample_spec.channels -                            of the core. */ -        o->channels_given = 0; -    } +    if (u->core->high_priority) +        pa_make_realtime(); -    if (pa_modargs_get_value(ma, "connect", NULL)) { -        o->connect_given = 1; -        if (pa_modargs_get_value_boolean(ma, "connect", &o->connect) < 0) { -            pa_log_error("Failed to parse the \"connect\" argument."); -            goto fail; -        } -    } else { -        o->connect = 1; -        o->connect_given = 0; -    } +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); -    if (pa_modargs_get_value(ma, "buffersize", NULL)) { -        o->buffersize_given = 1; -        if (pa_modargs_get_value_u32(ma, "buffersize", &o->buffersize) < 0) { -            pa_log_error("Failed to parse the \"buffersize\" argument."); -            goto fail; -        } -    } else { -        o->buffersize = DEFAULT_RINGBUFFER_SIZE; -        o->buffersize_given = 0; -    } +    for (;;) { +        int ret; -    if (pa_modargs_get_value(ma, "channel_map", NULL)) { -        o->map_given = 1; -        if (pa_modargs_get_channel_map(ma, &o->map) < 0) { -            pa_log_error("Failed to parse the \"channel_map\" argument."); +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)              goto fail; -        } -        /* channel_map specifies the channel count too. */ -        if (o->channels_given && (o->channels != o->map.channels)) { -            pa_log_error( -                "\"channels\" and \"channel_map\" arguments conficted. If you " -                "use the \"channel_map\" argument, you can omit the " -                "\"channels\" argument."); -            goto fail; -        } else { -            o->channels = o->map.channels; -            o->channels_given = 1; -        } -    } else { -        /* The actual default value is the default alsa mappings, but that -           can't be set until the channel count is known. Here we initialize -           the map to some valid value, although the value won't be used. */ -        pa_channel_map_init_stereo(&o->map); -        o->map_given = 0; +        if (ret == 0) +            goto finish;      } -    pa_modargs_free(ma); - -    return 0; -  fail: -    if (ma) -      pa_modargs_free(ma); +    /* 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); -    return -1; +finish: +    pa_log_debug("Thread shutting down");  } +static void jack_error_func(const char*t) { +    char *s; -static void set_default_channels(pa_module* self, struct options* o) { -    struct userdata* u; -    const char **ports, **p; -     -    assert(self); -    assert(o); -    assert(self->userdata); - -    u = self->userdata; -     -    assert(u->j_client); -    assert(self->core); -     -    o->channels = 0; -     -    ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE, -                           JackPortIsPhysical|JackPortIsInput); -     -    for (p = ports; *p; p++) -        o->channels++; -     -    free(ports); -     -    if (o->channels >= PA_CHANNELS_MAX) -        o->channels = PA_CHANNELS_MAX - 1; -     -    if (o->channels == 0) -        o->channels = self->core->default_sample_spec.channels; +    s = pa_xstrndup(t, strcspn(t, "\n\r")); +    pa_log_warn("JACK error >%s<", s); +    pa_xfree(s);  } +static void jack_init(void *arg) { +    struct userdata *u = arg; -static int create_sink(pa_module* self, struct options* o) { -    struct userdata* u; -    pa_sample_spec ss; -    char *t; -     -    assert(self); -    assert(o); -    assert(self->userdata); - -    u = self->userdata; -     -    assert(u->j_client); -     -    ss.channels = u->channels; -    ss.rate = jack_get_sample_rate(u->j_client); -    ss.format = PA_SAMPLE_FLOAT32NE; -    assert(pa_sample_spec_valid(&ss)); +    pa_log_info("JACK thread starting up."); -    if (!(u->sink = pa_sink_new(self->core, __FILE__, o->sink_name, 0, &ss, -                                &o->map))) { -        pa_log("failed to create sink."); -        return -1; -    } -     -    u->sink->userdata = u; -    pa_sink_set_owner(u->sink, self); -     -    pa_sink_set_description( -        u->sink, -        t = pa_sprintf_malloc("Jack sink (%s)", -                              jack_get_client_name(u->j_client))); -    pa_xfree(t); -     -    u->sink->get_latency = sink_get_latency_cb; -     -    return 0; +    if (u->core->high_priority) +        pa_make_realtime();  } +static void jack_shutdown(void* arg) { +    struct userdata *u = arg; -static void connect_ports(pa_module* self) { -    struct userdata* u; -    unsigned i; -    const char **ports, **p; -     -    assert(self); -    assert(self->userdata); - -    u = self->userdata; -     -    assert(u->j_client); -     -    ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE, -                           JackPortIsPhysical|JackPortIsInput); -     -    for (i = 0, p = ports; i < u->channels; i++, p++) { -        assert(u->j_ports[i]); -         -        if (!*p) { -            pa_log("Not enough physical output ports, leaving unconnected."); -            break; -        } -         -        pa_log_info("connecting %s to %s", -                    jack_port_name(u->j_ports[i]), *p); -         -        if (jack_connect(u->j_client, jack_port_name(u->j_ports[i]), *p)) { -            pa_log("Failed to connect %s to %s, leaving unconnected.", -                   jack_port_name(u->j_ports[i]), *p); -            break; -        } -    } -     -    free(ports); +    pa_log_info("JACK thread shutting down.."); +    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);  } +int pa__init(pa_module*m) { +    struct userdata *u = NULL; +    pa_sample_spec ss; +    pa_channel_map map; +    pa_modargs *ma = NULL; +    jack_status_t status; +    const char *server_name, *client_name; +    uint32_t channels = 0; +    int do_connect = 1; +    unsigned i; +    const char **ports = NULL, **p; +    char *t; -static int start_filling_ringbuffer(pa_module* self) { -    struct userdata* u; -    pthread_attr_t thread_attributes; +    pa_assert(m); -    assert(self); -    assert(self->userdata); +    jack_set_error_function(jack_error_func); -    u = self->userdata; -     -    pthread_attr_init(&thread_attributes); -     -    if (pthread_attr_setinheritsched(&thread_attributes, -                                     PTHREAD_INHERIT_SCHED) != 0) { -        pa_log("pthread_attr_setinheritsched() failed."); +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments.");          goto fail;      } -    else if (pthread_create(&u->filler_thread, &thread_attributes, -                            fill_ringbuffer, u) != 0) { -        pa_log("pthread_create() failed."); + +    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { +        pa_log("Failed to parse connect= argument.");          goto fail;      } -     -    u->filler_thread_is_running = 1; -     -    pthread_attr_destroy(&thread_attributes); - -    return 0; -     -fail: -    pthread_attr_destroy(&thread_attributes); -    return -1; -} +    server_name = pa_modargs_get_value(ma, "server_name", NULL); +    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink"); + +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    u->saved_frame_time_valid = FALSE; +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + +    /* The queue linking the JACK thread and our RT thread */ +    u->jack_msgq = pa_asyncmsgq_new(0); + +    /* The msgq from the JACK RT thread should have an even higher +     * priority than the normal message queues, to match the guarantee +     * all other drivers make: supplying the audio device with data is +     * the top priority -- and as long as that is possible we don't do +     * anything else */ +    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); + +    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { +        pa_log("jack_client_open() failed."); +        goto fail; +    } -static void jack_error_func(const char* t) { -    pa_log_warn("JACK error >%s<", t); -} - +    ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); -static pa_usec_t sink_get_latency_cb(pa_sink* s) { -    /* The latency is approximately the sum of the first port's latency, -       buffersize of jack and the ringbuffer size. Maybe instead of using just -       the first port, the max of all ports' latencies should be used? */ -    struct userdata* u; -    jack_nframes_t l; -     -    assert(s); -    assert(s->userdata); - -    u = s->userdata; -     -    l = jack_port_get_total_latency(u->j_client, u->j_ports[0]) + -        u->j_buffersize + u->ringbuffer->size / u->frame_size; -     -    return pa_bytes_to_usec(l * u->frame_size, &s->sample_spec); -} +    channels = 0; +    for (p = ports; *p; p++) +        channels++; +    if (!channels) +        channels = m->core->default_sample_spec.channels; -static int jack_process(jack_nframes_t nframes, void* arg) { -    struct userdata* u = arg; -    float* j_buffers[PA_CHANNELS_MAX]; -    unsigned nsamples = u->channels * nframes; -    unsigned sample_idx_part1, sample_idx_part2; -    jack_nframes_t frame_idx; -    jack_ringbuffer_data_t data[2]; /* In case the readable area in the -                                       ringbuffer is non-continuous, the data -                                       will be split in two parts. */ -    unsigned chan; -    unsigned samples_left_over; -     -    for (chan = 0; chan < u->channels; chan++) { -        j_buffers[chan] = jack_port_get_buffer(u->j_ports[chan], nframes); -    } -     -    jack_ringbuffer_get_read_vector(u->ringbuffer, data); -     -    /* We assume that the possible discontinuity doesn't happen in the middle -     * of a sample. Should be a safe assumption. */ -    assert(((data[0].len % sizeof(float)) == 0) || -           (data[1].len == 0)); -     -    /* Copy from the first part of data until enough samples are copied or the -       first part ends. */ -    sample_idx_part1 = 0; -    chan = 0; -    frame_idx = 0; -    while (sample_idx_part1 < nsamples && -           ((sample_idx_part1 + 1) * sizeof(float)) <= data[0].len) { -        float *s = ((float*) data[0].buf) + sample_idx_part1; -        float *d = j_buffers[chan] + frame_idx; -        *d = *s; - -        sample_idx_part1++; -        chan = (chan + 1) % u->channels; -        frame_idx = sample_idx_part1 / u->channels; -    } -     -    samples_left_over = nsamples - sample_idx_part1; -     -    /* Copy from the second part of data until enough samples are copied or the -       second part ends. */ -    sample_idx_part2 = 0; -    while (sample_idx_part2 < samples_left_over && -           ((sample_idx_part2 + 1) * sizeof(float)) <= data[1].len) { -        float *s = ((float*) data[1].buf) + sample_idx_part2; -        float *d = j_buffers[chan] + frame_idx; -        *d = *s; - -        sample_idx_part2++; -        chan = (chan + 1) % u->channels; -        frame_idx = (sample_idx_part1 + sample_idx_part2) / u->channels; +    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) { +        pa_log("Failed to parse channels= argument."); +        goto fail;      } -     -    samples_left_over -= sample_idx_part2; -     -    /* If there's still samples left, fill the buffers with zeros. */ -    while (samples_left_over > 0) { -        float *d = j_buffers[chan] + frame_idx; -        *d = 0.0; - -        samples_left_over--; -        chan = (chan + 1) % u->channels; -        frame_idx = (nsamples - samples_left_over) / u->channels; + +    pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA); +    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { +        pa_log("Failed to parse channel_map= argument."); +        goto fail;      } -     -    jack_ringbuffer_read_advance( -        u->ringbuffer, (sample_idx_part1 + sample_idx_part2) * sizeof(float)); -     -    /* Tell the rendering part that there is room in the ringbuffer. */ -    u->ringbuffer_is_full = 0; -    pthread_cond_signal(&u->ringbuffer_cond); -     -    return 0; -} +    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); + +    ss.channels = u->channels = channels; +    ss.rate = jack_get_sample_rate(u->client); +    ss.format = PA_SAMPLE_FLOAT32NE; -static int jack_blocksize_cb(jack_nframes_t nframes, void* arg) { -    /* This gets called in the processing thread, so do we have to be realtime -       safe? No, we can do whatever we want. User gets silence while we do it. -        -       In addition to just updating the j_buffersize field in userdata, we have -       to create a new ringbuffer, if the new buffer size is bigger or equal to -       the old ringbuffer size. */ -    struct userdata* u = arg; -     -    assert(u); -     -    /* We don't want to change the blocksize and the ringbuffer while rendering -       is going on. */ -    pthread_mutex_lock(&u->buffersize_mutex); -     -    u->j_buffersize = nframes; -     -    if ((u->ringbuffer->size / u->frame_size) <= nframes) { -        /* We have to create a new ringbuffer. What are we going to do with the -           old data in the old buffer? We throw it away, because we're lazy -           coders. The listening experience is likely to get ruined anyway -           during the blocksize change. */ -        jack_ringbuffer_free(u->ringbuffer); -         -        /* The actual ringbuffer size will be rounded up to the nearest power -           of two. */ -        if (!(u->ringbuffer = -                  jack_ringbuffer_create((nframes + 1) * u->frame_size))) { -            pa_log_error( -                "jack_ringbuffer_create() failed while changing jack's buffer " -                "size, module exiting."); -            jack_client_close(u->j_client); -            u->quit_requested = 1; +    pa_assert(pa_sample_spec_valid(&ss)); + +    for (i = 0; i < ss.channels; i++) { +        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) { +            pa_log("jack_port_register() failed."); +            goto fail;          } -        assert((u->ringbuffer->size % sizeof(float)) == 0); -        pa_log_info("buffersize is %u frames (%u samples, %u bytes).", -                    u->ringbuffer->size / u->frame_size, -                    u->ringbuffer->size / sizeof(float), -                    u->ringbuffer->size);      } -     -    pthread_mutex_unlock(&u->buffersize_mutex); -     -    return 0; -} +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { +        pa_log("failed to create sink."); +        goto fail; +    } -static void jack_shutdown(void* arg) { -    struct userdata* u = arg; -    assert(u); +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->userdata = u; +    u->sink->flags = PA_SINK_LATENCY; -    u->quit_requested = 1; -    request_render(u); -} +    pa_sink_set_module(u->sink, m); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client))); +    pa_xfree(t); +    jack_set_process_callback(u->client, jack_process, u); +    jack_on_shutdown(u->client, jack_shutdown, u); +    jack_set_thread_init_callback(u->client, jack_init, u); -static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd, -                        pa_io_event_flags_t flags, void* userdata) { -    pa_module* self = userdata; -    struct userdata* u; -    char x; -    jack_ringbuffer_data_t buffer[2]; /* The write area in the ringbuffer may -                                         be split in two parts. */ -    pa_memchunk chunk; /* This is the data source. */ -    unsigned part1_length, part2_length; -    unsigned sample_idx_part1, sample_idx_part2; -    unsigned chan; -    unsigned frame_size; -    int rem; -     -    assert(m); -    assert(e); -    assert(flags == PA_IO_EVENT_INPUT); -    assert(self); -    assert(self->userdata); - -    u = self->userdata; -     -    assert(u->pipe_fds[0] == fd); - -    pa_read(fd, &x, 1, &u->pipe_fd_type); - -    if (u->quit_requested) { -        pa_module_unload_request(self); -        return; +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail;      } -    frame_size = u->frame_size; -     -    /* No blocksize changes during rendering, please. */ -    pthread_mutex_lock(&u->buffersize_mutex); -     -    jack_ringbuffer_get_write_vector(u->ringbuffer, buffer); -    assert(((buffer[0].len % sizeof(float)) == 0) || (buffer[1].len == 0)); -     -    part1_length = buffer[0].len / sizeof(float); -    part2_length = buffer[1].len / sizeof(float); - -    /* If the amount of free space is not a multiple of the frame size, we have -       to truncate the lengths so that we process only complete frames. */ -    if ((rem = (part1_length + part2_length) % u->channels) != 0) { -        if (part2_length >= rem) { -            part2_length -= rem; -        } else { -            part1_length -= rem - part2_length; -            part2_length = 0; -        } +    if (jack_activate(u->client)) { +        pa_log("jack_activate() failed"); +        goto fail;      } -     -    /* pa_sink_render_full doesn't accept zero length, so we have do the -       copying only if there's data to copy, which actually makes a kind of -       sense. */ -    if (part1_length > 0 || part2_length > 0) { -        pa_sink_render_full(u->sink, -                            (part1_length + part2_length) * sizeof(float), -                            &chunk); -         -        /* Write to the first part of the buffer. */ -        for (sample_idx_part1 = 0; -             sample_idx_part1 < part1_length; -             sample_idx_part1++) { -            float *s = -                ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + -                sample_idx_part1; -            float *d = ((float*) buffer[0].buf) + sample_idx_part1; -            *d = *s; -        } -         -        /* Write to the second part of the buffer. */ -        for (sample_idx_part2 = 0; -             sample_idx_part2 < part2_length; -             sample_idx_part2++) { -            float *s = -                ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + -                sample_idx_part1 + sample_idx_part2; -            float *d = ((float*) buffer[1].buf) + sample_idx_part2; -            *d = *s; + +    if (do_connect) { +        for (i = 0, p = ports; i < ss.channels; i++, p++) { + +            if (!*p) { +                pa_log("Not enough physical output ports, leaving unconnected."); +                break; +            } + +            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); + +            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) { +                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); +                break; +            }          } -         -        pa_memblock_unref(chunk.memblock); -         -        jack_ringbuffer_write_advance( -            u->ringbuffer, (part1_length + part2_length) * sizeof(float));      } -     -    /* Blocksize can be changed again. */ -    pthread_mutex_unlock(&u->buffersize_mutex); -} +    pa_sink_put(u->sink); -static void* fill_ringbuffer(void* arg) { -    struct userdata* u = arg; -     -    assert(u); -     -    while (!u->quit_requested) { -        if (u->ringbuffer_is_full) { -            pthread_mutex_lock(&u->cond_mutex); -            pthread_cond_wait(&u->ringbuffer_cond, -                              &u->cond_mutex); -            pthread_mutex_unlock(&u->cond_mutex); -        } -        /* No, it's not full yet, but this must be set to one as soon as -           possible, because if the jack thread manages to process another -           block before we set this to one, we may end up waiting without -           a reason. */ -        u->ringbuffer_is_full = 1; +    free(ports); +    pa_modargs_free(ma); -        request_render(u); -    } -     -    return NULL; -} +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    free(ports); +    pa__done(m); -static void request_render(struct userdata* u) { -    char c = 'x'; -     -    assert(u); -     -    assert(u->pipe_fds[1] >= 0); -    pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type); +    return -1;  } -void pa__done(pa_core* c, pa_module* self) { -    struct userdata* u; -     -    assert(c); -    assert(self); +void pa__done(pa_module*m) { +    struct userdata *u; + +    pa_assert(m); -    if (!self->userdata) +    if (!(u = m->userdata))          return; -    u = self->userdata; -     -    if (u->filler_thread_is_running) { -        u->quit_requested = 1; -        pthread_cond_signal(&u->ringbuffer_cond); -        pthread_join(u->filler_thread, NULL); +    if (u->client) +        jack_client_close(u->client); + +    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->j_client) -        jack_client_close(u->j_client); -    if (u->io_event) -        c->mainloop->io_free(u->io_event); +    pa_thread_mq_done(&u->thread_mq); -    if (u->sink) { -        pa_sink_disconnect(u->sink); +    if (u->sink)          pa_sink_unref(u->sink); -    } -     -    if (u->ringbuffer) -        jack_ringbuffer_free(u->ringbuffer); - -    if (u->pipe_fds[0] >= 0) -        pa_close(u->pipe_fds[0]); -    if (u->pipe_fds[1] >= 0) -        pa_close(u->pipe_fds[1]); -     -    pthread_mutex_destroy(&u->buffersize_mutex); -    pthread_cond_destroy(&u->ringbuffer_cond); -    pthread_mutex_destroy(&u->cond_mutex); -    pa_xfree(self->userdata); -    self->userdata = NULL; + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->jack_msgq) +        pa_asyncmsgq_unref(u->jack_msgq); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    pa_xfree(u);  } diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index 8ca24035..b62ebe7a 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -28,31 +28,34 @@  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <limits.h> -#include <pthread.h>  #include <jack/jack.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-error.h> -#include <pulsecore/iochannel.h>  #include <pulsecore/source.h>  #include <pulsecore/module.h>  #include <pulsecore/core-util.h>  #include <pulsecore/modargs.h>  #include <pulsecore/log.h> -#include <pulse/mainloop-api.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/sample-util.h>  #include "module-jack-source-symdef.h" +/* See module-jack-sink for a few comments how this module basically + * works */ +  PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("Jack Source") +PA_MODULE_DESCRIPTION("JACK Source")  PA_MODULE_VERSION(PACKAGE_VERSION)  PA_MODULE_USAGE(          "source_name=<name of source> " @@ -67,7 +70,6 @@ PA_MODULE_USAGE(  struct userdata {      pa_core *core;      pa_module *module; -      pa_source *source;      unsigned channels; @@ -75,19 +77,15 @@ struct userdata {      jack_port_t* port[PA_CHANNELS_MAX];      jack_client_t *client; -    pthread_mutex_t mutex; -    pthread_cond_t cond; +    pa_thread_mq thread_mq; +    pa_asyncmsgq *jack_msgq; +    pa_rtpoll *rtpoll; +    pa_rtpoll_item *rtpoll_item; -    void * buffer[PA_CHANNELS_MAX]; -    jack_nframes_t frames_posted; -    int quit_requested; +    pa_thread *thread; -    int pipe_fds[2]; -    int pipe_fd_type; -    pa_io_event *io_event; - -    jack_nframes_t frames_in_buffer; -    jack_nframes_t timestamp; +    jack_nframes_t saved_frame_time; +    pa_bool_t saved_frame_time_valid;  };  static const char* const valid_modargs[] = { @@ -100,141 +98,150 @@ static const char* const valid_modargs[] = {      NULL  }; -static void stop_source(struct userdata *u) { -    assert (u); - -    jack_client_close(u->client); -    u->client = NULL; -    u->core->mainloop->io_free(u->io_event); -    u->io_event = NULL; -    pa_source_disconnect(u->source); -    pa_source_unref(u->source); -    u->source = NULL; -    pa_module_unload_request(u->module); -} +enum { +    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX, +    SOURCE_MESSAGE_ON_SHUTDOWN +}; -static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { -    struct userdata *u = userdata; -    char x; +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SOURCE(o)->userdata; -    assert(m); -    assert(flags == PA_IO_EVENT_INPUT); -    assert(u); -    assert(u->pipe_fds[0] == fd); +    switch (code) { -    pa_read(fd, &x, 1, &u->pipe_fd_type); +        case SOURCE_MESSAGE_POST: -    if (u->quit_requested) { -        stop_source(u); -        u->quit_requested = 0; -        return; -    } +            /* Handle the new block from the JACK thread */ +            pa_assert(chunk); +            pa_assert(chunk->length > 0); -    pthread_mutex_lock(&u->mutex); +            if (u->source->thread_info.state == PA_SOURCE_RUNNING) +                pa_source_post(u->source, chunk); -    if (u->frames_posted > 0) { -        unsigned fs; -        jack_nframes_t frame_idx; -        pa_memchunk chunk; +            u->saved_frame_time = offset; +            u->saved_frame_time_valid = TRUE; -        fs = pa_frame_size(&u->source->sample_spec); +            return 0; -        chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs); -        chunk.index = 0; +        case SOURCE_MESSAGE_ON_SHUTDOWN: +            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +            return 0; -        for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) { -            unsigned c; +        case PA_SOURCE_MESSAGE_GET_LATENCY: { +            jack_nframes_t l, ft, d; +            size_t n; -            for (c = 0; c < u->channels; c++) { -                float *s = ((float*) u->buffer[c]) + frame_idx; -                float *d = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c; +            /* This is the "worst-case" latency */ +            l = jack_port_get_total_latency(u->client, u->port[0]); -                *d = *s; -            } -        } +            if (u->saved_frame_time_valid) { +                /* Adjust the worst case latency by the time that +                 * passed since we last handed data to JACK */ -        pa_source_post(u->source, &chunk); -        pa_memblock_unref(chunk.memblock); +                ft = jack_frame_time(u->client); +                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; +                l += d; +            } -        u->frames_posted = 0; +            /* Convert it to usec */ +            n = l * pa_frame_size(&u->source->sample_spec); +            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec); -        pthread_cond_signal(&u->cond); +            return 0; +        }      } -    pthread_mutex_unlock(&u->mutex); -} - -static void request_post(struct userdata *u) { -    char c = 'x'; -    assert(u); - -    assert(u->pipe_fds[1] >= 0); -    pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type); -} - -static void jack_shutdown(void *arg) { -    struct userdata *u = arg; -    assert(u); - -    u->quit_requested = 1; -    request_post(u); +    return pa_source_process_msg(o, code, data, offset, chunk);  }  static int jack_process(jack_nframes_t nframes, void *arg) { +    unsigned c;      struct userdata *u = arg; -    assert(u); - -    if (jack_transport_query(u->client, NULL) == JackTransportRolling) { -        unsigned c; +    const void *buffer[PA_CHANNELS_MAX]; +    void *p; +    jack_nframes_t frame_time; +    pa_memchunk chunk; -        pthread_mutex_lock(&u->mutex); +    pa_assert(u); -        u->frames_posted = nframes; +    for (c = 0; c < u->channels; c++) +        pa_assert(buffer[c] = jack_port_get_buffer(u->port[c], nframes)); -        for (c = 0; c < u->channels; c++) { -            u->buffer[c] = jack_port_get_buffer(u->port[c], nframes); -            assert(u->buffer[c]); -        } +    /* We interleave the data and pass it on to the other RT thread */ -        request_post(u); +    pa_memchunk_reset(&chunk); +    chunk.length = nframes * pa_frame_size(&u->source->sample_spec); +    chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); +    p = pa_memblock_acquire(chunk.memblock); +    pa_interleave(buffer, u->channels, p, sizeof(float), nframes); +    pa_memblock_release(chunk.memblock); -        pthread_cond_wait(&u->cond, &u->mutex); +    frame_time = jack_frame_time(u->client); -        u->frames_in_buffer = nframes; -        u->timestamp = jack_get_current_transport_frame(u->client); +    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL); -        pthread_mutex_unlock(&u->mutex); -    } +    pa_memblock_unref(chunk.memblock);      return 0;  } -static pa_usec_t source_get_latency_cb(pa_source *s) { -    struct userdata *u; -    jack_nframes_t n, l, d; +static void thread_func(void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(u); -    assert(s); -    u = s->userdata; +    pa_log_debug("Thread starting up"); -    if (jack_transport_query(u->client, NULL) != JackTransportRolling) -        return 0; +    if (u->core->high_priority) +        pa_make_realtime(); -    n = jack_get_current_transport_frame(u->client); +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); -    if (n < u->timestamp) -        return 0; +    for (;;) { +        int ret; -    d = n - u->timestamp; -    l = jack_port_get_total_latency(u->client, u->port[0]); +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; +    } -    return pa_bytes_to_usec((l + d) * pa_frame_size(&s->sample_spec), &s->sample_spec); +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: +    pa_log_debug("Thread shutting down");  }  static void jack_error_func(const char*t) { -    pa_log_warn("JACK error >%s<", t); +    char *s; + +    s = pa_xstrndup(t, strcspn(t, "\n\r")); +    pa_log_warn("JACK error >%s<", s); +    pa_xfree(s); +} + +static void jack_init(void *arg) { +    struct userdata *u = arg; + +    pa_log_info("JACK thread starting up."); + +    if (u->core->high_priority) +        pa_make_realtime();  } -int pa__init(pa_core *c, pa_module*m) { +static void jack_shutdown(void* arg) { +    struct userdata *u = arg; + +    pa_log_info("JACK thread shutting down.."); +    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); +} + +int pa__init(pa_module*m) {      struct userdata *u = NULL;      pa_sample_spec ss;      pa_channel_map map; @@ -247,40 +254,35 @@ int pa__init(pa_core *c, pa_module*m) {      const char **ports = NULL, **p;      char *t; -    assert(c); -    assert(m); +    pa_assert(m);      jack_set_error_function(jack_error_func);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments."); +        pa_log("Failed to parse module arguments.");          goto fail;      }      if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { -        pa_log("failed to parse connect= argument."); +        pa_log("Failed to parse connect= argument.");          goto fail;      }      server_name = pa_modargs_get_value(ma, "server_name", NULL); -    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio"); +    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source");      u = pa_xnew0(struct userdata, 1); -    m->userdata = u; -    u->core = c; +    u->core = m->core;      u->module = m; -    u->pipe_fds[0] = u->pipe_fds[1] = -1; -    u->pipe_fd_type = 0; - -    pthread_mutex_init(&u->mutex, NULL); -    pthread_cond_init(&u->cond, NULL); +    m->userdata = u; +    u->saved_frame_time_valid = FALSE; -    if (pipe(u->pipe_fds) < 0) { -        pa_log("pipe() failed: %s", pa_cstrerror(errno)); -        goto fail; -    } +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); -    pa_make_nonblock_fd(u->pipe_fds[1]); +    u->jack_msgq = pa_asyncmsgq_new(0); +    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);      if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {          pa_log("jack_client_open() failed."); @@ -294,7 +296,7 @@ int pa__init(pa_core *c, pa_module*m) {          channels++;      if (!channels) -        channels = c->default_sample_spec.channels; +        channels = m->core->default_sample_spec.channels;      if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {          pa_log("failed to parse channels= argument."); @@ -302,7 +304,7 @@ int pa__init(pa_core *c, pa_module*m) {      }      pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA); -    if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) { +    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {          pa_log("failed to parse channel_map= argument.");          goto fail;      } @@ -313,7 +315,7 @@ int pa__init(pa_core *c, pa_module*m) {      ss.rate = jack_get_sample_rate(u->client);      ss.format = PA_SAMPLE_FLOAT32NE; -    assert(pa_sample_spec_valid(&ss)); +    pa_assert(pa_sample_spec_valid(&ss));      for (i = 0; i < ss.channels; i++) {          if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) { @@ -322,19 +324,29 @@ int pa__init(pa_core *c, pa_module*m) {          }      } -    if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) { +    if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {          pa_log("failed to create source.");          goto fail;      } +    u->source->parent.process_msg = source_process_msg;      u->source->userdata = u; -    pa_source_set_owner(u->source, m); +    u->source->flags = PA_SOURCE_LATENCY; + +    pa_source_set_module(u->source, m); +    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +    pa_source_set_rtpoll(u->source, u->rtpoll);      pa_source_set_description(u->source, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client)));      pa_xfree(t); -    u->source->get_latency = source_get_latency_cb;      jack_set_process_callback(u->client, jack_process, u);      jack_on_shutdown(u->client, jack_shutdown, u); +    jack_set_thread_init_callback(u->client, jack_init, u); + +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    }      if (jack_activate(u->client)) {          pa_log("jack_activate() failed"); @@ -359,7 +371,7 @@ int pa__init(pa_core *c, pa_module*m) {      } -    u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], PA_IO_EVENT_INPUT, io_event_cb, u); +    pa_source_put(u->source);      free(ports);      pa_modargs_free(ma); @@ -372,14 +384,14 @@ fail:      free(ports); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); +    pa_assert(m);      if (!(u = m->userdata))          return; @@ -387,20 +399,27 @@ void pa__done(pa_core *c, pa_module*m) {      if (u->client)          jack_client_close(u->client); -    if (u->io_event) -        c->mainloop->io_free(u->io_event); +    if (u->source) +        pa_source_unlink(u->source); -    if (u->source) { -        pa_source_disconnect(u->source); -        pa_source_unref(u->source); +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread);      } -    if (u->pipe_fds[0] >= 0) -        close(u->pipe_fds[0]); -    if (u->pipe_fds[1] >= 0) -        close(u->pipe_fds[1]); +    pa_thread_mq_done(&u->thread_mq); + +    if (u->source) +        pa_source_unref(u->source); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->jack_msgq) +        pa_asyncmsgq_unref(u->jack_msgq); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); -    pthread_mutex_destroy(&u->mutex); -    pthread_cond_destroy(&u->cond);      pa_xfree(u);  } diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c new file mode 100644 index 00000000..0265d971 --- /dev/null +++ b/src/modules/module-ladspa-sink.c @@ -0,0 +1,673 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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. +***/ + +/* TODO: Some plugins cause latency, and some even report it by using a control +   out port. We don't currently use the latency information. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/namereg.h> +#include <pulsecore/sink.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/sample-util.h> + +#include "module-ladspa-sink-symdef.h" +#include "ladspa.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Virtual LADSPA sink") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE( +        "sink_name=<name for the sink> " +        "master=<name of sink to remap> " +        "format=<sample format> " +        "channels=<number of channels> " +        "rate=<sample rate> " +        "channel_map=<channel map> " +        "plugin=<ladspa plugin name> " +        "label=<ladspa plugin label> " +        "control=<comma seperated list of input control values>") + +struct userdata { +    pa_core *core; +    pa_module *module; + +    pa_sink *sink, *master; +    pa_sink_input *sink_input; + +    const LADSPA_Descriptor *descriptor; +    unsigned channels; +    LADSPA_Handle handle[PA_CHANNELS_MAX]; +    LADSPA_Data *input, *output; +    size_t block_size; +    unsigned long input_port, output_port; +    LADSPA_Data *control; + +    /* This is a dummy buffer. Every port must be connected, but we don't care +       about control out ports. We connect them all to this single buffer. */ +    LADSPA_Data control_out; + +    pa_memchunk memchunk; +}; + +static const char* const valid_modargs[] = { +    "sink_name", +    "master", +    "format", +    "channels", +    "rate", +    "channel_map", +    "plugin", +    "label", +    "control", +    NULL +}; + +/* Called from I/O thread context */ +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_GET_LATENCY: { +            pa_usec_t usec = 0; + +            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) +                usec = 0; + +            *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); +            return 0; +        } +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk); +} + +/* Called from main context */ +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +    struct userdata *u; + +    pa_sink_assert_ref(s); +    pa_assert_se(u = s->userdata); + +    if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input))) +        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + +    return 0; +} + +/* Called from I/O thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SINK_INPUT(o)->userdata; + +    switch (code) { +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: +            *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +    } + +    return pa_sink_input_process_msg(o, code, data, offset, chunk); +} + +/* Called from I/O thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    if (!u->memchunk.memblock) { +        pa_memchunk tchunk; +        float *src, *dst; +        size_t fs; +        unsigned n, c; + +        pa_sink_render(u->sink, length, &tchunk); + +        fs = pa_frame_size(&i->sample_spec); +        n = tchunk.length / fs; + +        pa_assert(n > 0); + +        u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length); +        u->memchunk.index = 0; +        u->memchunk.length = tchunk.length; + +        src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); +        dst = (float*) pa_memblock_acquire(u->memchunk.memblock); + +        for (c = 0; c < u->channels; c++) { +            unsigned j; +            float *p, *q; + +            p = src + c; +            q = u->input; +            for (j = 0; j < n; j++, p += u->channels, q++) +                *q = CLAMP(*p, -1.0, 1.0); + +            u->descriptor->run(u->handle[c], n); + +            q = u->output; +            p = dst + c; +            for (j = 0; j < n; j++, q++, p += u->channels) +                *p = CLAMP(*q, -1.0, 1.0); +        } + +        pa_memblock_release(tchunk.memblock); +        pa_memblock_release(u->memchunk.memblock); + +        pa_memblock_unref(tchunk.memblock); +    } + +    pa_assert(u->memchunk.length > 0); +    pa_assert(u->memchunk.memblock); + +    *chunk = u->memchunk; +    pa_memblock_ref(chunk->memblock); + +    return 0; +} + +/* Called from I/O thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); +    pa_assert(length > 0); + +    if (u->memchunk.memblock) { + +        if (length < u->memchunk.length) { +            u->memchunk.index += length; +            u->memchunk.length -= length; +            return; +        } + +        pa_memblock_unref(u->memchunk.memblock); +        length -= u->memchunk.length; +        pa_memchunk_reset(&u->memchunk); +    } + +    if (length > 0) +        pa_sink_skip(u->sink, length); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_detach_within_thread(u->sink); +} + +/* Called from I/O thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); +    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + +    pa_sink_attach_within_thread(u->sink); +} + +/* Called from main context */ +static void sink_input_kill_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_input_unlink(u->sink_input); +    pa_sink_input_unref(u->sink_input); +    u->sink_input = NULL; + +    pa_sink_unlink(u->sink); +    pa_sink_unref(u->sink); +    u->sink = NULL; + +    pa_module_unload_request(u->module); +} + +int pa__init(pa_module*m) { +    struct userdata *u; +    pa_sample_spec ss; +    pa_channel_map map; +    pa_modargs *ma; +    char *t; +    pa_sink *master; +    pa_sink_input_new_data data; +    const char *plugin, *label; +    LADSPA_Descriptor_Function descriptor_func; +    const char *e, *cdata; +    const LADSPA_Descriptor *d; +    unsigned long input_port, output_port, p, j, n_control; +    unsigned c; +    pa_bool_t *use_default = NULL; +    char *default_sink_name = NULL; + +    pa_assert(m); + +    pa_assert(sizeof(LADSPA_Data) == sizeof(float)); + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments."); +        goto fail; +    } + +    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) { +        pa_log("Master sink not found"); +        goto fail; +    } + +    ss = master->sample_spec; +    ss.format = PA_SAMPLE_FLOAT32; +    map = master->channel_map; +    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { +        pa_log("Invalid sample format specification or channel map"); +        goto fail; +    } + +    if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) { +        pa_log("Missing LADSPA plugin name"); +        goto fail; +    } + +    if (!(label = pa_modargs_get_value(ma, "label", NULL))) { +        pa_log("Missing LADSPA plugin label"); +        goto fail; +    } + +    cdata = pa_modargs_get_value(ma, "control", NULL); + +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    u->master = master; +    pa_memchunk_reset(&u->memchunk); + +    if (!(e = getenv("LADSPA_PATH"))) +        e = LADSPA_PATH; + +    /* FIXME: This is not exactly thread safe */ +    t = pa_xstrdup(lt_dlgetsearchpath()); +    lt_dlsetsearchpath(e); +    m->dl = lt_dlopenext(plugin); +    lt_dlsetsearchpath(t); +    pa_xfree(t); + +    if (!m->dl) { +        pa_log("Failed to load LADSPA plugin: %s", lt_dlerror()); +        goto fail; +    } + +    if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) { +        pa_log("LADSPA module lacks ladspa_descriptor() symbol."); +        goto fail; +    } + +    for (j = 0;; j++) { + +        if (!(d = descriptor_func(j))) { +            pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label); +            goto fail; +        } + +        if (strcmp(d->Label, label) == 0) +            break; +    } + +    u->descriptor = d; + +    pa_log_debug("Module: %s", plugin); +    pa_log_debug("Label: %s", d->Label); +    pa_log_debug("Unique ID: %lu", d->UniqueID); +    pa_log_debug("Name: %s", d->Name); +    pa_log_debug("Maker: %s", d->Maker); +    pa_log_debug("Copyright: %s", d->Copyright); + +    input_port = output_port = (unsigned long) -1; +    n_control = 0; + +    for (p = 0; p < d->PortCount; p++) { + +        if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) { + +            if (strcmp(d->PortNames[p], "Input") == 0) { +                pa_assert(input_port == (unsigned long) -1); +                input_port = p; +            } else { +                pa_log("Found audio input port on plugin we cannot handle: %s", d->PortNames[p]); +                goto fail; +            } + +        } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) { + +            if (strcmp(d->PortNames[p], "Output") == 0) { +                pa_assert(output_port == (unsigned long) -1); +                output_port = p; +            } else { +                pa_log("Found audio output port on plugin we cannot handle: %s", d->PortNames[p]); +                goto fail; +            } + +        } else if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])) +            n_control++; +        else { +            pa_assert(LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])); +            pa_log_info("Ignored port \"%s\", because we ignore all control out ports.", d->PortNames[p]); +        } +    } + +    if ((input_port == (unsigned long) -1) || (output_port == (unsigned long) -1)) { +        pa_log("Failed to identify input and output ports. " +               "Right now this module can only deal with plugins which provide an 'Input' and an 'Output' audio port. " +               "Patches welcome!"); +        goto fail; +    } + +    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); +    if (LADSPA_IS_INPLACE_BROKEN(d->Properties)) +        u->output = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size); +    else +        u->output = u->input; + +    u->channels = ss.channels; + +    for (c = 0; c < ss.channels; c++) { +        if (!(u->handle[c] = d->instantiate(d, ss.rate))) { +            pa_log("Failed to instantiate plugin %s with label %s for channel %i", plugin, d->Label, c); +            goto fail; +        } + +        d->connect_port(u->handle[c], input_port, u->input); +        d->connect_port(u->handle[c], output_port, u->output); +    } + +    if (!cdata && n_control > 0) { +        pa_log("This plugin requires specification of %lu control parameters.", n_control); +        goto fail; +    } + +    if (n_control > 0) { +        const char *state = NULL; +        char *k; +        unsigned long h; + +        u->control = pa_xnew(LADSPA_Data, n_control); +        use_default = pa_xnew(pa_bool_t, n_control); +        p = 0; + +        while ((k = pa_split(cdata, ",", &state))) { +            float f; + +            if (*k == 0) { +                use_default[p++] = TRUE; +                pa_xfree(k); +                continue; +            } + +            if (pa_atof(k, &f) < 0) { +                pa_log("Failed to parse control value '%s'", k); +                pa_xfree(k); +                goto fail; +            } + +            pa_xfree(k); + +            if (p >= n_control) { +                pa_log("Too many control values passed, %lu expected.", n_control); +                goto fail; +            } + +            use_default[p] = FALSE; +            u->control[p++] = f; +        } + +        if (p < n_control) { +            pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p); +            goto fail; +        } + +        h = 0; +        for (p = 0; p < d->PortCount; p++) { +            LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor; + +            if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])) +                continue; + +            if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) { +                for (c = 0; c < ss.channels; c++) +                    d->connect_port(u->handle[c], p, &u->control_out); +                continue; +            } + +            pa_assert(h < n_control); + +            if (use_default[h]) { +                LADSPA_Data lower, upper; + +                if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) { +                    pa_log("Control port value left empty but plugin defines no default."); +                    goto fail; +                } + +                lower = d->PortRangeHints[p].LowerBound; +                upper = d->PortRangeHints[p].UpperBound; + +                if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) { +                    lower *= ss.rate; +                    upper *= ss.rate; +                } + +                switch (hint & LADSPA_HINT_DEFAULT_MASK) { + +                    case LADSPA_HINT_DEFAULT_MINIMUM: +                        u->control[h] = lower; +                        break; + +                    case LADSPA_HINT_DEFAULT_MAXIMUM: +                        u->control[h] = upper; +                        break; + +                    case LADSPA_HINT_DEFAULT_LOW: +                        if (LADSPA_IS_HINT_LOGARITHMIC(hint)) +                            u->control[h] = exp(log(lower) * 0.75 + log(upper) * 0.25); +                        else +                            u->control[h] = 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); +                        else +                            u->control[h] = 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); +                        else +                            u->control[h] = lower * 0.25 + upper * 0.75; +                        break; + +                    case LADSPA_HINT_DEFAULT_0: +                        u->control[h] = 0; +                        break; + +                    case LADSPA_HINT_DEFAULT_1: +                        u->control[h] = 1; +                        break; + +                    case LADSPA_HINT_DEFAULT_100: +                        u->control[h] = 100; +                        break; + +                    case LADSPA_HINT_DEFAULT_440: +                        u->control[h] = 440; +                        break; + +                    default: +                        pa_assert_not_reached(); +                } +            } + +            if (LADSPA_IS_HINT_INTEGER(hint)) +                u->control[h] = roundf(u->control[h]); + +            pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]); + +            for (c = 0; c < ss.channels; c++) +                d->connect_port(u->handle[c], p, &u->control[h]); + +            h++; +        } + +        pa_assert(h == n_control); +    } + +    if (d->activate) +        for (c = 0; c < u->channels; c++) +            d->activate(u->handle[c]); + +    default_sink_name = pa_sprintf_malloc("%s.ladspa", master->name); + +    /* Create sink */ +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &map))) { +        pa_log("Failed to create sink."); +        goto fail; +    } + +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->set_state = sink_set_state; +    u->sink->userdata = u; +    u->sink->flags = PA_SINK_LATENCY; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("LADSPA plugin '%s' on '%s'", label, master->description)); +    pa_xfree(t); +    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); +    pa_sink_set_rtpoll(u->sink, master->rtpoll); + +    /* Create sink input */ +    pa_sink_input_new_data_init(&data); +    data.sink = u->master; +    data.driver = __FILE__; +    data.name = "LADSPA Stream"; +    pa_sink_input_new_data_set_sample_spec(&data, &ss); +    pa_sink_input_new_data_set_channel_map(&data, &map); +    data.module = m; + +    if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE))) +        goto fail; + +    u->sink_input->parent.process_msg = sink_input_process_msg; +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb; +    u->sink_input->attach = sink_input_attach_cb; +    u->sink_input->detach = sink_input_detach_cb; +    u->sink_input->userdata = u; + +    pa_sink_put(u->sink); +    pa_sink_input_put(u->sink_input); + +    pa_modargs_free(ma); + +    pa_xfree(use_default); +    pa_xfree(default_sink_name); + +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    pa_xfree(use_default); +    pa_xfree(default_sink_name); + +    pa__done(m); + +    return -1; +} + +void pa__done(pa_module*m) { +    struct userdata *u; +    unsigned c; + +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->sink_input) { +        pa_sink_input_unlink(u->sink_input); +        pa_sink_input_unref(u->sink_input); +    } + +    if (u->sink) { +        pa_sink_unlink(u->sink); +        pa_sink_unref(u->sink); +    } + +    if (u->memchunk.memblock) +        pa_memblock_unref(u->memchunk.memblock); + +    for (c = 0; c < u->channels; c++) +        if (u->handle[c]) { +            if (u->descriptor->deactivate) +                u->descriptor->deactivate(u->handle[c]); +            u->descriptor->cleanup(u->handle[c]); +        } + +    if (u->output != u->input) +        pa_xfree(u->output); + +    pa_xfree(u->input); + +    pa_xfree(u->control); + +    pa_xfree(u); +} diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index c8adbc8b..21d93837 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -26,12 +26,12 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <string.h> -#include <lirc/lirc_client.h>  #include <stdlib.h> +#include <lirc/lirc_client.h> +  #include <pulse/xmalloc.h>  #include <pulsecore/module.h> @@ -39,6 +39,7 @@  #include <pulsecore/namereg.h>  #include <pulsecore/sink.h>  #include <pulsecore/modargs.h> +#include <pulsecore/macro.h>  #include "module-lirc-symdef.h" @@ -68,11 +69,12 @@ 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) {      struct userdata *u = userdata;      char *name = NULL, *code = NULL; -    assert(io); -    assert(u); + +    pa_assert(io); +    pa_assert(u);      if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { -        pa_log("lost connection to LIRC daemon."); +        pa_log("Lost connection to LIRC daemon.");          goto fail;      } @@ -86,7 +88,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC          c = pa_xstrdup(code);          c[strcspn(c, "\n\r")] = 0; -        pa_log_debug("raw IR code '%s'", c); +        pa_log_debug("Raw IR code '%s'", c);          pa_xfree(c);          while (lirc_code2char(u->config, code, &name) == 0 && name) { @@ -99,7 +101,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                  MUTE_TOGGLE              } volchange = INVALID; -            pa_log_info("translated IR code '%s'", name); +            pa_log_info("Translated IR code '%s'", name);              if (strcasecmp(name, "volume-up") == 0)                  volchange = UP; @@ -113,15 +115,15 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                  volchange = RESET;              if (volchange == INVALID) -                pa_log_warn("recieved unknown IR code '%s'", name); +                pa_log_warn("Recieved unknown IR code '%s'", name);              else {                  pa_sink *s;                  if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) -                    pa_log("failed to get sink '%s'", u->sink_name); +                    pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE); +                    pa_cvolume cv = *pa_sink_get_volume(s);  #define DELTA (PA_VOLUME_NORM/20) @@ -134,7 +136,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                                      cv.values[i] = PA_VOLUME_NORM;                              } -                            pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); +                            pa_sink_set_volume(s, &cv);                              break;                          case DOWN: @@ -145,20 +147,20 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                                      cv.values[i] = PA_VOLUME_MUTED;                              } -                            pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); +                            pa_sink_set_volume(s, &cv);                              break;                          case MUTE: -                            pa_sink_set_mute(s, PA_MIXER_HARDWARE, 0); +                            pa_sink_set_mute(s, 0);                              break;                          case RESET: -                            pa_sink_set_mute(s, PA_MIXER_HARDWARE, 1); +                            pa_sink_set_mute(s, 1);                              break;                          case MUTE_TOGGLE: -                            pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE)); +                            pa_sink_set_mute(s, !pa_sink_get_mute(s));                              break;                          case INVALID: @@ -179,13 +181,14 @@ fail:      pa_module_unload_request(u->module); -    free(code); +    pa_xfree(code);  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (lirc_in_use) {          pa_log("module-lirc may no be loaded twice."); @@ -197,7 +200,7 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    m->userdata = u = pa_xmalloc(sizeof(struct userdata)); +    m->userdata = u = pa_xnew(struct userdata, 1);      u->module = m;      u->io = NULL;      u->config = NULL; @@ -215,7 +218,7 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); +    u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);      lirc_in_use = 1; @@ -228,14 +231,13 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(u = m->userdata))          return; diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 0b051fac..0155b2af 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -26,7 +26,6 @@  #endif  #include <unistd.h> -#include <assert.h>  #include <string.h>  #include <errno.h>  #include <sys/types.h> @@ -80,6 +79,8 @@ static int load_rules(struct userdata *u, const char *filename) {      struct rule *end = NULL;      char *fn = NULL; +    pa_assert(u); +      f = filename ?          fopen(fn = pa_xstrdup(filename), "r") :          pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r"); @@ -132,7 +133,7 @@ static int load_rules(struct userdata *u, const char *filename) {              goto finish;          } -        rule = pa_xmalloc(sizeof(struct rule)); +        rule = pa_xnew(struct rule, 1);          rule->regex = regex;          rule->volume = volume;          rule->next = NULL; @@ -164,7 +165,9 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v      struct userdata *u =  userdata;      pa_sink_input *si;      struct rule *r; -    assert(c && u); + +    pa_assert(c); +    pa_assert(u);      if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))          return; @@ -179,23 +182,24 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v          if (!regexec(&r->regex, si->name, 0, NULL, 0)) {              pa_cvolume cv;              pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume); -            pa_cvolume_set(&cv, r->volume, si->sample_spec.channels); +            pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);              pa_sink_input_set_volume(si, &cv);          }      }  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments");          goto fail;      } -    u = pa_xmalloc(sizeof(struct userdata)); +    u = pa_xnew(struct userdata, 1);      u->rules = NULL;      u->subscription = NULL;      m->userdata = u; @@ -203,23 +207,24 @@ int pa__init(pa_core *c, pa_module*m) {      if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)          goto fail; -    u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); +    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);      pa_modargs_free(ma);      return 0;  fail: -    pa__done(c, m); +    pa__done(m);      if (ma)          pa_modargs_free(ma);      return  -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata* u;      struct rule *r, *n; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index b7433ac8..47a10645 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <string.h>  #include <stdlib.h> @@ -80,11 +79,12 @@ struct userdata {  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) {      struct userdata *u = userdata; -    assert(io); -    assert(u); + +    pa_assert(io); +    pa_assert(u);      if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { -        pa_log("lost connection to evdev device."); +        pa_log("Lost connection to evdev device.");          goto fail;      } @@ -92,14 +92,14 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC          struct input_event ev;          if (pa_loop_read(u->fd, &ev, sizeof(ev), &u->fd_type) <= 0) { -            pa_log("failed to read from event device: %s", pa_cstrerror(errno)); +            pa_log("Failed to read from event device: %s", pa_cstrerror(errno));              goto fail;          }          if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {              enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID; -            pa_log_debug("key code=%u, value=%u", ev.code, ev.value); +            pa_log_debug("Key code=%u, value=%u", ev.code, ev.value);              switch (ev.code) {                  case KEY_VOLUMEDOWN:  volchange = DOWN; break; @@ -111,10 +111,10 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                  pa_sink *s;                  if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) -                    pa_log("failed to get sink '%s'", u->sink_name); +                    pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE); +                    pa_cvolume cv = *pa_sink_get_volume(s);  #define DELTA (PA_VOLUME_NORM/20) @@ -127,7 +127,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                                      cv.values[i] = PA_VOLUME_NORM;                              } -                            pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); +                            pa_sink_set_volume(s, &cv);                              break;                          case DOWN: @@ -138,12 +138,12 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                                      cv.values[i] = PA_VOLUME_MUTED;                              } -                            pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); +                            pa_sink_set_volume(s, &cv);                              break;                          case MUTE_TOGGLE: -                            pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE)); +                            pa_sink_set_mute(s, !pa_sink_get_mute(s));                              break;                          case INVALID: @@ -165,21 +165,23 @@ fail:  #define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) { +      pa_modargs *ma = NULL;      struct userdata *u;      int version;      struct _input_id input_id;      char name[256];      uint8_t evtype_bitmask[EV_MAX/8 + 1]; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments");          goto fail;      } -    m->userdata = u = pa_xmalloc(sizeof(struct userdata)); +    m->userdata = u = pa_xnew(struct userdata,1);      u->module = m;      u->io = NULL;      u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); @@ -221,11 +223,11 @@ int pa__init(pa_core *c, pa_module*m) {      }      if (!test_bit(EV_KEY, evtype_bitmask)) { -        pa_log("device has no keys."); +        pa_log("Device has no keys.");          goto fail;      } -    u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); +    u->io = m->core->mainloop->io_new(m->core->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);      pa_modargs_free(ma); @@ -236,14 +238,14 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); + +    pa_assert(m);      if (!(u = m->userdata))          return; @@ -252,7 +254,7 @@ void pa__done(pa_core *c, pa_module*m) {          m->core->mainloop->io_free(u->io);      if (u->fd >= 0) -        close(u->fd); +        pa_assert_se(pa_close(u->fd) == 0);      pa_xfree(u->sink_name);      pa_xfree(u); diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c index 3c1c2bca..2ef61c88 100644 --- a/src/modules/module-native-protocol-fd.c +++ b/src/modules/module-native-protocol-fd.c @@ -26,10 +26,10 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <pulsecore/module.h> +#include <pulsecore/macro.h>  #include <pulsecore/iochannel.h>  #include <pulsecore/modargs.h>  #include <pulsecore/protocol-native.h> @@ -48,25 +48,26 @@ static const char* const valid_modargs[] = {      NULL,  }; -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_iochannel *io;      pa_modargs *ma;      int fd, r = -1; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments."); +        pa_log("Failed to parse module arguments.");          goto finish;      }      if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) { -        pa_log("invalid file descriptor."); +        pa_log("Invalid file descriptor.");          goto finish;      } -    io = pa_iochannel_new(c->mainloop, fd, fd); +    io = pa_iochannel_new(m->core->mainloop, fd, fd); -    if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) { +    if (!(m->userdata = pa_protocol_native_new_iochannel(m->core, io, m, ma))) {          pa_iochannel_free(io);          goto finish;      } @@ -80,8 +81,8 @@ finish:      return r;  } -void pa__done(pa_core *c, pa_module*m) { -    assert(c && m); +void pa__done(pa_module*m) { +    pa_assert(m);      pa_protocol_native_free(m->userdata);  } diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 54a8e890..3a4edec7 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -28,7 +28,6 @@  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <fcntl.h> @@ -38,12 +37,17 @@  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> -#include <pulsecore/iochannel.h> +#include <pulsecore/macro.h>  #include <pulsecore/sink.h>  #include <pulsecore/module.h>  #include <pulsecore/core-util.h> +#include <pulsecore/core-error.h>  #include <pulsecore/modargs.h>  #include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/rtclock.h>  #include "module-null-sink-symdef.h" @@ -64,11 +68,14 @@ struct userdata {      pa_core *core;      pa_module *module;      pa_sink *sink; -    pa_time_event *time_event; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +      size_t block_size; -    uint64_t n_bytes; -    struct timeval start_time; +    struct timeval timestamp;  };  static const char* const valid_modargs[] = { @@ -81,78 +88,132 @@ static const char* const valid_modargs[] = {      NULL  }; -static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { -    struct userdata *u = userdata; -    pa_memchunk chunk; -    struct timeval ntv = *tv; -    size_t l; +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: -    assert(u); +            if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) +                pa_rtclock_get(&u->timestamp); -    if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) { -        l = chunk.length; -        pa_memblock_unref(chunk.memblock); -    } else -        l = u->block_size; +            break; -    pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec)); -    m->time_restart(e, &ntv); +        case PA_SINK_MESSAGE_GET_LATENCY: { +            struct timeval now; -    u->n_bytes += l; +            pa_rtclock_get(&now); + +            if (pa_timeval_cmp(&u->timestamp, &now) > 0) +                *((pa_usec_t*) data) = 0; +            else +                *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now); +            break; +        } +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk);  } -static pa_usec_t get_latency(pa_sink *s) { -    struct userdata *u = s->userdata; -    pa_usec_t a, b; -    struct timeval now; +static void thread_func(void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    pa_rtclock_get(&u->timestamp); + +    for (;;) { +        int ret; + +        /* Render some data and drop it immediately */ +        if (u->sink->thread_info.state == PA_SINK_RUNNING) { +            struct timeval now; + +            pa_rtclock_get(&now); -    a = pa_timeval_diff(pa_gettimeofday(&now), &u->start_time); -    b = pa_bytes_to_usec(u->n_bytes, &s->sample_spec); +            if (pa_timeval_cmp(&u->timestamp, &now) <= 0) { +                pa_sink_skip(u->sink, u->block_size); +                pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec)); +            } -    return b > a ? b - a : 0; +            pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp); +        } else +            pa_rtpoll_set_timer_disabled(u->rtpoll); + +        /* Hmm, nothing to do. Let's sleep */ +        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; +    } + +fail: +    /* If this was no regular exit from the loop we have to continue +     * processing messages until we received PA_MESSAGE_SHUTDOWN */ +    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: +    pa_log_debug("Thread shutting down");  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u = NULL;      pa_sample_spec ss;      pa_channel_map map;      pa_modargs *ma = NULL; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments."); +        pa_log("Failed to parse module arguments.");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { -        pa_log("invalid sample format specification or channel map."); +        pa_log("Invalid sample format specification or channel map");          goto fail;      }      u = pa_xnew0(struct userdata, 1); -    u->core = c; +    u->core = m->core;      u->module = m;      m->userdata = u; +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); -    if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { -        pa_log("failed to create sink."); +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { +        pa_log("Failed to create sink.");          goto fail;      } -    u->sink->get_latency = get_latency; +    u->sink->parent.process_msg = sink_process_msg;      u->sink->userdata = u; -    pa_sink_set_owner(u->sink, m); +    u->sink->flags = PA_SINK_LATENCY; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll);      pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink")); -    u->n_bytes = 0; -    pa_gettimeofday(&u->start_time); +    u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ +    if (u->block_size <= 0) +        u->block_size = pa_frame_size(&ss); -    u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u); +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    } -    u->block_size = pa_bytes_per_second(&ss) / 10; +    pa_sink_put(u->sink);      pa_modargs_free(ma); @@ -162,22 +223,34 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; -    pa_sink_disconnect(u->sink); -    pa_sink_unref(u->sink); +    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); -    u->core->mainloop->time_free(u->time_event); +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll);      pa_xfree(u);  } diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c deleted file mode 100644 index 16c9b311..00000000 --- a/src/modules/module-oss-mmap.c +++ /dev/null @@ -1,637 +0,0 @@ -/* $Id$ */ - -/*** -  This file is part of PulseAudio. - -  Copyright 2004-2006 Lennart Poettering -  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB - -  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/soundcard.h> -#include <sys/ioctl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <stdio.h> -#include <assert.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <limits.h> - -#ifdef HAVE_SYS_MMAN_H -#include <sys/mman.h> -#endif - -#include <pulse/xmalloc.h> -#include <pulse/util.h> - -#include <pulsecore/core-error.h> -#include <pulsecore/iochannel.h> -#include <pulsecore/sink.h> -#include <pulsecore/source.h> -#include <pulsecore/module.h> -#include <pulsecore/sample-util.h> -#include <pulsecore/core-util.h> -#include <pulsecore/modargs.h> -#include <pulsecore/log.h> - -#include "oss-util.h" -#include "module-oss-mmap-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("OSS Sink/Source (mmap)") -PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE( -        "sink_name=<name for the sink> " -        "source_name=<name for the source> " -        "device=<OSS device> " -        "record=<enable source?> " -        "playback=<enable sink?> " -        "format=<sample format> " -        "channels=<number of channels> " -        "rate=<sample rate> " -        "fragments=<number of fragments> " -        "fragment_size=<fragment size> " -        "channel_map=<channel map>") - -struct userdata { -    pa_sink *sink; -    pa_source *source; -    pa_core *core; -    pa_sample_spec sample_spec; - -    size_t in_fragment_size, out_fragment_size; -    unsigned in_fragments, out_fragments; -    unsigned out_blocks_saved, in_blocks_saved; - -    int fd; - -    void *in_mmap, *out_mmap; -    size_t in_mmap_length, out_mmap_length; - -    pa_io_event *io_event; - -    pa_memblock **in_memblocks, **out_memblocks; -    unsigned out_current, in_current; -    pa_module *module; -}; - -static const char* const valid_modargs[] = { -    "sink_name", -    "source_name", -    "device", -    "record", -    "playback", -    "fragments", -    "fragment_size", -    "format", -    "rate", -    "channels", -    "channel_map", -    NULL -}; - -#define DEFAULT_DEVICE "/dev/dsp" -#define DEFAULT_NFRAGS 12 -#define DEFAULT_FRAGSIZE 1024 - -static void update_usage(struct userdata *u) { -   pa_module_set_used(u->module, -                      (u->sink ? pa_sink_used_by(u->sink) : 0) + -                      (u->source ? pa_source_used_by(u->source) : 0)); -} - -static void clear_up(struct userdata *u) { -    assert(u); - -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); -        u->sink = NULL; -    } - -    if (u->source) { -        pa_source_disconnect(u->source); -        pa_source_unref(u->source); -        u->source = NULL; -    } - -    if (u->in_mmap && u->in_mmap != MAP_FAILED) { -        munmap(u->in_mmap, u->in_mmap_length); -        u->in_mmap = NULL; -    } - -    if (u->out_mmap && u->out_mmap != MAP_FAILED) { -        munmap(u->out_mmap, u->out_mmap_length); -        u->out_mmap = NULL; -    } - -    if (u->io_event) { -        u->core->mainloop->io_free(u->io_event); -        u->io_event = NULL; -    } - -    if (u->fd >= 0) { -        close(u->fd); -        u->fd = -1; -    } -} - -static void out_fill_memblocks(struct userdata *u, unsigned n) { -    assert(u && u->out_memblocks); - -    while (n > 0) { -        pa_memchunk chunk; - -        if (u->out_memblocks[u->out_current]) -            pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); - -        chunk.memblock = u->out_memblocks[u->out_current] = -            pa_memblock_new_fixed( -                    u->core->mempool, -                    (uint8_t*) u->out_mmap+u->out_fragment_size*u->out_current, -                    u->out_fragment_size, -                    1); -        assert(chunk.memblock); -        chunk.length = chunk.memblock->length; -        chunk.index = 0; - -        pa_sink_render_into_full(u->sink, &chunk); - -        u->out_current++; -        while (u->out_current >= u->out_fragments) -            u->out_current -= u->out_fragments; - -        n--; -    } -} - -static void do_write(struct userdata *u) { -    struct count_info info; -    assert(u && u->sink); - -    update_usage(u); - -    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { -        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno)); - -        clear_up(u); -        pa_module_unload_request(u->module); -        return; -    } - -    info.blocks += u->out_blocks_saved; -    u->out_blocks_saved = 0; - -    if (!info.blocks) -        return; - -    out_fill_memblocks(u, info.blocks); -} - -static void in_post_memblocks(struct userdata *u, unsigned n) { -    assert(u && u->in_memblocks); - -    while (n > 0) { -        pa_memchunk chunk; - -        if (!u->in_memblocks[u->in_current]) { -            chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->core->mempool, (uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1); -            chunk.length = chunk.memblock->length; -            chunk.index = 0; - -            pa_source_post(u->source, &chunk); -        } - -        u->in_current++; -        while (u->in_current >= u->in_fragments) -            u->in_current -= u->in_fragments; - -        n--; -    } -} - -static void in_clear_memblocks(struct userdata*u, unsigned n) { -    unsigned i = u->in_current; -    assert(u && u->in_memblocks); - -    if (n > u->in_fragments) -        n = u->in_fragments; - -    while (n > 0) { -        if (u->in_memblocks[i]) { -            pa_memblock_unref_fixed(u->in_memblocks[i]); -            u->in_memblocks[i] = NULL; -        } - -        i++; -        while (i >= u->in_fragments) -            i -= u->in_fragments; - -        n--; -    } -} - -static void do_read(struct userdata *u) { -    struct count_info info; -    assert(u && u->source); - -    update_usage(u); - -    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { -        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno)); - -        clear_up(u); -        pa_module_unload_request(u->module); -        return; -    } - -    info.blocks += u->in_blocks_saved; -    u->in_blocks_saved = 0; - -    if (!info.blocks) -        return; - -    in_post_memblocks(u, info.blocks); -    in_clear_memblocks(u, u->in_fragments/2); -} - -static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) { -    struct userdata *u = userdata; -    assert (u && u->core->mainloop == m && u->io_event == e); - -    if (f & PA_IO_EVENT_ERROR) { -        clear_up(u); -        pa_module_unload_request(u->module); -        return; -    } - -    if (f & PA_IO_EVENT_INPUT) -        do_read(u); -    if (f & PA_IO_EVENT_OUTPUT) -        do_write(u); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    struct count_info info; -    size_t bpos, n, total; -    assert(s && u); - -    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { -        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno)); -        return 0; -    } - -    u->out_blocks_saved += info.blocks; - -    total = u->out_fragments * u->out_fragment_size; -    bpos = ((u->out_current + u->out_blocks_saved) * u->out_fragment_size) % total; - -    if (bpos <= (size_t) info.ptr) -        n = total - (info.ptr - bpos); -    else -        n = bpos - 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); */ - -    return pa_bytes_to_usec(n, &s->sample_spec); -} - -static pa_usec_t source_get_latency_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    struct count_info info; -    size_t bpos, n, total; -    assert(s && u); - -    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { -        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno)); -        return 0; -    } - -    u->in_blocks_saved += info.blocks; - -    total = u->in_fragments * u->in_fragment_size; -    bpos = ((u->in_current + u->in_blocks_saved) * u->in_fragment_size) % total; - -    if (bpos <= (size_t) info.ptr) -        n = info.ptr - bpos; -    else -        n = (u->in_fragments * u->in_fragment_size) - bpos + 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);  */ - -    return pa_bytes_to_usec(n, &s->sample_spec); -} - -static int sink_get_hw_volume(pa_sink *s) { -    struct userdata *u = s->userdata; - -    if (pa_oss_get_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); -        s->get_hw_volume = NULL; -        return -1; -    } - -    return 0; -} - -static int sink_set_hw_volume(pa_sink *s) { -    struct userdata *u = s->userdata; - -    if (pa_oss_set_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); -        s->set_hw_volume = NULL; -        return -1; -    } - -    return 0; -} - -static int source_get_hw_volume(pa_source *s) { -    struct userdata *u = s->userdata; - -    if (pa_oss_get_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); -        s->get_hw_volume = NULL; -        return -1; -    } - -    return 0; -} - -static int source_set_hw_volume(pa_source *s) { -    struct userdata *u = s->userdata; - -    if (pa_oss_set_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); -        s->set_hw_volume = NULL; -        return -1; -    } - -    return 0; -} - -int pa__init(pa_core *c, pa_module*m) { -    struct audio_buf_info info; -    struct userdata *u = NULL; -    const char *p; -    int nfrags, frag_size; -    int mode, caps; -    int enable_bits = 0, zero = 0; -    int playback = 1, record = 1; -    pa_modargs *ma = NULL; -    char hwdesc[64], *t; -    pa_channel_map map; -    const char *name; -    char *name_buf = NULL; -    int namereg_fail; - -    assert(c); -    assert(m); - -    m->userdata = u = pa_xnew0(struct userdata, 1); -    u->module = m; -    u->fd = -1; -    u->core = c; - -    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments."); -        goto fail; -    } - -    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { -        pa_log("record= and playback= expect numeric arguments."); -        goto fail; -    } - -    if (!playback && !record) { -        pa_log("neither playback nor record enabled for device."); -        goto fail; -    } - -    mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - -    nfrags = DEFAULT_NFRAGS; -    frag_size = DEFAULT_FRAGSIZE; -    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"); -        goto fail; -    } - -    u->sample_spec = c->default_sample_spec; -    if (pa_modargs_get_sample_spec_and_channel_map(ma, &u->sample_spec, &map, PA_CHANNEL_MAP_OSS) < 0) { -        pa_log("failed to parse sample specification or channel map"); -        goto fail; -    } - -    if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) -        goto fail; - -    if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER)) { -        pa_log("OSS device not mmap capable."); -        goto fail; -    } - -    pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - -    if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0) -        pa_log_info("hardware name is '%s'.", hwdesc); -    else -        hwdesc[0] = 0; - -    if (nfrags >= 2 && frag_size >= 1) -        if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0) -            goto fail; - -    if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0) -        goto fail; - -    if (mode != O_WRONLY) { -        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { -            pa_log("SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno)); -            goto fail; -        } - -        pa_log_info("input -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); - -        if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { -            if (mode == O_RDWR) { -                pa_log("mmap failed for input. Changing to O_WRONLY mode."); -                mode = O_WRONLY; -            } else { -                pa_log("mmap(): %s", pa_cstrerror(errno)); -                goto fail; -            } -        } else { -            if ((name = pa_modargs_get_value(ma, "source_name", NULL))) -                namereg_fail = 1; -            else { -                name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p)); -                namereg_fail = 0; -            } - -            if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map))) -                goto fail; - -            u->source->userdata = u; -            u->source->get_latency = source_get_latency_cb; -            u->source->get_hw_volume = source_get_hw_volume; -            u->source->set_hw_volume = source_set_hw_volume; -            pa_source_set_owner(u->source, m); -            pa_source_set_description(u->source, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s", -                                                                       p, -                                                                       hwdesc[0] ? " (" : "", -                                                                       hwdesc[0] ? hwdesc : "", -                                                                       hwdesc[0] ? ")" : "")); -            pa_xfree(t); -            u->source->is_hardware = 1; - -            u->in_memblocks = pa_xnew0(pa_memblock*, u->in_fragments); - -            enable_bits |= PCM_ENABLE_INPUT; -        } -    } - -    pa_xfree(name_buf); -    name_buf = NULL; - -    if (mode != O_RDONLY) { -        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { -            pa_log("SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno)); -            goto fail; -        } - -        pa_log_info("output -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); - -        if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0))  == MAP_FAILED) { -            if (mode == O_RDWR) { -                pa_log("mmap filed for output. Changing to O_RDONLY mode."); -                mode = O_RDONLY; -            } else { -                pa_log("mmap(): %s", pa_cstrerror(errno)); -                goto fail; -            } -        } else { -            pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); - -            if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) -                namereg_fail = 1; -            else { -                name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p)); -                namereg_fail = 0; -            } - -            if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map))) -                goto fail; - -            u->sink->get_latency = sink_get_latency_cb; -            u->sink->get_hw_volume = sink_get_hw_volume; -            u->sink->set_hw_volume = sink_set_hw_volume; -            u->sink->userdata = u; -            pa_sink_set_owner(u->sink, m); -            pa_sink_set_description(u->sink, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s", -                                                                   p, -                                                                   hwdesc[0] ? " (" : "", -                                                                   hwdesc[0] ? hwdesc : "", -                                                                   hwdesc[0] ? ")" : "")); -            pa_xfree(t); - -            u->sink->is_hardware = 1; -            u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments); - -            enable_bits |= PCM_ENABLE_OUTPUT; -        } -    } - -    pa_xfree(name_buf); -    name_buf = NULL; - -    zero = 0; -    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { -        pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno)); -        goto fail; -    } - -    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { -        pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno)); -        goto fail; -    } - -    assert(u->source || u->sink); - -    u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u); -    assert(u->io_event); - -    pa_modargs_free(ma); - -    /* Read mixer settings */ -    if (u->source) -        source_get_hw_volume(u->source); -    if (u->sink) -        sink_get_hw_volume(u->sink); - -    return 0; - -fail: -    pa__done(c, m); - -    if (ma) -        pa_modargs_free(ma); - -    pa_xfree(name_buf); - -    return -1; -} - -void pa__done(pa_core *c, pa_module*m) { -    struct userdata *u; - -    assert(c); -    assert(m); - -    if (!(u = m->userdata)) -        return; - -    clear_up(u); - -    if (u->out_memblocks) { -        unsigned i; -        for (i = 0; i < u->out_fragments; i++) -            if (u->out_memblocks[i]) -                pa_memblock_unref_fixed(u->out_memblocks[i]); -        pa_xfree(u->out_memblocks); -    } - -    if (u->in_memblocks) { -        unsigned i; -        for (i = 0; i < u->in_fragments; i++) -            if (u->in_memblocks[i]) -                pa_memblock_unref_fixed(u->in_memblocks[i]); -        pa_xfree(u->in_memblocks); -    } - -    pa_xfree(u); -} diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index 9d4d0eac..19dceef2 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -22,27 +22,46 @@    USA.  ***/ +/* General power management rules: + * + *   When SUSPENDED we close the audio device. + * + *   We make no difference between IDLE and RUNNING in our handling. + * + *   As long as we are in RUNNING/IDLE state we will *always* write data to + *   the device. If none is avilable from the inputs, we write silence + *   instead. + * + *   If power should be saved on IDLE module-suspend-on-idle should be used. + * + */ +  #ifdef HAVE_CONFIG_H  #include <config.h>  #endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +  #include <sys/soundcard.h>  #include <sys/ioctl.h>  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <limits.h> +#include <signal.h> +#include <poll.h>  #include <pulse/xmalloc.h>  #include <pulse/util.h>  #include <pulsecore/core-error.h> -#include <pulsecore/iochannel.h> +#include <pulsecore/thread.h>  #include <pulsecore/sink.h>  #include <pulsecore/source.h>  #include <pulsecore/module.h> @@ -50,6 +69,9 @@  #include <pulsecore/core-util.h>  #include <pulsecore/modargs.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h>  #include "oss-util.h"  #include "module-oss-symdef.h" @@ -68,21 +90,48 @@ PA_MODULE_USAGE(          "rate=<sample rate> "          "fragments=<number of fragments> "          "fragment_size=<fragment size> " -        "channel_map=<channel map>") +        "channel_map=<channel map> " +        "mmap=<enable memory mapping?>") + +#define DEFAULT_DEVICE "/dev/dsp"  struct userdata { +    pa_core *core; +    pa_module *module;      pa_sink *sink;      pa_source *source; -    pa_iochannel *io; -    pa_core *core; -    pa_memchunk memchunk, silence; +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; + +    char *device_name; + +    pa_memchunk memchunk; -    uint32_t in_fragment_size, out_fragment_size, sample_size; -    int use_getospace, use_getispace; +    size_t frame_size; +    uint32_t in_fragment_size, out_fragment_size, in_nfrags, out_nfrags, in_hwbuf_size, out_hwbuf_size; +    pa_bool_t use_getospace, use_getispace; +    pa_bool_t use_getodelay; + +    pa_bool_t sink_suspended, source_suspended;      int fd; -    pa_module *module; +    int mode; + +    int mixer_fd; +    int mixer_devmask; + +    int nfrags, frag_size; + +    pa_bool_t use_mmap; +    unsigned out_mmap_current, in_mmap_current; +    void *in_mmap, *out_mmap; +    pa_memblock **in_mmap_memblocks, **out_mmap_memblocks; + +    int in_mmap_saved_nfrags, out_mmap_saved_nfrags; + +    pa_rtpoll_item *rtpoll_item;  };  static const char* const valid_modargs[] = { @@ -97,280 +146,1010 @@ static const char* const valid_modargs[] = {      "rate",      "channels",      "channel_map", +    "mmap",      NULL  }; -#define DEFAULT_DEVICE "/dev/dsp" +static void trigger(struct userdata *u, pa_bool_t quick) { +    int enable_bits = 0, zero = 0; -static void update_usage(struct userdata *u) { -   pa_module_set_used(u->module, -                      (u->sink ? pa_sink_used_by(u->sink) : 0) + -                      (u->source ? pa_source_used_by(u->source) : 0)); -} +    pa_assert(u); -static void clear_up(struct userdata *u) { -    assert(u); +    if (u->fd < 0) +        return; -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); -        u->sink = NULL; -    } +     pa_log_debug("trigger"); -    if (u->source) { -        pa_source_disconnect(u->source); -        pa_source_unref(u->source); -        u->source = NULL; -    } +    if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) +        enable_bits |= PCM_ENABLE_INPUT; -    if (u->io) { -        pa_iochannel_free(u->io); -        u->io = NULL; -    } -} +    if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) +        enable_bits |= PCM_ENABLE_OUTPUT; -static void do_write(struct userdata *u) { -    pa_memchunk *memchunk; -    ssize_t r; -    size_t l; -    int loop = 0; +    pa_log_debug("trigger: %i", enable_bits); -    assert(u); -    if (!u->sink || !pa_iochannel_is_writable(u->io)) -        return; +    if (u->use_mmap) { -    update_usage(u); +        if (!quick) +            ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero); -    l = u->out_fragment_size; +#ifdef SNDCTL_DSP_HALT +        if (enable_bits == 0) +            if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0) +                pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno)); +#endif -    if (u->use_getospace) { -        audio_buf_info info; +        if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) +            pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno)); -        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) -            u->use_getospace = 0; -        else { -            if (info.bytes/l > 0) { -                l = (info.bytes/l)*l; -                loop = 1; +        if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) { +            pa_log_debug("clearing playback buffer"); +            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec); +        } + +    } else { + +        if (enable_bits) +            if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0) +                pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno)); + +        if (!quick) { +            /* +             * Some crappy drivers do not start the recording until we +             * read something.  Without this snippet, poll will never +             * register the fd as ready. +             */ + +            if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) { +                uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size); +                pa_read(u->fd, buf, u->in_fragment_size, NULL); +                pa_xfree(buf);              }          }      } +} -    do { -        memchunk = &u->memchunk; +static void mmap_fill_memblocks(struct userdata *u, unsigned n) { +    pa_assert(u); +    pa_assert(u->out_mmap_memblocks); -        if (!memchunk->length) -            if (pa_sink_render(u->sink, l, memchunk) < 0) -                memchunk = &u->silence; +/*     pa_log("Mmmap writing %u blocks", n); */ -        assert(memchunk->memblock); -        assert(memchunk->memblock->data); -        assert(memchunk->length); +    while (n > 0) { +        pa_memchunk chunk; -        if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { +        if (u->out_mmap_memblocks[u->out_mmap_current]) +            pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]); -            if (errno != EAGAIN) { -                pa_log("write() failed: %s", pa_cstrerror(errno)); +        chunk.memblock = u->out_mmap_memblocks[u->out_mmap_current] = +            pa_memblock_new_fixed( +                    u->core->mempool, +                    (uint8_t*) u->out_mmap + u->out_fragment_size * u->out_mmap_current, +                    u->out_fragment_size, +                    1); -                clear_up(u); -                pa_module_unload_request(u->module); -            } +        chunk.length = pa_memblock_get_length(chunk.memblock); +        chunk.index = 0; -            break; -        } +        pa_sink_render_into_full(u->sink, &chunk); -        if (memchunk == &u->silence) -            assert(r % u->sample_size == 0); -        else { -            u->memchunk.index += r; -            u->memchunk.length -= r; +        u->out_mmap_current++; +        while (u->out_mmap_current >= u->out_nfrags) +            u->out_mmap_current -= u->out_nfrags; -            if (u->memchunk.length <= 0) { -                pa_memblock_unref(u->memchunk.memblock); -                u->memchunk.memblock = NULL; -            } -        } +        n--; +    } +} + +static int mmap_write(struct userdata *u) { +    struct count_info info; + +    pa_assert(u); +    pa_assert(u->sink); + +/*     pa_log("Mmmap writing..."); */ + +    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { +        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno)); +        return -1; +    } + +    info.blocks += u->out_mmap_saved_nfrags; +    u->out_mmap_saved_nfrags = 0; + +    if (info.blocks > 0) +        mmap_fill_memblocks(u, info.blocks); -        l = l > (size_t) r ? l - r : 0; -    } while (loop && l > 0); +    return info.blocks;  } -static void do_read(struct userdata *u) { -    pa_memchunk memchunk; -    ssize_t r; -    size_t l; -    int loop = 0; -    assert(u); +static void mmap_post_memblocks(struct userdata *u, unsigned n) { +    pa_assert(u); +    pa_assert(u->in_mmap_memblocks); -    if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs)) -        return; +/*     pa_log("Mmmap reading %u blocks", n); */ -    update_usage(u); +    while (n > 0) { +        pa_memchunk chunk; -    l = u->in_fragment_size; +        if (!u->in_mmap_memblocks[u->in_mmap_current]) { -    if (u->use_getispace) { -        audio_buf_info info; +            chunk.memblock = u->in_mmap_memblocks[u->in_mmap_current] = +                pa_memblock_new_fixed( +                        u->core->mempool, +                        (uint8_t*) u->in_mmap + u->in_fragment_size*u->in_mmap_current, +                        u->in_fragment_size, +                        1); -        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) -            u->use_getispace = 0; -        else { -            if (info.bytes/l > 0) { -                l = (info.bytes/l)*l; -                loop = 1; -            } +            chunk.length = pa_memblock_get_length(chunk.memblock); +            chunk.index = 0; + +            pa_source_post(u->source, &chunk);          } + +        u->in_mmap_current++; +        while (u->in_mmap_current >= u->in_nfrags) +            u->in_mmap_current -= u->in_nfrags; + +        n--;      } +} -    do { -        memchunk.memblock = pa_memblock_new(u->core->mempool, l); -        assert(memchunk.memblock); -        if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { -            pa_memblock_unref(memchunk.memblock); +static void mmap_clear_memblocks(struct userdata*u, unsigned n) { +    unsigned i = u->in_mmap_current; -            if (errno != EAGAIN) { -                pa_log("read() failed: %s", pa_cstrerror(errno)); +    pa_assert(u); +    pa_assert(u->in_mmap_memblocks); -                clear_up(u); -                pa_module_unload_request(u->module); -            } +    if (n > u->in_nfrags) +        n = u->in_nfrags; -            break; +    while (n > 0) { +        if (u->in_mmap_memblocks[i]) { +            pa_memblock_unref_fixed(u->in_mmap_memblocks[i]); +            u->in_mmap_memblocks[i] = NULL;          } -        assert(r <= (ssize_t) memchunk.memblock->length); -        memchunk.length = memchunk.memblock->length = r; -        memchunk.index = 0; +        i++; +        while (i >= u->in_nfrags) +            i -= u->in_nfrags; -        pa_source_post(u->source, &memchunk); -        pa_memblock_unref(memchunk.memblock); +        n--; +    } +} -        l = l > (size_t) r ? l - r : 0; -    } while (loop && l > 0); +static int mmap_read(struct userdata *u) { +    struct count_info info; +    pa_assert(u); +    pa_assert(u->source); + +/*     pa_log("Mmmap reading..."); */ + +    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { +        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno)); +        return -1; +    } + +/*     pa_log("... %i", info.blocks); */ + +    info.blocks += u->in_mmap_saved_nfrags; +    u->in_mmap_saved_nfrags = 0; + +    if (info.blocks > 0) { +        mmap_post_memblocks(u, info.blocks); +        mmap_clear_memblocks(u, u->in_nfrags/2); +    } + +    return info.blocks;  } -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_write(u); -    do_read(u); +static pa_usec_t mmap_sink_get_latency(struct userdata *u) { +    struct count_info info; +    size_t bpos, n; + +    pa_assert(u); + +    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { +        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno)); +        return 0; +    } + +    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; + +    if (bpos <= (size_t) info.ptr) +        n = u->out_hwbuf_size - (info.ptr - bpos); +    else +        n = bpos - 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); */ + +    return pa_bytes_to_usec(n, &u->sink->sample_spec);  } -static void source_notify_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    assert(u); -    do_read(u); +static pa_usec_t mmap_source_get_latency(struct userdata *u) { +    struct count_info info; +    size_t bpos, n; + +    pa_assert(u); + +    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { +        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno)); +        return 0; +    } + +    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; + +    if (bpos <= (size_t) info.ptr) +        n = info.ptr - bpos; +    else +        n = u->in_hwbuf_size - bpos + 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);  */ + +    return pa_bytes_to_usec(n, &u->source->sample_spec);  } -static pa_usec_t sink_get_latency_cb(pa_sink *s) { +static pa_usec_t io_sink_get_latency(struct userdata *u) {      pa_usec_t r = 0; -    int arg; -    struct userdata *u = s->userdata; -    assert(s && u && u->sink); -    if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { -        pa_log_info("device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno)); -        s->get_latency = NULL; -        return 0; +    pa_assert(u); + +    if (u->use_getodelay) { +        int arg; + +        if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { +            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(arg, &s->sample_spec); +    if (!u->use_getodelay && u->use_getospace) { +        struct audio_buf_info info; + +        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { +            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); +    }      if (u->memchunk.memblock) -        r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); +        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);      return r;  } -static pa_usec_t source_get_latency_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    audio_buf_info info; -    assert(s && u && u->source); -    if (!u->use_getispace) -        return 0; +static pa_usec_t io_source_get_latency(struct userdata *u) { +    pa_usec_t r = 0; -    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { -        u->use_getispace = 0; -        return 0; +    pa_assert(u); + +    if (u->use_getispace) { +        struct audio_buf_info info; + +        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { +            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);      } -    if (info.bytes <= 0) -        return 0; +    return r; +} + +static void build_pollfd(struct userdata *u) { +    struct pollfd *pollfd; -    return pa_bytes_to_usec(info.bytes, &s->sample_spec); +    pa_assert(u); +    pa_assert(u->fd >= 0); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(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 = 0; +    pollfd->revents = 0;  } -static int sink_get_hw_volume(pa_sink *s) { -    struct userdata *u = s->userdata; +static int suspend(struct userdata *u) { +    pa_assert(u); +    pa_assert(u->fd >= 0); -    if (pa_oss_get_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); -        s->get_hw_volume = NULL; -        return -1; +    pa_log_info("Suspending..."); + +    if (u->out_mmap_memblocks) { +        unsigned i; +        for (i = 0; i < u->out_nfrags; i++) +            if (u->out_mmap_memblocks[i]) { +                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]); +                u->out_mmap_memblocks[i] = NULL; +            }      } +    if (u->in_mmap_memblocks) { +        unsigned i; +        for (i = 0; i < u->in_nfrags; i++) +            if (u->in_mmap_memblocks[i]) { +                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]); +                u->in_mmap_memblocks[i] = NULL; +            } +    } + +    if (u->in_mmap && u->in_mmap != MAP_FAILED) { +        munmap(u->in_mmap, u->in_hwbuf_size); +        u->in_mmap = NULL; +    } + +    if (u->out_mmap && u->out_mmap != MAP_FAILED) { +        munmap(u->out_mmap, u->out_hwbuf_size); +        u->out_mmap = NULL; +    } + +    /* Let's suspend */ +    ioctl(u->fd, SNDCTL_DSP_SYNC, NULL); +    pa_close(u->fd); +    u->fd = -1; + +    if (u->rtpoll_item) { +        pa_rtpoll_item_free(u->rtpoll_item); +        u->rtpoll_item = NULL; +    } + +    pa_log_info("Device suspended..."); +      return 0;  } -static int sink_set_hw_volume(pa_sink *s) { -    struct userdata *u = s->userdata; +static int unsuspend(struct userdata *u) { +    int m; +    pa_sample_spec ss, *ss_original; +    int frag_size, in_frag_size, out_frag_size; +    int in_nfrags, out_nfrags; +    struct audio_buf_info info; + +    pa_assert(u); +    pa_assert(u->fd < 0); + +    m = u->mode; + +    pa_log_info("Trying resume..."); -    if (pa_oss_set_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); -        s->set_hw_volume = NULL; +    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) +        pa_log_warn("Resume failed, couldn't open device with original access mode."); +        goto fail; +    } + +    if (u->nfrags >= 2 && u->frag_size >= 1) +        if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) { +            pa_log_warn("Resume failed, couldn't set original fragment settings."); +            goto fail; +        } + +    ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec); +    if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) { +        pa_log_warn("Resume failed, couldn't set original sample format settings."); +        goto fail; +    } + +    if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { +        pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno)); +        goto fail; +    } + +    in_frag_size = out_frag_size = frag_size; +    in_nfrags = out_nfrags = u->nfrags; + +    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { +        in_frag_size = info.fragsize; +        in_nfrags = info.fragstotal; +    } + +    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { +        out_frag_size = info.fragsize; +        out_nfrags = info.fragstotal; +    } + +    if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) || +        (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) { +        pa_log_warn("Resume failed, input fragment settings don't match."); +        goto fail; +    } + +    if (u->use_mmap) { +        if (u->source) { +            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { +                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno)); +                goto fail; +            } +        } + +        if (u->sink) { +            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { +                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno)); +                if (u->in_mmap && u->in_mmap != MAP_FAILED) { +                    munmap(u->in_mmap, u->in_hwbuf_size); +                    u->in_mmap = NULL; +                } + +                goto fail; +            } + +            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss); +        }      } +    u->out_mmap_current = u->in_mmap_current = 0; +    u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0; + +    pa_assert(!u->rtpoll_item); + +    build_pollfd(u); + +    if (u->sink) +        pa_sink_get_volume(u->sink); +    if (u->source) +        pa_source_get_volume(u->source); + +    pa_log_info("Resumed successfully..."); +      return 0; + +fail: +    pa_close(u->fd); +    u->fd = -1; +    return -1;  } -static int source_get_hw_volume(pa_source *s) { -    struct userdata *u = s->userdata; +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; +    int ret; +    pa_bool_t do_trigger = FALSE, quick = TRUE; + +    switch (code) { + +        case PA_SINK_MESSAGE_GET_LATENCY: { +            pa_usec_t r = 0; + +            if (u->fd >= 0) { +                if (u->use_mmap) +                    r = mmap_sink_get_latency(u); +                else +                    r = io_sink_get_latency(u); +            } + +            *((pa_usec_t*) data) = r; + +            return 0; +        } + +        case PA_SINK_MESSAGE_SET_STATE: + +            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + +                case PA_SINK_SUSPENDED: +                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + +                    if (!u->source || u->source_suspended) { +                        if (suspend(u) < 0) +                            return -1; +                    } + +                    do_trigger = TRUE; + +                    u->sink_suspended = TRUE; +                    break; + +                case PA_SINK_IDLE: +                case PA_SINK_RUNNING: + +                    if (u->sink->thread_info.state == PA_SINK_INIT) { +                        do_trigger = TRUE; +                        quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state); +                    } + +                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + +                        if (!u->source || u->source_suspended) { +                            if (unsuspend(u) < 0) +                                return -1; +                            quick = FALSE; +                        } + +                        do_trigger = TRUE; + +                        u->out_mmap_current = 0; +                        u->out_mmap_saved_nfrags = 0; + +                        u->sink_suspended = FALSE; +                    } + +                    break; + +                case PA_SINK_UNLINKED: +                case PA_SINK_INIT: +                    ; +            } + +            break; -    if (pa_oss_get_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); -        s->get_hw_volume = NULL; -        return -1;      } -    return 0; +    ret = pa_sink_process_msg(o, code, data, offset, chunk); + +    if (do_trigger) +        trigger(u, quick); + +    return ret;  } -static int source_set_hw_volume(pa_source *s) { -    struct userdata *u = s->userdata; +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SOURCE(o)->userdata; +    int ret; +    int do_trigger = FALSE, quick = TRUE; + +    switch (code) { + +        case PA_SOURCE_MESSAGE_GET_LATENCY: { +            pa_usec_t r = 0; + +            if (u->fd >= 0) { +                if (u->use_mmap) +                    r = mmap_source_get_latency(u); +                else +                    r = io_source_get_latency(u); +            } + +            *((pa_usec_t*) data) = r; +            return 0; +        } + +        case PA_SOURCE_MESSAGE_SET_STATE: + +            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { +                case PA_SOURCE_SUSPENDED: +                    pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state)); + +                    if (!u->sink || u->sink_suspended) { +                        if (suspend(u) < 0) +                            return -1; +                    } + +                    do_trigger = TRUE; + +                    u->source_suspended = TRUE; +                    break; + +                case PA_SOURCE_IDLE: +                case PA_SOURCE_RUNNING: + +                    if (u->source->thread_info.state == PA_SOURCE_INIT) { +                        do_trigger = TRUE; +                        quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state); +                    } + +                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { + +                        if (!u->sink || u->sink_suspended) { +                            if (unsuspend(u) < 0) +                                return -1; +                            quick = FALSE; +                        } + +                        do_trigger = TRUE; + +                        u->in_mmap_current = 0; +                        u->in_mmap_saved_nfrags = 0; + +                        u->source_suspended = FALSE; +                    } +                    break; + +                case PA_SOURCE_UNLINKED: +                case PA_SOURCE_INIT: +                    ; + +            } +            break; -    if (pa_oss_set_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) { -        pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); -        s->set_hw_volume = NULL; -        return -1;      } -    return 0; +    ret = pa_source_process_msg(o, code, data, offset, chunk); + +    if (do_trigger) +        trigger(u, quick); + +    return ret; +} + +static int sink_get_volume(pa_sink *s) { +    struct userdata *u; +    int r; + +    pa_assert_se(u = s->userdata); + +    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); + +    if (u->mixer_devmask & SOUND_MASK_VOLUME) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    if (u->mixer_devmask & SOUND_MASK_PCM) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); +    return -1; +} + +static int sink_set_volume(pa_sink *s) { +    struct userdata *u; +    int r; + +    pa_assert_se(u = s->userdata); + +    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); + +    if (u->mixer_devmask & SOUND_MASK_VOLUME) +        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    if (u->mixer_devmask & SOUND_MASK_PCM) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); +    return -1;  } -int pa__init(pa_core *c, pa_module*m) { +static int source_get_volume(pa_source *s) { +    struct userdata *u; +    int r; + +    pa_assert_se(u = s->userdata); + +    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); + +    if (u->mixer_devmask & SOUND_MASK_IGAIN) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    if (u->mixer_devmask & SOUND_MASK_RECLEV) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); +    return -1; +} + +static int source_set_volume(pa_source *s) { +    struct userdata *u; +    int r; + +    pa_assert_se(u = s->userdata); + +    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); + +    if (u->mixer_devmask & SOUND_MASK_IGAIN) +        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    if (u->mixer_devmask & SOUND_MASK_RECLEV) +        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0) +            return r; + +    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); +    return -1; +} + +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    int write_type = 0, read_type = 0; +    unsigned short revents = 0; + +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    if (u->core->high_priority) +        pa_make_realtime(); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    for (;;) { +        int ret; + +/*        pa_log("loop");    */ + +        /* Render some data and write it to the dsp */ + +        if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) { + +            if (u->use_mmap) { + +                if ((ret = mmap_write(u)) < 0) +                    goto fail; + +                revents &= ~POLLOUT; + +                if (ret > 0) +                    continue; + +            } else { +                ssize_t l; +                pa_bool_t loop = FALSE, work_done = FALSE; + +                l = u->out_fragment_size; + +                if (u->use_getospace) { +                    audio_buf_info info; + +                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { +                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno)); +                        u->use_getospace = FALSE; +                    } else { +                        l = info.bytes; + +                        /* We loop only if GETOSPACE worked and we +                         * actually *know* that we can write more than +                         * one fragment at a time */ +                        loop = TRUE; +                    } +                } + +                /* 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; + +                /* 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; +                    loop = FALSE; +                } + +                while (l > 0) { +                    void *p; +                    ssize_t t; + +                    if (u->memchunk.length <= 0) +                        pa_sink_render(u->sink, l, &u->memchunk); + +                    pa_assert(u->memchunk.length > 0); + +                    p = pa_memblock_acquire(u->memchunk.memblock); +                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); +                    pa_memblock_release(u->memchunk.memblock); + +/*                     pa_log("wrote %i bytes of %u", t, l); */ + +                    pa_assert(t != 0); + +                    if (t < 0) { + +                        if (errno == EINTR) +                            continue; + +                        else if (errno == EAGAIN) { +                            pa_log_debug("EAGAIN"); + +                            revents &= ~POLLOUT; +                            break; + +                        } else { +                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); +                            goto fail; +                        } + +                    } else { + +                        u->memchunk.index += t; +                        u->memchunk.length -= t; + +                        if (u->memchunk.length <= 0) { +                            pa_memblock_unref(u->memchunk.memblock); +                            pa_memchunk_reset(&u->memchunk); +                        } + +                        l -= t; + +                        revents &= ~POLLOUT; +                        work_done = TRUE; +                    } + +                    if (!loop) +                        break; +                } + +                if (work_done) +                    continue; +            } +        } + +        /* Try to read some data and pass it on to the source driver. */ + +        if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) { + +            if (u->use_mmap) { + +                if ((ret = mmap_read(u)) < 0) +                    goto fail; + +                revents &= ~POLLIN; + +                if (ret > 0) +                    continue; + +            } else { + +                void *p; +                ssize_t l; +                pa_memchunk memchunk; +                pa_bool_t loop = FALSE, work_done = FALSE; + +                l = u->in_fragment_size; + +                if (u->use_getispace) { +                    audio_buf_info info; + +                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { +                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno)); +                        u->use_getispace = FALSE; +                    } else { +                        l = info.bytes; +                        loop = TRUE; +                    } +                } + +                l = (l/u->in_fragment_size) * u->in_fragment_size; + +                if (l <= 0 && (revents & POLLIN)) { +                    l = u->in_fragment_size; +                    loop = FALSE; +                } + +                while (l > 0) { +                    ssize_t t, k; + +                    pa_assert(l > 0); + +                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); + +                    k = pa_memblock_get_length(memchunk.memblock); + +                    if (k > l) +                        k = l; + +                    k = (k/u->frame_size)*u->frame_size; + +                    p = pa_memblock_acquire(memchunk.memblock); +                    t = pa_read(u->fd, p, k, &read_type); +                    pa_memblock_release(memchunk.memblock); + +                    pa_assert(t != 0); /* EOF cannot happen */ + +/*                     pa_log("read %i bytes of %u", t, l); */ + +                    if (t < 0) { +                        pa_memblock_unref(memchunk.memblock); + +                        if (errno == EINTR) +                            continue; + +                        else if (errno == EAGAIN) { +                            pa_log_debug("EAGAIN"); + +                            revents &= ~POLLIN; +                            break; + +                        } else { +                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); +                            goto fail; +                        } + +                    } else { +                        memchunk.index = 0; +                        memchunk.length = t; + +                        pa_source_post(u->source, &memchunk); +                        pa_memblock_unref(memchunk.memblock); + +                        l -= t; + +                        revents &= ~POLLIN; +                        work_done = TRUE; +                    } + +                    if (!loop) +                        break; +                } + +                if (work_done) +                    continue; +            } +        } + +/*         pa_log("loop2 revents=%i", revents); */ + +        if (u->rtpoll_item) { +            struct pollfd *pollfd; + +            pa_assert(u->fd >= 0); + +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); +            pollfd->events = +                ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | +                ((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0); +        } + +        /* Hmm, nothing to do. Let's sleep */ +        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|POLLIN)) { +                pa_log("DSP shutdown."); +                goto fail; +            } + +            revents = pollfd->revents; +        } else +            revents = 0; +    } + +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: +    pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { +      struct audio_buf_info info;      struct userdata *u = NULL; -    const char *p; +    const char *dev;      int fd = -1; -    int nfrags, frag_size, in_frag_size, out_frag_size; -    int mode; -    int record = 1, playback = 1; +    int nfrags, frag_size; +    int mode, caps; +    int record = 1, playback = 1, use_mmap = 1;      pa_sample_spec ss;      pa_channel_map map;      pa_modargs *ma = NULL;      char hwdesc[64], *t;      const char *name; -    char *name_buf = NULL;      int namereg_fail; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments."); +        pa_log("Failed to parse module arguments.");          goto fail;      } @@ -380,36 +1159,52 @@ int pa__init(pa_core *c, pa_module*m) {      }      if (!playback && !record) { -        pa_log("neither playback nor record enabled for device."); +        pa_log("Neither playback nor record enabled for device.");          goto fail;      } -    mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); +    mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) { -        pa_log("failed to parse sample specification or channel map"); +        pa_log("Failed to parse sample specification or channel map");          goto fail;      } -    /* Fix latency to 100ms */ -    nfrags = 12; -    frag_size = pa_bytes_per_second(&ss)/128; +    nfrags = m->core->default_n_fragments; +    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); +    if (frag_size <= 0) +        frag_size = 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"); +        pa_log("Failed to parse fragments arguments");          goto fail;      } -    if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0) +    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { +        pa_log("Failed to parse mmap argument.");          goto fail; +    } -    if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0) -        pa_log_info("hardware name is '%s'.", hwdesc); +    if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) +        goto fail; + +    if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) { +        pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode."); +        use_mmap = 0; +    } + +    if (use_mmap && mode == O_WRONLY) { +        pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode."); +        use_mmap = 0; +    } + +    if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0) +        pa_log_info("Hardware name is '%s'.", hwdesc);      else          hwdesc[0] = 0; -    pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); +    pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));      if (nfrags >= 2 && frag_size >= 1)          if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0) @@ -422,152 +1217,282 @@ int pa__init(pa_core *c, pa_module*m) {          pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));          goto fail;      } -    assert(frag_size); -    in_frag_size = out_frag_size = frag_size; +    pa_assert(frag_size > 0); -    u = pa_xmalloc(sizeof(struct userdata)); -    u->core = c; -    u->use_getospace = u->use_getispace = 0; +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    u->fd = fd; +    u->mixer_fd = -1; +    u->use_getospace = u->use_getispace = 1; +    u->use_getodelay = 1; +    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->use_mmap = use_mmap; +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); +    u->rtpoll_item = NULL; +    build_pollfd(u);      if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { -        pa_log_info("input -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        in_frag_size = info.fragsize; +        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->use_getispace = 1;      }      if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { -        pa_log_info("output -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        out_frag_size = info.fragsize; +        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->use_getospace = 1;      } +    u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size; +    u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size; +      if (mode != O_WRONLY) { +        char *name_buf = NULL; + +        if (use_mmap) { +            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { +                pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno)); +                use_mmap = u->use_mmap = 0; +                u->in_mmap = NULL; +            } else +                pa_log_debug("Successfully mmap()ed input buffer."); +        } +          if ((name = pa_modargs_get_value(ma, "source_name", NULL)))              namereg_fail = 1;          else { -            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p)); +            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));              namereg_fail = 0;          } -        if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map))) +        u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map); +        pa_xfree(name_buf); +        if (!u->source) { +            pa_log("Failed to create source object");              goto fail; +        } +        u->source->parent.process_msg = source_process_msg;          u->source->userdata = u; -        u->source->notify = source_notify_cb; -        u->source->get_latency = source_get_latency_cb; -        u->source->get_hw_volume = source_get_hw_volume; -        u->source->set_hw_volume = source_set_hw_volume; -        pa_source_set_owner(u->source, m); -        pa_source_set_description(u->source, t = pa_sprintf_malloc("OSS PCM on %s%s%s%s", -                                                                 p, -                                                                 hwdesc[0] ? " (" : "", -                                                                 hwdesc[0] ? hwdesc : "", -                                                                 hwdesc[0] ? ")" : "")); + +        pa_source_set_module(u->source, m); +        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +        pa_source_set_rtpoll(u->source, u->rtpoll); +        pa_source_set_description(u->source, t = pa_sprintf_malloc( +                                          "OSS PCM on %s%s%s%s%s", +                                          dev, +                                          hwdesc[0] ? " (" : "", +                                          hwdesc[0] ? hwdesc : "", +                                          hwdesc[0] ? ")" : "", +                                          use_mmap ? " via DMA" : ""));          pa_xfree(t); -        u->source->is_hardware = 1; -    } else -        u->source = NULL; +        u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY; +        u->source->refresh_volume = TRUE; -    pa_xfree(name_buf); -    name_buf = NULL; +        if (use_mmap) +            u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags); +    }      if (mode != O_RDONLY) { +        char *name_buf = NULL; + +        if (use_mmap) { +            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { +                if (mode == O_RDWR) { +                    pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode."); +                    mode = O_WRONLY; +                    goto go_on; +                } else { +                    pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno)); +                    u->use_mmap = (use_mmap = FALSE); +                    u->out_mmap = NULL; +                } +            } else { +                pa_log_debug("Successfully mmap()ed output buffer."); +                pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss); +            } +        } +          if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))              namereg_fail = 1;          else { -            name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p)); +            name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));              namereg_fail = 0;          } -        if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map))) +        u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map); +        pa_xfree(name_buf); +        if (!u->sink) { +            pa_log("Failed to create sink object");              goto fail; +        } -        u->sink->get_latency = sink_get_latency_cb; -        u->sink->get_hw_volume = sink_get_hw_volume; -        u->sink->set_hw_volume = sink_set_hw_volume; +        u->sink->parent.process_msg = sink_process_msg;          u->sink->userdata = u; -        pa_sink_set_owner(u->sink, m); -        pa_sink_set_description(u->sink, t = pa_sprintf_malloc("OSS PCM on %s%s%s%s", -                                                           p, -                                                           hwdesc[0] ? " (" : "", -                                                           hwdesc[0] ? hwdesc : "", -                                                           hwdesc[0] ? ")" : "")); + +        pa_sink_set_module(u->sink, m); +        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +        pa_sink_set_rtpoll(u->sink, u->rtpoll); +        pa_sink_set_description(u->sink, t = pa_sprintf_malloc( +                                        "OSS PCM on %s%s%s%s%s", +                                        dev, +                                        hwdesc[0] ? " (" : "", +                                        hwdesc[0] ? hwdesc : "", +                                        hwdesc[0] ? ")" : "", +                                        use_mmap ? " via DMA" : ""));          pa_xfree(t); -        u->sink->is_hardware = 1; -    } else -        u->sink = NULL; +        u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY; +        u->sink->refresh_volume = TRUE; -    pa_xfree(name_buf); -    name_buf = NULL; +        if (use_mmap) +            u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags); +    } -    assert(u->source || u->sink); +    if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) { +        int do_close = 1; +        u->mixer_devmask = 0; -    u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : -1); -    assert(u->io); -    pa_iochannel_set_callback(u->io, io_callback, u); -    u->fd = fd; +        if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0) +            pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno)); -    u->memchunk.memblock = NULL; -    u->memchunk.length = 0; -    u->sample_size = pa_frame_size(&ss); +        else { +            if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) { +                pa_log_debug("Found hardware mixer track for playback."); +                u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; +                u->sink->get_volume = sink_get_volume; +                u->sink->set_volume = sink_set_volume; +                do_close = 0; +            } -    u->out_fragment_size = out_frag_size; -    u->in_fragment_size = in_frag_size; -    u->silence.memblock = pa_memblock_new(u->core->mempool, u->silence.length = u->out_fragment_size); -    assert(u->silence.memblock); -    pa_silence_memblock(u->silence.memblock, &ss); -    u->silence.index = 0; +            if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) { +                pa_log_debug("Found hardware mixer track for recording."); +                u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; +                u->source->get_volume = source_get_volume; +                u->source->set_volume = source_set_volume; +                do_close = 0; +            } +        } -    u->module = m; -    m->userdata = u; +        if (do_close) { +            pa_close(u->mixer_fd); +            u->mixer_fd = -1; +        } +    } -    pa_modargs_free(ma); +go_on: + +    pa_assert(u->source || u->sink); -    /* -     * Some crappy drivers do not start the recording until we read something. -     * Without this snippet, poll will never register the fd as ready. -     */ -    if (u->source) { -        char *buf = pa_xnew(char, u->sample_size); -        pa_read(u->fd, buf, u->sample_size, NULL); -        pa_xfree(buf); +    pa_memchunk_reset(&u->memchunk); + +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail;      }      /* Read mixer settings */ -    if (u->source) -        source_get_hw_volume(u->source); +    if (u->sink && u->sink->get_volume) +        sink_get_volume(u->sink); +    if (u->source && u->source->get_volume) +        source_get_volume(u->source); +      if (u->sink) -        sink_get_hw_volume(u->sink); +        pa_sink_put(u->sink); +    if (u->source) +        pa_source_put(u->source); + +    pa_modargs_free(ma);      return 0;  fail: -    if (fd >= 0) -        close(fd); + +    if (u) +        pa__done(m); +    else if (fd >= 0) +        pa_close(fd);      if (ma)          pa_modargs_free(ma); -    pa_xfree(name_buf); -      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(u = m->userdata))          return; -    clear_up(u); +    if (u->sink) +        pa_sink_unlink(u->sink); + +    if (u->source) +        pa_source_unlink(u->source); + +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread); +    } + +    pa_thread_mq_done(&u->thread_mq); + +    if (u->sink) +        pa_sink_unref(u->sink); + +    if (u->source) +        pa_source_unref(u->source);      if (u->memchunk.memblock)          pa_memblock_unref(u->memchunk.memblock); -    if (u->silence.memblock) -        pa_memblock_unref(u->silence.memblock); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->out_mmap_memblocks) { +        unsigned i; +        for (i = 0; i < u->out_nfrags; i++) +            if (u->out_mmap_memblocks[i]) +                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]); +        pa_xfree(u->out_mmap_memblocks); +    } + +    if (u->in_mmap_memblocks) { +        unsigned i; +        for (i = 0; i < u->in_nfrags; i++) +            if (u->in_mmap_memblocks[i]) +                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]); +        pa_xfree(u->in_mmap_memblocks); +    } + +    if (u->in_mmap && u->in_mmap != MAP_FAILED) +        munmap(u->in_mmap, u->in_hwbuf_size); + +    if (u->out_mmap && u->out_mmap != MAP_FAILED) +        munmap(u->out_mmap, u->out_hwbuf_size); + +    if (u->fd >= 0) +        pa_close(u->fd); + +    if (u->mixer_fd >= 0) +        pa_close(u->mixer_fd); + +    pa_xfree(u->device_name);      pa_xfree(u);  } diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 170b046e..75748474 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -28,22 +28,25 @@  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <limits.h> +#include <sys/ioctl.h> +#include <poll.h>  #include <pulse/xmalloc.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/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h>  #include "module-pipe-sink-symdef.h" @@ -58,20 +61,24 @@ PA_MODULE_USAGE(          "rate=<sample rate>"          "channel_map=<channel map>") -#define DEFAULT_FIFO_NAME "/tmp/music.output" +#define DEFAULT_FILE_NAME "/tmp/music.output"  #define DEFAULT_SINK_NAME "fifo_output"  struct userdata {      pa_core *core; +    pa_module *module; +    pa_sink *sink; -    char *filename; +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; -    pa_sink *sink; -    pa_iochannel *io; -    pa_defer_event *defer_event; +    char *filename; +    int fd;      pa_memchunk memchunk; -    pa_module *module; + +    pa_rtpoll_item *rtpoll_item;  };  static const char* const valid_modargs[] = { @@ -84,133 +91,191 @@ static const char* const valid_modargs[] = {      NULL  }; -static void do_write(struct userdata *u) { -    ssize_t r; -    assert(u); +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; -    u->core->mainloop->defer_enable(u->defer_event, 0); +    switch (code) { -    if (!pa_iochannel_is_writable(u->io)) -        return; +        case PA_SINK_MESSAGE_GET_LATENCY: { +            size_t n = 0; +            int l; -    pa_module_set_used(u->module, pa_sink_used_by(u->sink)); - -    if (!u->memchunk.length) -        if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) -            return; +#ifdef TIOCINQ +            if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0) +                n = (size_t) l; +#endif -    assert(u->memchunk.memblock && u->memchunk.length); +            n += u->memchunk.length; -    if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { -        pa_log("write(): %s", pa_cstrerror(errno)); -        return; +            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); +            break; +        }      } -    u->memchunk.index += r; -    u->memchunk.length -= r; - -    if (u->memchunk.length <= 0) { -        pa_memblock_unref(u->memchunk.memblock); -        u->memchunk.memblock = NULL; -    } +    return pa_sink_process_msg(o, code, data, offset, chunk);  } -static void notify_cb(pa_sink*s) { -    struct userdata *u = s->userdata; -    assert(s && u); +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    int write_type = 0; -    if (pa_iochannel_is_writable(u->io)) -        u->core->mainloop->defer_enable(u->defer_event, 1); -} +    pa_assert(u); -static pa_usec_t get_latency_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    assert(s && u); +    pa_log_debug("Thread starting up"); -    return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0; -} +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); -static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_write(u); -} +    for (;;) { +        struct pollfd *pollfd; +        int ret; -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_write(u); +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +        /* Render some data and write it to the fifo */ +        if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) { +            ssize_t l; +            void *p; + +            if (u->memchunk.length <= 0) +                pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); + +            pa_assert(u->memchunk.length > 0); + +            p = pa_memblock_acquire(u->memchunk.memblock); +            l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); +            pa_memblock_release(u->memchunk.memblock); + +            pa_assert(l != 0); + +            if (l < 0) { + +                if (errno == EINTR) +                    continue; +                else if (errno != EAGAIN) { +                    pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); +                    goto fail; +                } + +            } else { + +                u->memchunk.index += l; +                u->memchunk.length -= l; + +                if (u->memchunk.length <= 0) { +                    pa_memblock_unref(u->memchunk.memblock); +                    pa_memchunk_reset(&u->memchunk); +                } + +                pollfd->revents = 0; +            } +        } + +        /* Hmm, nothing to do. Let's sleep */ +        pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0; + +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; + +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +        if (pollfd->revents & ~POLLOUT) { +            pa_log("FIFO shutdown."); +            goto fail; +        } +    } + +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: +    pa_log_debug("Thread shutting down");  } -int pa__init(pa_core *c, pa_module*m) { -    struct userdata *u = NULL; +int pa__init(pa_module*m) { +    struct userdata *u;      struct stat st; -    const char *p; -    int fd = -1;      pa_sample_spec ss;      pa_channel_map map; -    pa_modargs *ma = NULL; +    pa_modargs *ma;      char *t; +    struct pollfd *pollfd; -    assert(c && m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("Failed to parse module arguments.");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { -        pa_log("invalid sample format specification"); +        pa_log("Invalid sample format specification or channel map");          goto fail;      } -    mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    pa_memchunk_reset(&u->memchunk); +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); -    if ((fd = open(p, O_RDWR)) < 0) { -        pa_log("open('%s'): %s", p, pa_cstrerror(errno)); +    u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); + +    mkfifo(u->filename, 0666); +    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { +        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));          goto fail;      } -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(u->fd); +    pa_make_fd_nonblock(u->fd); -    if (fstat(fd, &st) < 0) { -        pa_log("fstat('%s'): %s", p, pa_cstrerror(errno)); +    if (fstat(u->fd, &st) < 0) { +        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));          goto fail;      }      if (!S_ISFIFO(st.st_mode)) { -        pa_log("'%s' is not a FIFO.", p); +        pa_log("'%s' is not a FIFO.", u->filename);          goto fail;      } -    u = pa_xmalloc0(sizeof(struct userdata)); -    u->filename = pa_xstrdup(p); -    u->core = c; -    u->module = m; -    m->userdata = u; - -    if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { -        pa_log("failed to create sink."); +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { +        pa_log("Failed to create sink.");          goto fail;      } -    u->sink->notify = notify_cb; -    u->sink->get_latency = get_latency_cb; + +    u->sink->parent.process_msg = sink_process_msg;      u->sink->userdata = u; -    pa_sink_set_owner(u->sink, m); -    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", p)); +    u->sink->flags = PA_SINK_LATENCY; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", u->filename));      pa_xfree(t); -    u->io = pa_iochannel_new(c->mainloop, -1, fd); -    assert(u->io); -    pa_iochannel_set_callback(u->io, io_callback, u); +    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 = pollfd->revents = 0; -    u->memchunk.memblock = NULL; -    u->memchunk.length = 0; +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    } -    u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); -    assert(u->defer_event); -    c->mainloop->defer_enable(u->defer_event, 0); +    pa_sink_put(u->sink);      pa_modargs_free(ma); @@ -220,32 +285,48 @@ fail:      if (ma)          pa_modargs_free(ma); -    if (fd >= 0) -        close(fd); - -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); + +    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->memchunk.memblock) -        pa_memblock_unref(u->memchunk.memblock); +       pa_memblock_unref(u->memchunk.memblock); -    pa_sink_disconnect(u->sink); -    pa_sink_unref(u->sink); -    pa_iochannel_free(u->io); -    u->core->mainloop->defer_free(u->defer_event); +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->filename) { +        unlink(u->filename); +        pa_xfree(u->filename); +    } -    assert(u->filename); -    unlink(u->filename); -    pa_xfree(u->filename); +    if (u->fd >= 0) +        pa_assert_se(pa_close(u->fd) == 0);      pa_xfree(u);  } diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 56c721b0..45708c68 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -28,22 +28,24 @@  #include <stdlib.h>  #include <sys/stat.h>  #include <stdio.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <limits.h> +#include <sys/poll.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-error.h> -#include <pulsecore/iochannel.h>  #include <pulsecore/source.h>  #include <pulsecore/module.h>  #include <pulsecore/core-util.h>  #include <pulsecore/modargs.h>  #include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h>  #include "module-pipe-source-symdef.h" @@ -58,18 +60,24 @@ PA_MODULE_USAGE(          "rate=<sample rate> "          "channel_map=<channel map>") -#define DEFAULT_FIFO_NAME "/tmp/music.input" +#define DEFAULT_FILE_NAME "/tmp/music.input"  #define DEFAULT_SOURCE_NAME "fifo_input"  struct userdata {      pa_core *core; +    pa_module *module; +    pa_source *source; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll;      char *filename; +    int fd; -    pa_source *source; -    pa_iochannel *io; -    pa_module *module; -    pa_memchunk chunk; +    pa_memchunk memchunk; + +    pa_rtpoll_item *rtpoll_item;  };  static const char* const valid_modargs[] = { @@ -82,109 +90,168 @@ static const char* const valid_modargs[] = {      NULL  }; -static void do_read(struct userdata *u) { -    ssize_t r; -    pa_memchunk chunk; -    assert(u); +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    int read_type = 0; -    if (!pa_iochannel_is_readable(u->io)) -        return; +    pa_assert(u); -    pa_module_set_used(u->module, pa_idxset_size(u->source->outputs)); +    pa_log_debug("Thread starting up"); -    if (!u->chunk.memblock) { -        u->chunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF); -        u->chunk.index = chunk.length = 0; -    } +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); -    assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index); -    if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) { -        pa_log("read(): %s", pa_cstrerror(errno)); -        return; -    } +    for (;;) { +        int ret; +        struct pollfd *pollfd; + +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +        /* Try to read some data and pass it on to the source driver */ +        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) { +            ssize_t l; +            void *p; -    u->chunk.length = r; -    pa_source_post(u->source, &u->chunk); -    u->chunk.index += r; +            if (!u->memchunk.memblock) { +                u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF); +                u->memchunk.index = u->memchunk.length = 0; +            } -    if (u->chunk.index >= u->chunk.memblock->length) { -        u->chunk.index = u->chunk.length = 0; -        pa_memblock_unref(u->chunk.memblock); -        u->chunk.memblock = NULL; +            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index); + +            p = pa_memblock_acquire(u->memchunk.memblock); +            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type); +            pa_memblock_release(u->memchunk.memblock); + +            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */ + +            if (l < 0) { + +                if (errno == EINTR) +                    continue; +                else if (errno != EAGAIN) { +                    pa_log("Faile to read data from FIFO: %s", pa_cstrerror(errno)); +                    goto fail; +                } + +            } else { + +                u->memchunk.length = l; +                pa_source_post(u->source, &u->memchunk); +                u->memchunk.index += l; + +                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) { +                    pa_memblock_unref(u->memchunk.memblock); +                    pa_memchunk_reset(&u->memchunk); +                } + +                pollfd->revents = 0; +            } +        } + +        /* Hmm, nothing to do. Let's sleep */ +        pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0; + +        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; + +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); +        if (pollfd->revents & ~POLLIN) { +            pa_log("FIFO shutdown."); +            goto fail; +        }      } -} -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_read(u); +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: +    pa_log_debug("Thread shutting down");  } -int pa__init(pa_core *c, pa_module*m) { -    struct userdata *u = NULL; +int pa__init(pa_module*m) { +    struct userdata *u;      struct stat st; -    const char *p; -    int fd = -1;      pa_sample_spec ss;      pa_channel_map map; -    pa_modargs *ma = NULL; +    pa_modargs *ma;      char *t; +    struct pollfd *pollfd; -    assert(c && m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("failed to parse module arguments.");          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {          pa_log("invalid sample format specification or channel map");          goto fail;      } -    mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    pa_memchunk_reset(&u->memchunk); +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + +    u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); -    if ((fd = open(p, O_RDWR)) < 0) { -        pa_log("open('%s'): %s", p, pa_cstrerror(errno)); +    mkfifo(u->filename, 0666); +    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { +        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));          goto fail;      } -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(u->fd); +    pa_make_fd_nonblock(u->fd); -    if (fstat(fd, &st) < 0) { -        pa_log("fstat('%s'): %s", p, pa_cstrerror(errno)); +    if (fstat(u->fd, &st) < 0) { +        pa_log("fstat('%s'): %s",u->filename, pa_cstrerror(errno));          goto fail;      }      if (!S_ISFIFO(st.st_mode)) { -        pa_log("'%s' is not a FIFO.", p); +        pa_log("'%s' is not a FIFO.", u->filename);          goto fail;      } -    u = pa_xmalloc0(sizeof(struct userdata)); - -    u->filename = pa_xstrdup(p); -    u->core = c; - -    if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) { -        pa_log("failed to create source."); +    if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) { +        pa_log("Failed to create source.");          goto fail;      } +      u->source->userdata = u; -    pa_source_set_owner(u->source, m); -    pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", p)); +    u->source->flags = 0; + +    pa_source_set_module(u->source, m); +    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +    pa_source_set_rtpoll(u->source, u->rtpoll); +    pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", u->filename));      pa_xfree(t); -    u->io = pa_iochannel_new(c->mainloop, fd, -1); -    assert(u->io); -    pa_iochannel_set_callback(u->io, io_callback, u); +    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 = pollfd->revents = 0; -    u->chunk.memblock = NULL; -    u->chunk.index = u->chunk.length = 0; +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    } -    u->module = m; -    m->userdata = u; +    pa_source_put(u->source);      pa_modargs_free(ma); @@ -194,31 +261,48 @@ fail:      if (ma)          pa_modargs_free(ma); -    if (fd >= 0) -        close(fd); - -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; -    if (u->chunk.memblock) -        pa_memblock_unref(u->chunk.memblock); +    if (u->source) +        pa_source_unlink(u->source); + +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread); +    } + +    pa_thread_mq_done(&u->thread_mq); + +    if (u->source) +        pa_source_unref(u->source); + +    if (u->memchunk.memblock) +        pa_memblock_unref(u->memchunk.memblock); -    pa_source_disconnect(u->source); -    pa_source_unref(u->source); -    pa_iochannel_free(u->io); +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->filename) { +        unlink(u->filename); +        pa_xfree(u->filename); +    } -    assert(u->filename); -    unlink(u->filename); -    pa_xfree(u->filename); +    if (u->fd >= 0) +        pa_assert_se(pa_close(u->fd) == 0);      pa_xfree(u);  } diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 5c8733fb..6bd78079 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -29,7 +29,6 @@  #include <string.h>  #include <errno.h>  #include <stdio.h> -#include <assert.h>  #include <unistd.h>  #include <limits.h> @@ -43,10 +42,9 @@  #include <netinet/in.h>  #endif -#include "../pulsecore/winsock.h" -  #include <pulse/xmalloc.h> +#include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/module.h>  #include <pulsecore/socket-server.h> @@ -154,7 +152,6 @@    #define protocol_free pa_protocol_esound_free    #define TCPWRAP_SERVICE "esound"    #define IPV4_PORT ESD_DEFAULT_PORT -  #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME    #define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie",    #ifdef USE_TCP_SOCKETS      #include "module-esound-protocol-tcp-symdef.h" @@ -205,10 +202,9 @@ struct userdata {  #endif  }; -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      int ret = -1; -      struct userdata *u = NULL;  #if defined(USE_TCP_SOCKETS) @@ -219,9 +215,13 @@ int pa__init(pa_core *c, pa_module*m) {      pa_socket_server *s;      int r;      char tmp[PATH_MAX]; + +#if defined(USE_PROTOCOL_ESOUND) +    char tmp2[PATH_MAX]; +#endif  #endif -    assert(c && m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments"); @@ -239,22 +239,22 @@ int pa__init(pa_core *c, pa_module*m) {      listen_on = pa_modargs_get_value(ma, "listen", NULL);      if (listen_on) { -        s_ipv6 = pa_socket_server_new_ipv6_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE); -        s_ipv4 = pa_socket_server_new_ipv4_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE); +        s_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE); +        s_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);      } else { -        s_ipv6 = pa_socket_server_new_ipv6_any(c->mainloop, port, TCPWRAP_SERVICE); -        s_ipv4 = pa_socket_server_new_ipv4_any(c->mainloop, port, TCPWRAP_SERVICE); +        s_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, port, TCPWRAP_SERVICE); +        s_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, port, TCPWRAP_SERVICE);      }      if (!s_ipv4 && !s_ipv6)          goto fail;      if (s_ipv4) -        if (!(u->protocol_ipv4 = protocol_new(c, s_ipv4, m, ma))) +        if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))              pa_socket_server_unref(s_ipv4);      if (s_ipv6) -        if (!(u->protocol_ipv6 = protocol_new(c, s_ipv6, m, ma))) +        if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))              pa_socket_server_unref(s_ipv6);      if (!u->protocol_ipv4 && !u->protocol_ipv6) @@ -262,18 +262,23 @@ int pa__init(pa_core *c, pa_module*m) {  #else -    pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp)); -    u->socket_path = pa_xstrdup(tmp); -  #if defined(USE_PROTOCOL_ESOUND) +    snprintf(tmp2, sizeof(tmp2), "/tmp/.esd-%lu/socket", (unsigned long) getuid()); +    pa_runtime_path(pa_modargs_get_value(ma, "socket", tmp2), tmp, sizeof(tmp)); +    u->socket_path = pa_xstrdup(tmp); +      /* 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, c->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) { +    if (pa_make_secure_parent_dir(u->socket_path, m->core->is_system_instance ? 0755 : 0700, (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;      } + +#else +    pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp)); +    u->socket_path = pa_xstrdup(tmp);  #endif      if ((r = pa_unix_socket_remove_stale(tmp)) < 0) { @@ -284,10 +289,10 @@ int pa__init(pa_core *c, pa_module*m) {      if (r)          pa_log("Removed stale UNIX socket '%s'.", tmp); -    if (!(s = pa_socket_server_new_unix(c->mainloop, tmp))) +    if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))          goto fail; -    if (!(u->protocol_unix = protocol_new(c, s, m, ma))) +    if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))          goto fail;  #endif @@ -333,11 +338,10 @@ fail:      goto finish;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      u = m->userdata; @@ -358,7 +362,6 @@ void pa__done(pa_core *c, pa_module*m) {      }  #endif -      pa_xfree(u->socket_path);  #endif diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c new file mode 100644 index 00000000..e863c0c3 --- /dev/null +++ b/src/modules/module-remap-sink.c @@ -0,0 +1,334 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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/core-error.h> +#include <pulsecore/namereg.h> +#include <pulsecore/sink.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> + +#include "module-remap-sink-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Virtual channel remapping sink") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE( +        "sink_name=<name for the sink> " +        "master=<name of sink to remap> " +        "master_channel_map=<channel map> " +        "format=<sample format> " +        "channels=<number of channels> " +        "rate=<sample rate> " +        "channel_map=<channel map>") + +struct userdata { +    pa_core *core; +    pa_module *module; + +    pa_sink *sink, *master; +    pa_sink_input *sink_input; + +    pa_memchunk memchunk; +}; + +static const char* const valid_modargs[] = { +    "sink_name", +    "master", +    "master_channel_map", +    "rate", +    "format", +    "channels", +    "channel_map", +    NULL +}; + +/* Called from I/O thread context */ +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_GET_LATENCY: { +            pa_usec_t usec = 0; + +            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) +                usec = 0; + +            *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); +            return 0; +        } +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk); +} + +/* Called from main context */ +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +    struct userdata *u; + +    pa_sink_assert_ref(s); +    pa_assert_se(u = s->userdata); + +    if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input))) +        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + +    return 0; +} + +/* Called from I/O thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SINK_INPUT(o)->userdata; + +    switch (code) { +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: +            *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +    } + +    return pa_sink_input_process_msg(o, code, data, offset, chunk); +} + +/* Called from I/O thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    if (!u->memchunk.memblock) +        pa_sink_render(u->sink, length, &u->memchunk); + +    pa_assert(u->memchunk.memblock); +    *chunk = u->memchunk; +    pa_memblock_ref(chunk->memblock); +    return 0; +} + +/* Called from I/O thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); +    pa_assert(length > 0); + +    if (u->memchunk.memblock) { + +        if (length < u->memchunk.length) { +            u->memchunk.index += length; +            u->memchunk.length -= length; +            return; +        } + +        pa_memblock_unref(u->memchunk.memblock); +        length -= u->memchunk.length; +        pa_memchunk_reset(&u->memchunk); +    } + +    if (length > 0) +        pa_sink_skip(u->sink, length); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_detach_within_thread(u->sink); +} + +/* Called from I/O thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); +    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + +    pa_sink_attach_within_thread(u->sink); +} + +/* Called from main context */ +static void sink_input_kill_cb(pa_sink_input *i) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    pa_sink_input_unlink(u->sink_input); +    pa_sink_input_unref(u->sink_input); +    u->sink_input = NULL; + +    pa_sink_unlink(u->sink); +    pa_sink_unref(u->sink); +    u->sink = NULL; + +    pa_module_unload_request(u->module); +} + +int pa__init(pa_module*m) { +    struct userdata *u; +    pa_sample_spec ss; +    pa_channel_map sink_map, stream_map; +    pa_modargs *ma; +    char *t; +    pa_sink *master; +    pa_sink_input_new_data data; +    char *default_sink_name = NULL; + +    pa_assert(m); + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments."); +        goto fail; +    } + +    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) { +        pa_log("Master sink not found"); +        goto fail; +    } + +    ss = master->sample_spec; +    sink_map = master->channel_map; +    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &sink_map, PA_CHANNEL_MAP_DEFAULT) < 0) { +        pa_log("Invalid sample format specification or channel map"); +        goto fail; +    } + +    stream_map = sink_map; +    if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) { +        pa_log("Invalid master hannel map"); +        goto fail; +    } + +    if (stream_map.channels != ss.channels) { +        pa_log("Number of channels doesn't match"); +        goto fail; +    } + +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    u->master = master; +    pa_memchunk_reset(&u->memchunk); + +    default_sink_name = pa_sprintf_malloc("%s.remapped", master->name); + +    /* Create sink */ +    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &sink_map))) { +        pa_log("Failed to create sink."); +        goto fail; +    } + +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->set_state = sink_set_state; +    u->sink->userdata = u; +    u->sink->flags = PA_SINK_LATENCY; + +    pa_sink_set_module(u->sink, m); +    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Remapped %s", master->description)); +    pa_xfree(t); +    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); +    pa_sink_set_rtpoll(u->sink, master->rtpoll); + +    /* Create sink input */ +    pa_sink_input_new_data_init(&data); +    data.sink = u->master; +    data.driver = __FILE__; +    data.name = "Remapped Stream"; +    pa_sink_input_new_data_set_sample_spec(&data, &ss); +    pa_sink_input_new_data_set_channel_map(&data, &stream_map); +    data.module = m; + +    if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE))) +        goto fail; + +    u->sink_input->parent.process_msg = sink_input_process_msg; +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb; +    u->sink_input->attach = sink_input_attach_cb; +    u->sink_input->detach = sink_input_detach_cb; +    u->sink_input->userdata = u; + +    pa_sink_put(u->sink); +    pa_sink_input_put(u->sink_input); + +    pa_modargs_free(ma); +    pa_xfree(default_sink_name); + +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    pa__done(m); + +    pa_xfree(default_sink_name); + +    return -1; +} + +void pa__done(pa_module*m) { +    struct userdata *u; + +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->sink_input) { +        pa_sink_input_unlink(u->sink_input); +        pa_sink_input_unref(u->sink_input); +    } + +    if (u->sink) { +        pa_sink_unlink(u->sink); +        pa_sink_unref(u->sink); +    } + +    if (u->memchunk.memblock) +        pa_memblock_unref(u->memchunk.memblock); + +    pa_xfree(u); +} diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index 25005f25..5cabef76 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -52,20 +52,26 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user      pa_sink_input *i;      pa_sink *target; -    assert(c); -    assert(sink); +    pa_assert(c); +    pa_assert(sink);      if (!pa_idxset_size(sink->inputs)) {          pa_log_debug("No sink inputs to move away.");          return PA_HOOK_OK;      } -    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0))) { -        pa_log_info("No evacuation sink found."); -        return PA_HOOK_OK; -    } +    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) { +        uint32_t idx; + +        for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx)) +            if (target != sink) +                break; -    assert(target != sink); +        if (!target) { +            pa_log_info("No evacuation sink found."); +            return PA_HOOK_OK; +        } +    }      while ((i = pa_idxset_first(sink->inputs, NULL))) {          if (pa_sink_input_move_to(i, target, 1) < 0) { @@ -84,20 +90,28 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void      pa_source_output *o;      pa_source *target; -    assert(c); -    assert(source); +    pa_assert(c); +    pa_assert(source);      if (!pa_idxset_size(source->outputs)) {          pa_log_debug("No source outputs to move away.");          return PA_HOOK_OK;      } -    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0))) { -        pa_log_info("No evacuation source found."); -        return PA_HOOK_OK; +    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) { +        uint32_t idx; + +        for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx)) +            if (target != source && !target->monitor_of == !source->monitor_of) +                break; + +        if (!target) { +            pa_log_info("No evacuation source found."); +            return PA_HOOK_OK; +        }      } -    assert(target != source); +    pa_assert(target != source);      while ((o = pa_idxset_first(source->outputs, NULL))) {          if (pa_source_output_move_to(o, target) < 0) { @@ -112,12 +126,11 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void      return PA_HOOK_OK;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments"); @@ -125,18 +138,17 @@ int pa__init(pa_core *c, pa_module*m) {      }      m->userdata = u = pa_xnew(struct userdata, 1); -    u->sink_slot = pa_hook_connect(&c->hook_sink_disconnect, (pa_hook_cb_t) sink_hook_callback, NULL); -    u->source_slot = pa_hook_connect(&c->hook_source_disconnect, (pa_hook_cb_t) source_hook_callback, NULL); +    u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_hook_callback, NULL); +    u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) source_hook_callback, NULL);      pa_modargs_free(ma);      return 0;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!m->userdata)          return; diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 661455b3..65b8ee7f 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <math.h>  #include <pulse/xmalloc.h> @@ -36,6 +35,7 @@  #include <pulsecore/modargs.h>  #include <pulsecore/namereg.h>  #include <pulsecore/log.h> +#include <pulsecore/core-util.h>  #include "module-sine-symdef.h" @@ -58,36 +58,46 @@ static const char* const valid_modargs[] = {      NULL,  }; -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {      struct userdata *u; -    assert(i && chunk && i->userdata); + +    pa_assert(i);      u = i->userdata; +    pa_assert(u); +    pa_assert(chunk);      chunk->memblock = pa_memblock_ref(u->memblock);      chunk->index = u->peek_index; -    chunk->length = u->memblock->length - u->peek_index; +    chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index; +      return 0;  } -static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { +static void sink_input_drop_cb(pa_sink_input *i, size_t length) {      struct userdata *u; -    assert(i && chunk && length && i->userdata); -    u = i->userdata; +    size_t l; -    assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index); +    pa_assert(i); +    u = i->userdata; +    pa_assert(u); +    pa_assert(length > 0);      u->peek_index += length; -    if (u->peek_index >= u->memblock->length) -        u->peek_index = 0; +    l = pa_memblock_get_length(u->memblock); + +    while (u->peek_index >= l) +        u->peek_index -= l;  } -static void sink_input_kill(pa_sink_input *i) { +static void sink_input_kill_cb(pa_sink_input *i) {      struct userdata *u; -    assert(i && i->userdata); + +    pa_assert(i);      u = i->userdata; +    pa_assert(u); -    pa_sink_input_disconnect(u->sink_input); +    pa_sink_input_unlink(u->sink_input);      pa_sink_input_unref(u->sink_input);      u->sink_input = NULL; @@ -103,14 +113,14 @@ static void calc_sine(float *f, size_t l, float freq) {          f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u;      pa_sink *sink; -    const char *sink_name;      pa_sample_spec ss;      uint32_t frequency;      char t[256]; +    void *p;      pa_sink_input_new_data data;      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { @@ -118,15 +128,14 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    m->userdata = u = pa_xmalloc(sizeof(struct userdata)); -    u->core = c; +    m->userdata = u = pa_xnew0(struct userdata, 1); +    u->core = m->core;      u->module = m;      u->sink_input = NULL;      u->memblock = NULL; +    u->peek_index = 0; -    sink_name = pa_modargs_get_value(ma, "sink", NULL); - -    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { +    if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, 1))) {          pa_log("No such sink.");          goto fail;      } @@ -141,10 +150,12 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    u->memblock = pa_memblock_new(c->mempool, pa_bytes_per_second(&ss)); -    calc_sine(u->memblock->data, u->memblock->length, frequency); +    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); +    pa_memblock_release(u->memblock); -    snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); +    pa_snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);      pa_sink_input_new_data_init(&data);      data.sink = sink; @@ -153,15 +164,15 @@ int pa__init(pa_core *c, pa_module*m) {      pa_sink_input_new_data_set_sample_spec(&data, &ss);      data.module = m; -    if (!(u->sink_input = pa_sink_input_new(c, &data, 0))) +    if (!(u->sink_input = pa_sink_input_new(m->core, &data, 0)))          goto fail; -    u->sink_input->peek = sink_input_peek; -    u->sink_input->drop = sink_input_drop; -    u->sink_input->kill = sink_input_kill; +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb;      u->sink_input->userdata = u; -    u->peek_index = 0; +    pa_sink_input_put(u->sink_input);      pa_modargs_free(ma);      return 0; @@ -170,24 +181,26 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { -    struct userdata *u = m->userdata; -    assert(c && m); +void pa__done(pa_module*m) { +    struct userdata *u; + +    pa_assert(m); -    if (!u) +    if (!(u = m->userdata))          return;      if (u->sink_input) { -        pa_sink_input_disconnect(u->sink_input); +        pa_sink_input_unlink(u->sink_input);          pa_sink_input_unref(u->sink_input);      }      if (u->memblock)          pa_memblock_unref(u->memblock); +      pa_xfree(u);  } diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index a50f1ecf..a8a94712 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -4,7 +4,7 @@    This file is part of PulseAudio.    Copyright 2006 Lennart Poettering -  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB +  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB    PulseAudio is free software; you can redistribute it and/or modify    it under the terms of the GNU Lesser General Public License as published @@ -57,6 +57,9 @@  #include <pulsecore/modargs.h>  #include <pulsecore/log.h>  #include <pulsecore/core-error.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/thread.h>  #include "module-solaris-symdef.h" @@ -75,12 +78,14 @@ PA_MODULE_USAGE(      "channel_map=<channel map>")  struct userdata { +    pa_core *core;      pa_sink *sink;      pa_source *source; -    pa_iochannel *io; -    pa_core *core; -    pa_time_event *timer; -    pa_usec_t poll_timeout; + +    pa_thread *thread; +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +      pa_signal_event *sig;      pa_memchunk memchunk; @@ -90,9 +95,9 @@ struct userdata {      uint32_t frame_size;      uint32_t buffer_size;      unsigned int written_bytes, read_bytes; -    int sink_underflow;      int fd; +    pa_rtpoll_item *rtpoll_item;      pa_module *module;  }; @@ -114,309 +119,357 @@ static const char* const valid_modargs[] = {  #define DEFAULT_SOURCE_NAME "solaris_input"  #define DEFAULT_DEVICE "/dev/audio" -#define CHUNK_SIZE 2048 - -static void update_usage(struct userdata *u) { -   pa_module_set_used(u->module, -                      (u->sink ? pa_sink_used_by(u->sink) : 0) + -                      (u->source ? pa_source_used_by(u->source) : 0)); -} - -static void do_write(struct userdata *u) { -    audio_info_t info; +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;      int err; -    size_t len; -    ssize_t r; +    audio_info_t info; -    assert(u); +    switch (code) { +    case PA_SINK_MESSAGE_GET_LATENCY: { +        pa_usec_t r = 0; -    /* We cannot check pa_iochannel_is_writable() because of our buffer hack */ -    if (!u->sink) -        return; +        if (u->fd >= 0) { -    update_usage(u); +            err = ioctl(u->fd, AUDIO_GETINFO, &info); +            pa_assert(err >= 0); -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +            r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec); +            r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec); -    /* -     * Since we cannot modify the size of the output buffer we fake it -     * by not filling it more than u->buffer_size. -     */ -    len = u->buffer_size; -    len -= u->written_bytes - (info.play.samples * u->frame_size); +            if (u->memchunk.memblock) +                r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec); +        } -    /* The sample counter can sometimes go backwards :( */ -    if (len > u->buffer_size) -        len = 0; +        *((pa_usec_t*) data) = r; -    if (!u->sink_underflow && (len == u->buffer_size)) -        pa_log_debug("Solaris buffer underflow!"); +        return 0; +    } -    len -= len % u->frame_size; +    case PA_SINK_MESSAGE_SET_VOLUME: +        if (u->fd >= 0) { +            AUDIO_INITINFO(&info); + +            info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; +            assert(info.play.gain <= AUDIO_MAX_GAIN); + +            if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { +                if (errno == EINVAL) +                    pa_log("AUDIO_SETINFO: Unsupported volume."); +                else +                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); +            } else { +                return 0; +            } +        } +        break; -    if (len == 0) -        return; +    case PA_SINK_MESSAGE_GET_VOLUME: +        if (u->fd >= 0) { +            err = ioctl(u->fd, AUDIO_GETINFO, &info); +            assert(err >= 0); -    if (!u->memchunk.length) { -        if (pa_sink_render(u->sink, len, &u->memchunk) < 0) { -            u->sink_underflow = 1; -            return; -        } -    } +            pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels, +                info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); -    u->sink_underflow = 0; +            return 0; +        } +        break; -    assert(u->memchunk.memblock); -    assert(u->memchunk.memblock->data); -    assert(u->memchunk.length); +    case PA_SINK_MESSAGE_SET_MUTE: +        if (u->fd >= 0) { +            AUDIO_INITINFO(&info); -    if (u->memchunk.length < len) { -        len = u->memchunk.length; -        len -= len % u->frame_size; -        assert(len); -    } +            info.output_muted = !!PA_PTR_TO_UINT(data); -    if ((r = pa_iochannel_write(u->io, -        (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, len)) < 0) { -        pa_log("write() failed: %s", pa_cstrerror(errno)); -        return; -    } +            if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) +                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); +            else +                return 0; +        } +        break; -    assert(r % u->frame_size == 0); +    case PA_SINK_MESSAGE_GET_MUTE: +        if (u->fd >= 0) { +            err = ioctl(u->fd, AUDIO_GETINFO, &info); +            pa_assert(err >= 0); -    u->memchunk.index += r; -    u->memchunk.length -= r; +            *(int*)data = !!info.output_muted; -    if (u->memchunk.length <= 0) { -        pa_memblock_unref(u->memchunk.memblock); -        u->memchunk.memblock = NULL; +            return 0; +        } +        break;      } -    u->written_bytes += r; +    return pa_sink_process_msg(o, code, data, offset, chunk);  } -static void do_read(struct userdata *u) { -    pa_memchunk memchunk; +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SOURCE(o)->userdata;      int err; -    size_t l; -    ssize_t r; -    assert(u); +    audio_info_t info; -    if (!u->source || !pa_iochannel_is_readable(u->io)) -        return; +    switch (code) { +        case PA_SOURCE_MESSAGE_GET_LATENCY: { +            pa_usec_t r = 0; -    update_usage(u); +            if (u->fd) { +                err = ioctl(u->fd, AUDIO_GETINFO, &info); +                pa_assert(err >= 0); -    err = ioctl(u->fd, I_NREAD, &l); -    assert(err >= 0); +                r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec); +                r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec); +            } -    /* This is to make sure it fits in the memory pool. Also, a page -       should be the most efficient transfer size. */ -    if (l > u->page_size) -        l = u->page_size; +            *((pa_usec_t*) data) = r; -    memchunk.memblock = pa_memblock_new(u->core->mempool, l); -    assert(memchunk.memblock); -    if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { -        pa_memblock_unref(memchunk.memblock); -        if (errno != EAGAIN) -            pa_log("read() failed: %s", pa_cstrerror(errno)); -        return; -    } +            return 0; +        } -    assert(r <= (ssize_t) memchunk.memblock->length); -    memchunk.length = memchunk.memblock->length = r; -    memchunk.index = 0; +        case PA_SOURCE_MESSAGE_SET_VOLUME: +            if (u->fd >= 0) { +                AUDIO_INITINFO(&info); + +                info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; +                assert(info.record.gain <= AUDIO_MAX_GAIN); + +                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { +                    if (errno == EINVAL) +                        pa_log("AUDIO_SETINFO: Unsupported volume."); +                    else +                        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); +                } else { +                    return 0; +                } +            } +            break; -    pa_source_post(u->source, &memchunk); -    pa_memblock_unref(memchunk.memblock); +        case PA_SOURCE_MESSAGE_GET_VOLUME: +            if (u->fd >= 0) { +                err = ioctl(u->fd, AUDIO_GETINFO, &info); +                pa_assert(err >= 0); -    u->read_bytes += r; -} +                pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels, +                    info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); -static void io_callback(pa_iochannel *io, void*userdata) { -    struct userdata *u = userdata; -    assert(u); -    do_write(u); -    do_read(u); -} +                return 0; +            } +            break; +    } -static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { -    struct userdata *u = userdata; -    struct timeval ntv; +    return pa_source_process_msg(o, code, data, offset, chunk); +} -    assert(u); +static void clear_underflow(struct userdata *u) +{ +    audio_info_t info; -    do_write(u); +    AUDIO_INITINFO(&info); -    pa_gettimeofday(&ntv); -    pa_timeval_add(&ntv, u->poll_timeout); +    info.play.error = 0; -    a->time_restart(e, &ntv); +    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) +        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));  } -static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) { -    struct userdata *u = userdata; -    pa_cvolume old_vol; +static void clear_overflow(struct userdata *u) +{ +    audio_info_t info; -    assert(u); +    AUDIO_INITINFO(&info); -    if (u->sink) { -        assert(u->sink->get_hw_volume); -        memcpy(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)); -        if (u->sink->get_hw_volume(u->sink) < 0) -            return; -        if (memcmp(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)) != 0) { -            pa_subscription_post(u->sink->core, -                PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, -                u->sink->index); -        } -    } +    info.record.error = 0; -    if (u->source) { -        assert(u->source->get_hw_volume); -        memcpy(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)); -        if (u->source->get_hw_volume(u->source) < 0) -            return; -        if (memcmp(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)) != 0) { -            pa_subscription_post(u->source->core, -                PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, -                u->source->index); -        } -    } +    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) +        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));  } -static pa_usec_t sink_get_latency_cb(pa_sink *s) { -    pa_usec_t r = 0; -    audio_info_t info; -    int err; -    struct userdata *u = s->userdata; -    assert(s && u && u->sink); +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    unsigned short revents = 0; +    int ret; -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +    pa_assert(u); -    r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec); -    r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &s->sample_spec); +    pa_log_debug("Thread starting up"); -    if (u->memchunk.memblock) -        r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); +    if (u->core->high_priority) +        pa_make_realtime(); -    return r; -} +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); -static pa_usec_t source_get_latency_cb(pa_source *s) { -    pa_usec_t r = 0; -    struct userdata *u = s->userdata; -    audio_info_t info; -    int err; -    assert(s && u && u->source); +    for (;;) { +        /* Render some data and write it to the dsp */ -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +        if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) { +            audio_info_t info; +            int err; +            size_t len; -    r += pa_bytes_to_usec(info.record.samples * u->frame_size, &s->sample_spec); -    r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec); +            err = ioctl(u->fd, AUDIO_GETINFO, &info); +            pa_assert(err >= 0); -    return r; -} +            /* +             * Since we cannot modify the size of the output buffer we fake it +             * by not filling it more than u->buffer_size. +             */ +            len = u->buffer_size; +            len -= u->written_bytes - (info.play.samples * u->frame_size); -static int sink_get_hw_volume_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; -    int err; +            /* The sample counter can sometimes go backwards :( */ +            if (len > u->buffer_size) +                len = 0; -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +            if (info.play.error) { +                pa_log_debug("Solaris buffer underflow!"); +                clear_underflow(u); +            } -    pa_cvolume_set(&s->hw_volume, s->hw_volume.channels, -        info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); +            len -= len % u->frame_size; -    return 0; -} +            while (len) { +                void *p; +                ssize_t r; -static int sink_set_hw_volume_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; +                if (!u->memchunk.length) +                    pa_sink_render(u->sink, len, &u->memchunk); -    AUDIO_INITINFO(&info); +                pa_assert(u->memchunk.length); -    info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; -    assert(info.play.gain <= AUDIO_MAX_GAIN); +                p = pa_memblock_acquire(u->memchunk.memblock); +                r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL); +                pa_memblock_release(u->memchunk.memblock); -    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { -        if (errno == EINVAL) -            pa_log("AUDIO_SETINFO: Unsupported volume."); -        else -            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); -        return -1; -    } +                if (r < 0) { +                    if (errno == EINTR) +                        continue; +                    else if (errno != EAGAIN) { +                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); +                        goto fail; +                    } +                } else { +                    pa_assert(r % u->frame_size == 0); -    return 0; -} +                    u->memchunk.index += r; +                    u->memchunk.length -= r; -static int sink_get_hw_mute_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; -    int err; +                    if (u->memchunk.length <= 0) { +                        pa_memblock_unref(u->memchunk.memblock); +                        pa_memchunk_reset(&u->memchunk); +                    } -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +                    len -= r; +                    u->written_bytes += r; +                } +            } +        } -    s->hw_muted = !!info.output_muted; +        /* Try to read some data and pass it on to the source driver */ + +        if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) { +            pa_memchunk memchunk; +            int err; +            size_t l; +            void *p; +            ssize_t r; +            audio_info_t info; + +            err = ioctl(u->fd, AUDIO_GETINFO, &info); +            pa_assert(err >= 0); + +            if (info.record.error) { +                pa_log_debug("Solaris buffer overflow!"); +                clear_overflow(u); +            } + +            err = ioctl(u->fd, I_NREAD, &l); +            pa_assert(err >= 0); + +            if (l > 0) { +                /* This is to make sure it fits in the memory pool. Also, a page +                   should be the most efficient transfer size. */ +                if (l > u->page_size) +                    l = u->page_size; + +                memchunk.memblock = pa_memblock_new(u->core->mempool, l); +                pa_assert(memchunk.memblock); + +                p = pa_memblock_acquire(memchunk.memblock); +                r = pa_read(u->fd, p, l, NULL); +                pa_memblock_release(memchunk.memblock); + +                if (r < 0) { +                    pa_memblock_unref(memchunk.memblock); +                    if (errno != EAGAIN) { +                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); +                        goto fail; +                    } +                } else { +                    memchunk.index = 0; +                    memchunk.length = r; + +                    pa_source_post(u->source, &memchunk); +                    pa_memblock_unref(memchunk.memblock); + +                    u->read_bytes += r; + +                    revents &= ~POLLIN; +                } +            } +        } -    return 0; -} +        if (u->fd >= 0) { +            struct pollfd *pollfd; -static int sink_set_hw_mute_cb(pa_sink *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); +            pollfd->events = +                ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0); +        } -    AUDIO_INITINFO(&info); +        /* Hmm, nothing to do. Let's sleep */ +        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +            goto fail; -    info.output_muted = !!s->hw_muted; +        if (ret == 0) +            goto finish; -    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { -        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); -        return -1; -    } +        if (u->fd >= 0) { +            struct pollfd *pollfd; -    return 0; -} +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); -static int source_get_hw_volume_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; -    int err; +            if (pollfd->revents & ~(POLLOUT|POLLIN)) { +                pa_log("DSP shutdown."); +                goto fail; +            } -    err = ioctl(u->fd, AUDIO_GETINFO, &info); -    assert(err >= 0); +            revents = pollfd->revents; +        } else +            revents = 0; +    } -    pa_cvolume_set(&s->hw_volume, s->hw_volume.channels, -        info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); +fail: +    /* We have to continue processing messages until we receive the +     * SHUTDOWN message */ +    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); -    return 0; +finish: +    pa_log_debug("Thread shutting down");  } -static int source_set_hw_volume_cb(pa_source *s) { -    struct userdata *u = s->userdata; -    audio_info_t info; - -    AUDIO_INITINFO(&info); +static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) { +    struct userdata *u = userdata; -    info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; -    assert(info.record.gain <= AUDIO_MAX_GAIN); +    assert(u); -    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { -        if (errno == EINVAL) -            pa_log("AUDIO_SETINFO: Unsupported volume."); -        else -            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); -        return -1; +    if (u->sink) { +        pa_sink_get_volume(u->sink); +        pa_sink_get_mute(u->sink);      } -    return 0; +    if (u->source) +        pa_source_get_volume(u->source);  }  static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) { @@ -490,6 +543,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {      AUDIO_INITINFO(&info); +    info.play.buffer_size = buffer_size;      info.record.buffer_size = buffer_size;      if (ioctl(fd, AUDIO_SETINFO, &info) < 0) { @@ -503,7 +557,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {      return 0;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module *m) {      struct userdata *u = NULL;      const char *p;      int fd = -1; @@ -513,9 +567,10 @@ int pa__init(pa_core *c, pa_module*m) {      pa_sample_spec ss;      pa_channel_map map;      pa_modargs *ma = NULL; -    struct timeval tv;      char *t; -    assert(c && m); +    struct pollfd *pollfd; + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("failed to parse module arguments."); @@ -540,7 +595,7 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    ss = c->default_sample_spec; +    ss = m->core->default_sample_spec;      if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {          pa_log("failed to parse sample specification");          goto fail; @@ -554,55 +609,18 @@ int pa__init(pa_core *c, pa_module*m) {      if (pa_solaris_auto_format(fd, mode, &ss) < 0)          goto fail; -    if ((mode != O_WRONLY) && (buffer_size >= 1)) -        if (pa_solaris_set_buffer(fd, buffer_size) < 0) -            goto fail; +    if (pa_solaris_set_buffer(fd, buffer_size) < 0) +        goto fail;      u = pa_xmalloc(sizeof(struct userdata)); -    u->core = c; +    u->core = m->core; -    if (mode != O_WRONLY) { -        u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map); -        assert(u->source); -        u->source->userdata = u; -        u->source->get_latency = source_get_latency_cb; -        u->source->get_hw_volume = source_get_hw_volume_cb; -        u->source->set_hw_volume = source_set_hw_volume_cb; -        pa_source_set_owner(u->source, m); -        pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); -        pa_xfree(t); -        u->source->is_hardware = 1; -    } else -        u->source = NULL; - -    if (mode != O_RDONLY) { -        u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map); -        assert(u->sink); -        u->sink->get_latency = sink_get_latency_cb; -        u->sink->get_hw_volume = sink_get_hw_volume_cb; -        u->sink->set_hw_volume = sink_set_hw_volume_cb; -        u->sink->get_hw_mute = sink_get_hw_mute_cb; -        u->sink->set_hw_mute = sink_set_hw_mute_cb; -        u->sink->userdata = u; -        pa_sink_set_owner(u->sink, m); -        pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); -        pa_xfree(t); -        u->sink->is_hardware = 1; -    } else -        u->sink = NULL; - -    assert(u->source || u->sink); - -    u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); -    assert(u->io); -    pa_iochannel_set_callback(u->io, io_callback, u);      u->fd = fd; -    u->memchunk.memblock = NULL; -    u->memchunk.length = 0; +    pa_memchunk_reset(&u->memchunk);      /* We use this to get a reasonable chunk size */ -    u->page_size = sysconf(_SC_PAGESIZE); +    u->page_size = PA_PAGE_SIZE;      u->frame_size = pa_frame_size(&ss);      u->buffer_size = buffer_size; @@ -610,37 +628,91 @@ int pa__init(pa_core *c, pa_module*m) {      u->written_bytes = 0;      u->read_bytes = 0; -    u->sink_underflow = 1; -      u->module = m;      m->userdata = u; -    u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss); +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop); + +    u->rtpoll = pa_rtpoll_new(); +    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + +    pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss)); -    pa_gettimeofday(&tv); -    pa_timeval_add(&tv, u->poll_timeout); +    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 = fd; +    pollfd->events = 0; +    pollfd->revents = 0; -    u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u); -    assert(u->timer); +    if (mode != O_WRONLY) { +        u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map); +        pa_assert(u->source); + +        u->source->userdata = u; +        u->source->parent.process_msg = source_process_msg; + +        pa_source_set_module(u->source, m); +        pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); +        pa_xfree(t); +        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +        pa_source_set_rtpoll(u->source, u->rtpoll); + +        u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL; +        u->source->refresh_volume = 1; +    } else +        u->source = NULL; + +    if (mode != O_RDONLY) { +        u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map); +        pa_assert(u->sink); + +        u->sink->userdata = u; +        u->sink->parent.process_msg = sink_process_msg; + +        pa_sink_set_module(u->sink, m); +        pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); +        pa_xfree(t); +        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +        pa_sink_set_rtpoll(u->sink, u->rtpoll); + +        u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL; +        u->sink->refresh_volume = 1; +        u->sink->refresh_mute = 1; +    } else +        u->sink = NULL; + +    pa_assert(u->source || u->sink);      u->sig = pa_signal_new(SIGPOLL, sig_callback, u); -    assert(u->sig); +    pa_assert(u->sig);      ioctl(u->fd, I_SETSIG, S_MSG); -    pa_modargs_free(ma); +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    }      /* Read mixer settings */      if (u->source) -        source_get_hw_volume_cb(u->source); +        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);      if (u->sink) { -        sink_get_hw_volume_cb(u->sink); -        sink_get_hw_mute_cb(u->sink); +        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL); +        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);      } +    if (u->sink) +        pa_sink_put(u->sink); +    if (u->source) +        pa_source_put(u->source); + +    pa_modargs_free(ma); +      return 0;  fail: -    if (fd >= 0) +    if (u) +        pa__done(m); +    else if (fd >= 0)          close(fd);      if (ma) @@ -649,31 +721,47 @@ fail:      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module *m) {      struct userdata *u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; -    if (u->timer) -        c->mainloop->time_free(u->timer);      ioctl(u->fd, I_SETSIG, 0);      pa_signal_free(u->sig); -    if (u->memchunk.memblock) -        pa_memblock_unref(u->memchunk.memblock); +    if (u->sink) +        pa_sink_unlink(u->sink); -    if (u->sink) { -        pa_sink_disconnect(u->sink); -        pa_sink_unref(u->sink); +    if (u->source) +        pa_source_unlink(u->source); + +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread);      } -    if (u->source) { -        pa_source_disconnect(u->source); +    pa_thread_mq_done(&u->thread_mq); + +    if (u->sink) +        pa_sink_unref(u->sink); + +    if (u->source)          pa_source_unref(u->source); -    } -    pa_iochannel_free(u->io); +     if (u->memchunk.memblock) +        pa_memblock_unref(u->memchunk.memblock); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->fd >= 0) +        close(u->fd); +      pa_xfree(u);  } diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c new file mode 100644 index 00000000..5a711390 --- /dev/null +++ b/src/modules/module-suspend-on-idle.c @@ -0,0 +1,473 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <pulse/timeval.h> + +#include <pulsecore/core.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/source-output.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/namereg.h> + +#include "module-suspend-on-idle-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static const char* const valid_modargs[] = { +    "timeout", +    NULL, +}; + +struct userdata { +    pa_core *core; +    pa_usec_t timeout; +    pa_hashmap *device_infos; +    pa_hook_slot +        *sink_new_slot, +        *source_new_slot, +        *sink_unlink_slot, +        *source_unlink_slot, +        *sink_state_changed_slot, +        *source_state_changed_slot; + +    pa_hook_slot +        *sink_input_new_slot, +        *source_output_new_slot, +        *sink_input_unlink_slot, +        *source_output_unlink_slot, +        *sink_input_move_slot, +        *source_output_move_slot, +        *sink_input_move_post_slot, +        *source_output_move_post_slot, +        *sink_input_state_changed_slot, +        *source_output_state_changed_slot; +}; + +struct device_info { +    struct userdata *userdata; +    pa_sink *sink; +    pa_source *source; +    struct timeval last_use; +    pa_time_event *time_event; +}; + +static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +    struct device_info *d = userdata; + +    pa_assert(d); + +    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) { +        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) { +        pa_log_info("Source %s idle for too long, suspending ...", d->source->name); +        pa_source_suspend(d->source, TRUE); +    } +} + +static void restart(struct device_info *d) { +    struct timeval tv; +    pa_assert(d); + +    pa_gettimeofday(&tv); +    d->last_use = tv; +    pa_timeval_add(&tv, d->userdata->timeout*1000000); +    d->userdata->core->mainloop->time_restart(d->time_event, &tv); + +    if (d->sink) +        pa_log_debug("Sink %s becomes idle.", d->sink->name); +    if (d->source) +        pa_log_debug("Source %s becomes idle.", d->source->name); +} + +static void resume(struct device_info *d) { +    pa_assert(d); + +    d->userdata->core->mainloop->time_restart(d->time_event, NULL); + +    if (d->sink) { +        pa_sink_suspend(d->sink, FALSE); + +        pa_log_debug("Sink %s becomes busy.", d->sink->name); +    } + +    if (d->source) { +        pa_source_suspend(d->source, FALSE); + +        pa_log_debug("Source %s becomes busy.", d->source->name); +    } +} + +static pa_hook_result_t sink_input_new_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { +    struct device_info *d; + +    pa_assert(c); +    pa_sink_input_assert_ref(s); +    pa_assert(u); + +    if ((d = pa_hashmap_get(u->device_infos, s->sink))) +        resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { +    struct device_info *d; + +    pa_assert(c); +    pa_source_output_assert_ref(s); +    pa_assert(u); + +    if ((d = pa_hashmap_get(u->device_infos, s->source))) +        resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { +    pa_assert(c); +    pa_sink_input_assert_ref(s); +    pa_assert(u); + +    if (pa_sink_used_by(s->sink) <= 0) { +        struct device_info *d; +        if ((d = pa_hashmap_get(u->device_infos, s->sink))) +            restart(d); +    } + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { +    pa_assert(c); +    pa_source_output_assert_ref(s); +    pa_assert(u); + +    if (pa_source_used_by(s->source) <= 0) { +        struct device_info *d; +        if ((d = pa_hashmap_get(u->device_infos, s->source))) +            restart(d); +    } + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { +    pa_assert(c); +    pa_sink_input_assert_ref(s); +    pa_assert(u); + +    if (pa_sink_used_by(s->sink) <= 1) { +        struct device_info *d; +        if ((d = pa_hashmap_get(u->device_infos, s->sink))) +            restart(d); +    } + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_move_post_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { +    struct device_info *d; +    pa_assert(c); +    pa_sink_input_assert_ref(s); +    pa_assert(u); + +    if ((d = pa_hashmap_get(u->device_infos, s->sink))) +        resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { +    pa_assert(c); +    pa_source_output_assert_ref(s); +    pa_assert(u); + +    if (pa_source_used_by(s->source) <= 1) { +        struct device_info *d; + +        if ((d = pa_hashmap_get(u->device_infos, s->source))) +            restart(d); +    } + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_move_post_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { +    struct device_info *d; +    pa_assert(c); +    pa_source_output_assert_ref(s); +    pa_assert(u); + +    if ((d = pa_hashmap_get(u->device_infos, s->source))) +        resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { +    struct device_info *d; +    pa_sink_input_state_t state; +    pa_assert(c); +    pa_sink_input_assert_ref(s); +    pa_assert(u); + +    state = pa_sink_input_get_state(s); +    if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED) +        if ((d = pa_hashmap_get(u->device_infos, s->sink))) +            resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { +    struct device_info *d; +    pa_source_output_state_t state; +    pa_assert(c); +    pa_source_output_assert_ref(s); +    pa_assert(u); + +    state = pa_source_output_get_state(s); +    if (state == PA_SOURCE_OUTPUT_RUNNING) +        if ((d = pa_hashmap_get(u->device_infos, s->source))) +            resume(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { +    struct device_info *d; +    pa_source *source; +    pa_sink *sink; + +    pa_assert(c); +    pa_object_assert_ref(o); +    pa_assert(u); + +    source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL; +    sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL; + +    pa_assert(source || sink); + +    d = pa_xnew(struct device_info, 1); +    d->userdata = u; +    d->source = source ? pa_source_ref(source) : NULL; +    d->sink = sink ? pa_sink_ref(sink) : NULL; +    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)) +        restart(d); + +    return PA_HOOK_OK; +} + +static void device_info_free(struct device_info *d) { +    pa_assert(d); + +    if (d->source) +        pa_source_unref(d->source); +    if (d->sink) +        pa_sink_unref(d->sink); + +    d->userdata->core->mainloop->time_free(d->time_event); + +    pa_xfree(d); +} + +static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { +    struct device_info *d; + +    pa_assert(c); +    pa_object_assert_ref(o); +    pa_assert(u); + +    if ((d = pa_hashmap_remove(u->device_infos, o))) +        device_info_free(d); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { +    struct device_info *d; + +    pa_assert(c); +    pa_object_assert_ref(o); +    pa_assert(u); + +    if (!(d = pa_hashmap_get(u->device_infos, o))) +        return PA_HOOK_OK; + +    if (pa_sink_isinstance(o)) { +        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_OPENED(state)) +                restart(d); + +        } + +    } else if (pa_source_isinstance(o)) { +        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_OPENED(state)) +                restart(d); +        } +    } + +    return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { +    pa_modargs *ma = NULL; +    struct userdata *u; +    uint32_t timeout = 1; +    uint32_t idx; +    pa_sink *sink; +    pa_source *source; + +    pa_assert(m); + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments."); +        goto fail; +    } + +    if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) { +        pa_log("Failed to parse timeout value."); +        goto fail; +    } + +    m->userdata = u = pa_xnew(struct userdata, 1); +    u->core = m->core; +    u->timeout = timeout; +    u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + +    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx)) +        device_new_hook_cb(m->core, PA_OBJECT(sink), u); + +    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx)) +        device_new_hook_cb(m->core, PA_OBJECT(source), u); + +    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u); +    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u); +    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u); +    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u); +    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u); +    u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u); + +    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], (pa_hook_cb_t) sink_input_new_hook_cb, u); +    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], (pa_hook_cb_t) source_output_new_hook_cb, u); +    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], (pa_hook_cb_t) sink_input_unlink_hook_cb, u); +    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], (pa_hook_cb_t) source_output_unlink_hook_cb, u); +    u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], (pa_hook_cb_t) sink_input_move_hook_cb, u); +    u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], (pa_hook_cb_t) source_output_move_hook_cb, u); +    u->sink_input_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], (pa_hook_cb_t) sink_input_move_post_hook_cb, u); +    u->source_output_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], (pa_hook_cb_t) source_output_move_post_hook_cb, u); +    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], (pa_hook_cb_t) sink_input_state_changed_hook_cb, u); +    u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], (pa_hook_cb_t) source_output_state_changed_hook_cb, u); + + +    pa_modargs_free(ma); +    return 0; + +fail: + +    if (ma) +        pa_modargs_free(ma); + +    return -1; +} + +void pa__done(pa_module*m) { +    struct userdata *u; +    struct device_info *d; + +    pa_assert(m); + +    if (!m->userdata) +        return; + +    u = m->userdata; + +    if (u->sink_new_slot) +        pa_hook_slot_free(u->sink_new_slot); +    if (u->sink_unlink_slot) +        pa_hook_slot_free(u->sink_unlink_slot); +    if (u->sink_state_changed_slot) +        pa_hook_slot_free(u->sink_state_changed_slot); + +    if (u->source_new_slot) +        pa_hook_slot_free(u->source_new_slot); +    if (u->source_unlink_slot) +        pa_hook_slot_free(u->source_unlink_slot); +    if (u->source_state_changed_slot) +        pa_hook_slot_free(u->source_state_changed_slot); + +    if (u->sink_input_new_slot) +        pa_hook_slot_free(u->sink_input_new_slot); +    if (u->sink_input_unlink_slot) +        pa_hook_slot_free(u->sink_input_unlink_slot); +    if (u->sink_input_move_slot) +        pa_hook_slot_free(u->sink_input_move_slot); +    if (u->sink_input_move_post_slot) +        pa_hook_slot_free(u->sink_input_move_post_slot); +    if (u->sink_input_state_changed_slot) +        pa_hook_slot_free(u->sink_input_state_changed_slot); + +    if (u->source_output_new_slot) +        pa_hook_slot_free(u->source_output_new_slot); +    if (u->source_output_unlink_slot) +        pa_hook_slot_free(u->source_output_unlink_slot); +    if (u->source_output_move_slot) +        pa_hook_slot_free(u->source_output_move_slot); +    if (u->source_output_move_post_slot) +        pa_hook_slot_free(u->source_output_move_post_slot); +    if (u->source_output_state_changed_slot) +        pa_hook_slot_free(u->source_output_state_changed_slot); + +    while ((d = pa_hashmap_steal_first(u->device_infos))) +        device_info_free(d); + +    pa_hashmap_free(u->device_infos, NULL, NULL); + +    pa_xfree(u); +} diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 288e049e..b96d46b3 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -596,12 +596,12 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t      }  #ifdef TUNNEL_SINK -    snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s", +    pa_snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s",               pa_get_host_name(hn, sizeof(hn)),               pa_get_user_name(un, sizeof(un)),               u->sink->name);  #else -    snprintf(name, sizeof(name), "Tunnel from host %s, user %s, source %s", +    pa_snprintf(name, sizeof(name), "Tunnel from host %s, user %s, source %s",               pa_get_host_name(hn, sizeof(hn)),               pa_get_user_name(un, sizeof(un)),               u->source->name); diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 61a17aef..77e6174f 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -26,7 +26,6 @@  #endif  #include <unistd.h> -#include <assert.h>  #include <string.h>  #include <errno.h>  #include <sys/types.h> @@ -35,6 +34,7 @@  #include <ctype.h>  #include <pulse/xmalloc.h> +#include <pulse/volume.h>  #include <pulsecore/core-error.h>  #include <pulsecore/module.h> @@ -44,9 +44,7 @@  #include <pulsecore/core-subscribe.h>  #include <pulsecore/sink-input.h>  #include <pulsecore/source-output.h> -#include <pulsecore/core-util.h>  #include <pulsecore/namereg.h> -#include <pulse/volume.h>  #include "module-volume-restore-symdef.h" @@ -85,8 +83,8 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {      long k;      unsigned i; -    assert(s); -    assert(v); +    pa_assert(s); +    pa_assert(v);      if (!isdigit(*s))          return NULL; @@ -170,7 +168,7 @@ static int load_rules(struct userdata *u) {              continue;          } -        assert(ln == buf_source); +        pa_assert(ln == buf_source);          if (buf_volume[0]) {              if (!parse_volume(buf_volume, &v)) { @@ -297,8 +295,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3      struct rule *r;      char *name; -    assert(c); -    assert(u); +    pa_assert(c); +    pa_assert(u);      if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&          t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) && @@ -313,7 +311,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3          if (!si->client || !(name = client_name(si->client)))              return;      } else { -        assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT); +        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);          if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))              return; @@ -341,7 +339,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3                  u->modified = 1;              }          } else { -            assert(so); +            pa_assert(so);              if (!r->source || strcmp(so->source->name, r->source) != 0) {                  pa_log_info("Saving source for <%s>", r->name); @@ -363,7 +361,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3              r->sink = pa_xstrdup(si->sink->name);              r->source = NULL;          } else { -            assert(so); +            pa_assert(so);              r->volume_is_set = 0;              r->sink = NULL;              r->source = pa_xstrdup(so->source->name); @@ -378,7 +376,7 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d      struct rule *r;      char *name; -    assert(data); +    pa_assert(data);      if (!data->client || !(name = client_name(data->client)))          return PA_HOOK_OK; @@ -396,6 +394,8 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d          }      } +    pa_xfree(name); +      return PA_HOOK_OK;  } @@ -403,7 +403,7 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output      struct rule *r;      char *name; -    assert(data); +    pa_assert(data);      if (!data->client || !(name = client_name(data->client)))          return PA_HOOK_OK; @@ -418,12 +418,11 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output      return PA_HOOK_OK;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("Failed to parse module arguments"); @@ -442,16 +441,15 @@ int pa__init(pa_core *c, pa_module*m) {      if (load_rules(u) < 0)          goto fail; -    u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); -    u->sink_input_hook_slot = pa_hook_connect(&c->hook_sink_input_new, (pa_hook_cb_t) sink_input_hook_callback, u); -    u->source_output_hook_slot = pa_hook_connect(&c->hook_source_output_new, (pa_hook_cb_t) source_output_hook_callback, u); +    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); +    u->sink_input_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], (pa_hook_cb_t) sink_input_hook_callback, u); +    u->source_output_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], (pa_hook_cb_t) source_output_hook_callback, u);      pa_modargs_free(ma);      return 0;  fail: -    pa__done(c, m); - +    pa__done(m);      if (ma)          pa_modargs_free(ma); @@ -460,7 +458,7 @@ fail:  static void free_func(void *p, void *userdata) {      struct rule *r = p; -    assert(r); +    pa_assert(r);      pa_xfree(r->name);      pa_xfree(r->sink); @@ -468,11 +466,10 @@ static void free_func(void *p, void *userdata) {      pa_xfree(r);  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata* u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(u = m->userdata))          return; diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c index b9c4ad49..4eacbb1e 100644 --- a/src/modules/module-x11-bell.c +++ b/src/modules/module-x11-bell.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -67,30 +66,21 @@ static const char* const valid_modargs[] = {      NULL  }; -static int ring_bell(struct userdata *u, int percent) { -    pa_sink *s; -    assert(u); - -    if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) { -        pa_log("Invalid sink: %s", u->sink_name); -        return -1; -    } - -    pa_scache_play_item(u->core, u->scache_item, s, (percent*PA_VOLUME_NORM)/100); -    return 0; -} -  static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {      XkbBellNotifyEvent *bne;      struct userdata *u = userdata; -    assert(w && e && u && u->x11_wrapper == w); + +    pa_assert(w); +    pa_assert(e); +    pa_assert(u); +    pa_assert(u->x11_wrapper == w);      if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)          return 0;      bne = (XkbBellNotifyEvent*) e; -    if (ring_bell(u, bne->percent) < 0) { +    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, (bne->percent*PA_VOLUME_NORM)/100, 1) < 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);      } @@ -98,25 +88,27 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {      return 1;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) { +      struct userdata *u = NULL;      pa_modargs *ma = NULL;      int major, minor;      unsigned int auto_ctrls, auto_values; -    assert(c && m); + +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("Failed to parse module arguments");          goto fail;      } -    m->userdata = u = pa_xmalloc(sizeof(struct userdata)); -    u->core = c; +    m->userdata = u = pa_xnew(struct userdata, 1); +    u->core = m->core;      u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell"));      u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));      u->x11_client = NULL; -    if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) +    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))          goto fail;      major = XkbMajorVersion; @@ -130,7 +122,6 @@ int pa__init(pa_core *c, pa_module*m) {      major = XkbMajorVersion;      minor = XkbMinorVersion; -      if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) {          pa_log("XkbQueryExtension() failed");          goto fail; @@ -150,14 +141,21 @@ int pa__init(pa_core *c, pa_module*m) {  fail:      if (ma)          pa_modargs_free(ma); -    if (m->userdata) -        pa__done(c, m); + +    pa__done(m); +      return -1;  } -void pa__done(pa_core *c, pa_module*m) { -    struct userdata *u = m->userdata; -    assert(c && m && u); +void pa__done(pa_module*m) { +    struct userdata *u; + +    pa_assert(m); + +    if (!m->userdata) +        return; + +    u = m->userdata;      pa_xfree(u->scache_item);      pa_xfree(u->sink_name); diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c index fd1d532f..e0550e20 100644 --- a/src/modules/module-x11-publish.c +++ b/src/modules/module-x11-publish.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <unistd.h> @@ -76,7 +75,7 @@ struct userdata {  };  static int load_key(struct userdata *u, const char*fn) { -    assert(u); +    pa_assert(u);      u->auth_cookie_in_property = 0; @@ -93,7 +92,7 @@ static int load_key(struct userdata *u, const char*fn) {      if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)          return -1; -    pa_log_debug("loading cookie from disk."); +    pa_log_debug("Loading cookie from disk.");      if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)          u->auth_cookie_in_property = 1; @@ -101,7 +100,7 @@ static int load_key(struct userdata *u, const char*fn) {      return 0;  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u;      pa_modargs *ma = NULL;      char hn[256], un[128]; @@ -110,23 +109,25 @@ int pa__init(pa_core *c, pa_module*m) {      char *s;      pa_strlist *l; +    pa_assert(m); +      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("failed to parse module arguments");          goto fail;      }      m->userdata = u = pa_xmalloc(sizeof(struct userdata)); -    u->core = c; +    u->core = m->core;      u->id = NULL;      u->auth_cookie_in_property = 0;      if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)          goto fail; -    if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) +    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))          goto fail; -    if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME))) +    if (!(l = pa_property_get(m->core, PA_NATIVE_SERVER_PROPERTY_NAME)))          goto fail;      s = pa_strlist_tostring(l); @@ -154,13 +155,14 @@ fail:      if (ma)          pa_modargs_free(ma); -    pa__done(c, m); +    pa__done(m);      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata*u; -    assert(c && m); + +    pa_assert(m);      if (!(u = m->userdata))          return; @@ -185,7 +187,7 @@ void pa__done(pa_core *c, pa_module*m) {          pa_x11_wrapper_unref(u->x11_wrapper);      if (u->auth_cookie_in_property) -        pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME); +        pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);      pa_xfree(u->id);      pa_xfree(u); diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c new file mode 100644 index 00000000..3e353caf --- /dev/null +++ b/src/modules/module-x11-xsmp.c @@ -0,0 +1,195 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/SM/SMlib.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/iochannel.h> +#include <pulsecore/sink.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/modargs.h> +#include <pulsecore/namereg.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> + +#include "module-x11-xsmp-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("X11 session management") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static int ice_in_use = 0; + +static const char* const valid_modargs[] = { +    NULL +}; + +static void die_cb(SmcConn connection, SmPointer client_data){ +    pa_core *c = PA_CORE(client_data); + +    pa_log_debug("Got die message from XSM. Exiting..."); + +    pa_core_assert_ref(c); +    c->mainloop->quit(c->mainloop, 0); +} + +static void save_complete_cb(SmcConn connection, SmPointer client_data) { +} + +static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) { +    SmcSaveYourselfDone(connection, True); +} + +static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) { +    SmcSaveYourselfDone(connection, True); +} + +static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { +    IceConn connection = userdata; + +    if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) { +        IceSetShutdownNegotiation(connection, False); +        IceCloseConnection(connection); +    } +} + +static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) { +    pa_core *c = client_data; + +    pa_assert(c); + +    if (opening) +        *watch_data = c->mainloop->io_new(c->mainloop, IceConnectionNumber(connection), PA_IO_EVENT_INPUT, ice_io_cb, connection); +    else +        c->mainloop->io_free(*watch_data); +} + +int pa__init(pa_module*m) { + +    pa_modargs *ma = NULL; +    char t[256], *vendor, *client_id; +    SmcCallbacks callbacks; +    SmProp prop_program, prop_user; +    SmProp *prop_list[2]; +    SmPropValue val_program, val_user; +    SmcConn connection; + +    pa_assert(m); + +    if (ice_in_use) { +        pa_log("module-x11-xsmp may no be loaded twice."); +        return -1; +    } + +    IceAddConnectionWatch(new_ice_connection, m->core); +    ice_in_use = 1; + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments"); +        goto fail; +    } + +    if (!getenv("SESSION_MANAGER")) { +        pa_log("X11 session manager not running."); +        goto fail; +    } + +    memset(&callbacks, 0, sizeof(callbacks)); +    callbacks.die.callback = die_cb; +    callbacks.die.client_data = m->core; +    callbacks.save_yourself.callback = save_yourself_cb; +    callbacks.save_yourself.client_data = m->core; +    callbacks.save_complete.callback = save_complete_cb; +    callbacks.save_complete.client_data = m->core; +    callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb; +    callbacks.shutdown_cancelled.client_data = m->core; + +    if (!(m->userdata = connection = SmcOpenConnection( +                  NULL, m->core, +                  SmProtoMajor, SmProtoMinor, +                  SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, +                  &callbacks, NULL, &client_id, +                  sizeof(t), t))) { + +        pa_log("Failed to open connection to session manager: %s", t); +        goto fail; +    } + +    prop_program.name = (char*) SmProgram; +    prop_program.type = (char*) SmARRAY8; +    val_program.value = (char*) PACKAGE_NAME; +    val_program.length = strlen(val_program.value); +    prop_program.num_vals = 1; +    prop_program.vals = &val_program; +    prop_list[0] = &prop_program; + +    prop_user.name = (char*) SmUserID; +    prop_user.type = (char*) SmARRAY8; +    pa_get_user_name(t, sizeof(t)); +    val_user.value = t; +    val_user.length = strlen(val_user.value); +    prop_user.num_vals = 1; +    prop_user.vals = &val_user; +    prop_list[1] = &prop_user; + +    SmcSetProperties(connection, PA_ELEMENTSOF(prop_list), prop_list); + +    pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(connection), client_id); +    free(vendor); +    free(client_id); + +    pa_modargs_free(ma); + +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    pa__done(m); + +    return -1; +} + +void pa__done(pa_module*m) { +    pa_assert(m); + +    if (m->userdata) +        SmcCloseConnection(m->userdata, 0, NULL); + +    if (ice_in_use) { +        IceRemoveConnectionWatch(new_ice_connection, m->core); +        ice_in_use = 0; +    } +} diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c index 69508ad0..113686cf 100644 --- a/src/modules/module-zeroconf-publish.c +++ b/src/modules/module-zeroconf-publish.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <unistd.h> @@ -35,11 +34,11 @@  #include <avahi-client/publish.h>  #include <avahi-common/alternative.h>  #include <avahi-common/error.h> +#include <avahi-common/domain.h>  #include <pulse/xmalloc.h>  #include <pulse/util.h> -#include <pulsecore/autoload.h>  #include <pulsecore/sink.h>  #include <pulsecore/source.h>  #include <pulsecore/native-common.h> @@ -71,56 +70,52 @@ struct service {      struct userdata *userdata;      AvahiEntryGroup *entry_group;      char *service_name; -    char *name; -    enum  { UNPUBLISHED, PUBLISHED_REAL, PUBLISHED_AUTOLOAD } published ; - -    struct { -        int valid; -        pa_namereg_type_t type; -        uint32_t index; -    } loaded; - -    struct { -        int valid; -        pa_namereg_type_t type; -        uint32_t index; -    } autoload; +    pa_object *device;  };  struct userdata {      pa_core *core;      AvahiPoll *avahi_poll;      AvahiClient *client; +      pa_hashmap *services; -    pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray; -    pa_subscription *subscription;      char *service_name;      AvahiEntryGroup *main_entry_group;      uint16_t port; + +    pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;  }; -static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description) { -    assert(u && s && s->loaded.valid && ret_ss && ret_description); +static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, const char **ret_description) { +    pa_assert(s); +    pa_assert(ret_ss); +    pa_assert(ret_description); + +    if (pa_sink_isinstance(s->device)) { +        pa_sink *sink = PA_SINK(s->device); -    if (s->loaded.type == PA_NAMEREG_SINK) { -        pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index); -        assert(sink);          *ret_ss = sink->sample_spec; +        *ret_map = sink->channel_map; +        *ret_name = sink->name;          *ret_description = sink->description; -    } else if (s->loaded.type == PA_NAMEREG_SOURCE) { -        pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index); -        assert(source); + +    } else if (pa_source_isinstance(s->device)) { +        pa_source *source = PA_SOURCE(s->device); +          *ret_ss = source->sample_spec; +        *ret_map = source->channel_map; +        *ret_name = source->name;          *ret_description = source->description;      } else -        assert(0); +        pa_assert_not_reached();  }  static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {      char s[128]; -    assert(c); + +    pa_assert(c);      l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);      l = avahi_string_list_add_pair(l, "user-name", pa_get_user_name(s, sizeof(s))); @@ -130,325 +125,217 @@ static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {      return l;  } -static int publish_service(struct userdata *u, struct service *s); +static int publish_service(struct service *s);  static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {      struct service *s = userdata; -    if (state == AVAHI_ENTRY_GROUP_COLLISION) { -        char *t; +    pa_assert(s); + +    switch (state) { + +        case AVAHI_ENTRY_GROUP_ESTABLISHED: +            pa_log_info("Successfully established service %s.", s->service_name); +            break; + +        case AVAHI_ENTRY_GROUP_COLLISION: { +            char *t; + +            t = avahi_alternative_service_name(s->service_name); +            pa_log_info("Name collision, renaming %s to %s.", s->service_name, t); +            pa_xfree(s->service_name); +            s->service_name = t; + +            publish_service(s); +            break; +        } + +        case AVAHI_ENTRY_GROUP_FAILURE: { +            pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); -        t = avahi_alternative_service_name(s->service_name); -        pa_xfree(s->service_name); -        s->service_name = t; +            avahi_entry_group_free(g); +            s->entry_group = NULL; -        publish_service(s->userdata, s); +            break; +        } + +        case AVAHI_ENTRY_GROUP_UNCOMMITED: +        case AVAHI_ENTRY_GROUP_REGISTERING: +            ;      }  } -static int publish_service(struct userdata *u, struct service *s) { +static void service_free(struct service *s); + +static int publish_service(struct service *s) {      int r = -1;      AvahiStringList *txt = NULL; +    const char *description = NULL, *name = NULL; +    pa_sample_spec ss; +    pa_channel_map map; +    char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; -    assert(u); -    assert(s); +    pa_assert(s); -    if (!u->client || avahi_client_get_state(u->client) != AVAHI_CLIENT_S_RUNNING) +    if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)          return 0; -    if ((s->published == PUBLISHED_REAL && s->loaded.valid) || -        (s->published == PUBLISHED_AUTOLOAD && s->autoload.valid && !s->loaded.valid)) -        return 0; - -    if (s->published != UNPUBLISHED) { +    if (!s->entry_group) { +        if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) { +            pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); +            goto finish; +        } +    } else          avahi_entry_group_reset(s->entry_group); -        s->published = UNPUBLISHED; -    } -    if (s->loaded.valid || s->autoload.valid) { -        pa_namereg_type_t type; +    txt = txt_record_server_data(s->userdata->core, txt); -        if (!s->entry_group) { -            if (!(s->entry_group = avahi_entry_group_new(u->client, service_entry_group_callback, s))) { -                pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(u->client))); -                goto finish; -            } -        } +    get_service_data(s, &ss, &map, &name, &description); +    txt = avahi_string_list_add_pair(txt, "device", name); +    txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate); +    txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels); +    txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format)); +    txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &map)); -        txt = avahi_string_list_add_pair(txt, "device", s->name); -        txt = txt_record_server_data(u->core, txt); - -        if (s->loaded.valid) { -            char *description; -            pa_sample_spec ss; - -            get_service_data(u, s, &ss, &description); - -            txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate); -            txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels); -            txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format)); -            if (description) -                txt = avahi_string_list_add_pair(txt, "description", description); - -            type = s->loaded.type; -        } else if (s->autoload.valid) -            type = s->autoload.type; - -        if (avahi_entry_group_add_service_strlst( -                    s->entry_group, -                    AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, -                    0, -                    s->service_name, -                    type == PA_NAMEREG_SINK ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE, -                    NULL, -                    NULL, -                    u->port, -                    txt) < 0) { - -            pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(u->client))); -            goto finish; -        } +    if (avahi_entry_group_add_service_strlst( +                s->entry_group, +                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, +                0, +                s->service_name, +                pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE, +                NULL, +                NULL, +                s->userdata->port, +                txt) < 0) { -        if (avahi_entry_group_commit(s->entry_group) < 0) { -            pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(u->client))); -            goto finish; -        } +        pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); +        goto finish; +    } -        if (s->loaded.valid) -            s->published = PUBLISHED_REAL; -        else if (s->autoload.valid) -            s->published = PUBLISHED_AUTOLOAD; +    if (avahi_entry_group_commit(s->entry_group) < 0) { +        pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); +        goto finish;      }      r = 0; +    pa_log_debug("Successfully created entry group for %s.", s->service_name);  finish: -    if (s->published == UNPUBLISHED) { -        /* Remove this service */ - -        if (s->entry_group) -            avahi_entry_group_free(s->entry_group); +    /* Remove this service */ +    if (r < 0) +        service_free(s); -        pa_hashmap_remove(u->services, s->name); -        pa_xfree(s->name); -        pa_xfree(s->service_name); -        pa_xfree(s); -    } - -    if (txt) -        avahi_string_list_free(txt); +    avahi_string_list_free(txt);      return r;  } -static struct service *get_service(struct userdata *u, const char *name, const char *description) { +static struct service *get_service(struct userdata *u, pa_object *device) {      struct service *s; -    char hn[64]; +    char hn[64], un[64]; +    const char *n; + +    pa_assert(u); +    pa_object_assert_ref(device); -    if ((s = pa_hashmap_get(u->services, name))) +    if ((s = pa_hashmap_get(u->services, device)))          return s;      s = pa_xnew(struct service, 1);      s->userdata = u;      s->entry_group = NULL; -    s->published = UNPUBLISHED; -    s->name = pa_xstrdup(name); -    s->loaded.valid = s->autoload.valid = 0; -    s->service_name = pa_sprintf_malloc("%s on %s", description ? description : s->name, pa_get_host_name(hn, sizeof(hn))); - -    pa_hashmap_put(u->services, s->name, s); - -    return s; -} - -static int publish_sink(struct userdata *u, pa_sink *s) { -    struct service *svc; -    int ret; -    assert(u && s); - -    svc = get_service(u, s->name, s->description); -    if (svc->loaded.valid) -        return publish_service(u, svc); - -    svc->loaded.valid = 1; -    svc->loaded.type = PA_NAMEREG_SINK; -    svc->loaded.index = s->index; - -    if ((ret = publish_service(u, svc)) < 0) -        return ret; - -    pa_dynarray_put(u->sink_dynarray, s->index, svc); -    return ret; -} - -static int publish_source(struct userdata *u, pa_source *s) { -    struct service *svc; -    int ret; - -    assert(u && s); - -    svc = get_service(u, s->name, s->description); -    if (svc->loaded.valid) -        return publish_service(u, svc); - -    svc->loaded.valid = 1; -    svc->loaded.type = PA_NAMEREG_SOURCE; -    svc->loaded.index = s->index; +    s->device = device; + +    if (pa_sink_isinstance(device)) { +        if (!(n = PA_SINK(device)->description)) +            n = PA_SINK(device)->name; +    } else { +        if (!(n = PA_SOURCE(device)->description)) +            n = PA_SOURCE(device)->name; +    } -    pa_dynarray_put(u->source_dynarray, s->index, svc); +    s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", +                                                         pa_get_user_name(un, sizeof(un)), +                                                         pa_get_host_name(hn, sizeof(hn)), +                                                         n), +                                       AVAHI_LABEL_MAX-1); -    if ((ret = publish_service(u, svc)) < 0) -        return ret; +    pa_hashmap_put(u->services, s->device, s); -    pa_dynarray_put(u->sink_dynarray, s->index, svc); -    return ret; +    return s;  } -static int publish_autoload(struct userdata *u, pa_autoload_entry *s) { -    struct service *svc; -    int ret; +static void service_free(struct service *s) { +    pa_assert(s); -    assert(u && s); +    pa_hashmap_remove(s->userdata->services, s->device); -    svc = get_service(u, s->name, NULL); -    if (svc->autoload.valid) -        return publish_service(u, svc); - -    svc->autoload.valid = 1; -    svc->autoload.type = s->type; -    svc->autoload.index = s->index; - -    if ((ret = publish_service(u, svc)) < 0) -        return ret; +    if (s->entry_group) { +        pa_log_debug("Removing entry group for %s.", s->service_name); +        avahi_entry_group_free(s->entry_group); +    } -    pa_dynarray_put(u->autoload_dynarray, s->index, svc); -    return ret; +    pa_xfree(s->service_name); +    pa_xfree(s);  } -static int remove_sink(struct userdata *u, uint32_t idx) { -    struct service *svc; -    assert(u && idx != PA_INVALID_INDEX); - -    if (!(svc = pa_dynarray_get(u->sink_dynarray, idx))) -        return 0; - -    if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK) -        return 0; +static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) { +    pa_assert(c); +    pa_object_assert_ref(o); -    svc->loaded.valid = 0; -    pa_dynarray_put(u->sink_dynarray, idx, NULL); +    publish_service(get_service(u, o)); -    return publish_service(u, svc); +    return PA_HOOK_OK;  } -static int remove_source(struct userdata *u, uint32_t idx) { -    struct service *svc; -    assert(u && idx != PA_INVALID_INDEX); - -    if (!(svc = pa_dynarray_get(u->source_dynarray, idx))) -        return 0; +static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) { +    struct service *s; -    if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE) -        return 0; +    pa_assert(c); +    pa_object_assert_ref(o); -    svc->loaded.valid = 0; -    pa_dynarray_put(u->source_dynarray, idx, NULL); +    if ((s = pa_hashmap_get(u->services, o))) +        service_free(s); -    return publish_service(u, svc); +    return PA_HOOK_OK;  } -static int remove_autoload(struct userdata *u, uint32_t idx) { -    struct service *svc; -    assert(u && idx != PA_INVALID_INDEX); - -    if (!(svc = pa_dynarray_get(u->autoload_dynarray, idx))) -        return 0; - -    if (!svc->autoload.valid) -        return 0; - -    svc->autoload.valid = 0; -    pa_dynarray_put(u->autoload_dynarray, idx, NULL); - -    return publish_service(u, svc); -} +static int publish_main_service(struct userdata *u); -static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { +static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {      struct userdata *u = userdata; -    assert(u && c); - -    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) -        case PA_SUBSCRIPTION_EVENT_SINK: { -            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { -                pa_sink *sink; - -                if ((sink = pa_idxset_get_by_index(c->sinks, idx))) { -                    if (publish_sink(u, sink) < 0) -                        goto fail; -                } -            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -                if (remove_sink(u, idx) < 0) -                    goto fail; -            } +    pa_assert(u); -            break; +    switch (state) { -        case PA_SUBSCRIPTION_EVENT_SOURCE: +        case AVAHI_ENTRY_GROUP_ESTABLISHED: +            pa_log_info("Successfully established main service."); +            break; -            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { -                pa_source *source; +        case AVAHI_ENTRY_GROUP_COLLISION: { +            char *t; -                if ((source = pa_idxset_get_by_index(c->sources, idx))) { -                    if (publish_source(u, source) < 0) -                        goto fail; -                } -            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -                if (remove_source(u, idx) < 0) -                    goto fail; -            } +            t = avahi_alternative_service_name(u->service_name); +            pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t); +            pa_xfree(u->service_name); +            u->service_name = t; +            publish_main_service(u);              break; +        } -        case PA_SUBSCRIPTION_EVENT_AUTOLOAD: -            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { -                pa_autoload_entry *autoload; - -                if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, idx))) { -                    if (publish_autoload(u, autoload) < 0) -                        goto fail; -                } -            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -                if (remove_autoload(u, idx) < 0) -                        goto fail; -            } +        case AVAHI_ENTRY_GROUP_FAILURE: { +            pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); +            avahi_entry_group_free(g); +            u->main_entry_group = NULL;              break; -    } - -    return; - -fail: -    if (u->subscription) { -        pa_subscription_free(u->subscription); -        u->subscription = NULL; -    } -} - -static int publish_main_service(struct userdata *u); - -static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { -    struct userdata *u = userdata; -    assert(u); - -    if (state == AVAHI_ENTRY_GROUP_COLLISION) { -        char *t; - -        t = avahi_alternative_service_name(u->service_name); -        pa_xfree(u->service_name); -        u->service_name = t; +        } -        publish_main_service(u); +        case AVAHI_ENTRY_GROUP_UNCOMMITED: +        case AVAHI_ENTRY_GROUP_REGISTERING: +            break;      }  } @@ -456,6 +343,8 @@ static int publish_main_service(struct userdata *u) {      AvahiStringList *txt = NULL;      int r = -1; +    pa_assert(u); +      if (!u->main_entry_group) {          if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) {              pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); @@ -464,7 +353,7 @@ static int publish_main_service(struct userdata *u) {      } else          avahi_entry_group_reset(u->main_entry_group); -    txt = txt_record_server_data(u->core, NULL); +    txt = txt_record_server_data(u->core, txt);      if (avahi_entry_group_add_service_strlst(                  u->main_entry_group, @@ -497,26 +386,18 @@ fail:  static int publish_all_services(struct userdata *u) {      pa_sink *sink;      pa_source *source; -    pa_autoload_entry *autoload;      int r = -1;      uint32_t idx; -    assert(u); +    pa_assert(u);      pa_log_debug("Publishing services in Zeroconf"); -    for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) -        if (publish_sink(u, sink) < 0) -            goto fail; - -    for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) -        if (publish_source(u, source) < 0) -            goto fail; +    for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx))) +        publish_service(get_service(u, PA_OBJECT(sink))); -    if (u->core->autoload_idxset) -        for (autoload = pa_idxset_first(u->core->autoload_idxset, &idx); autoload; autoload = pa_idxset_next(u->core->autoload_idxset, &idx)) -            if (publish_autoload(u, autoload) < 0) -                goto fail; +    for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx))) +        publish_service(get_service(u, PA_OBJECT(source)));      if (publish_main_service(u) < 0)          goto fail; @@ -527,38 +408,44 @@ fail:      return r;  } -static void unpublish_all_services(struct userdata *u, int rem) { +static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {      void *state = NULL;      struct service *s; -    assert(u); +    pa_assert(u);      pa_log_debug("Unpublishing services in Zeroconf");      while ((s = pa_hashmap_iterate(u->services, &state, NULL))) {          if (s->entry_group) {              if (rem) { +                pa_log_debug("Removing entry group for %s.", s->service_name);                  avahi_entry_group_free(s->entry_group);                  s->entry_group = NULL; -            } else +            } else {                  avahi_entry_group_reset(s->entry_group); +                pa_log_debug("Resetting entry group for %s.", s->service_name); +            }          } - -        s->published = UNPUBLISHED;      }      if (u->main_entry_group) {          if (rem) { +            pa_log_debug("Removing main entry group.");              avahi_entry_group_free(u->main_entry_group);              u->main_entry_group = NULL; -        } else +        } else {              avahi_entry_group_reset(u->main_entry_group); +            pa_log_debug("Resetting main entry group."); +        }      }  }  static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {      struct userdata *u = userdata; -    assert(c); + +    pa_assert(c); +    pa_assert(u);      u->client = c; @@ -568,13 +455,17 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda              break;          case AVAHI_CLIENT_S_COLLISION: -            unpublish_all_services(u, 0); +            pa_log_debug("Host name collision"); +            unpublish_all_services(u, FALSE);              break;          case AVAHI_CLIENT_FAILURE:              if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {                  int error; -                unpublish_all_services(u, 1); + +                pa_log_debug("Avahi daemon disconnected."); + +                unpublish_all_services(u, TRUE);                  avahi_client_free(u->client);                  if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) @@ -587,11 +478,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda      }  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) { +      struct userdata *u;      uint32_t port = PA_NATIVE_DEFAULT_PORT;      pa_modargs *ma = NULL; -    char hn[256]; +    char hn[256], un[256];      int error;      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { @@ -599,30 +491,29 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } -    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) { +    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port <= 0 || port > 0xFFFF) {          pa_log("invalid port specified.");          goto fail;      }      m->userdata = u = pa_xnew(struct userdata, 1); -    u->core = c; +    u->core = m->core;      u->port = (uint16_t) port; -    u->avahi_poll = pa_avahi_poll_new(c->mainloop); +    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop); -    u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -    u->sink_dynarray = pa_dynarray_new(); -    u->source_dynarray = pa_dynarray_new(); -    u->autoload_dynarray = pa_dynarray_new(); +    u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -    u->subscription = pa_subscription_new(c, -                                          PA_SUBSCRIPTION_MASK_SINK| -                                          PA_SUBSCRIPTION_MASK_SOURCE| -                                          PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u); +    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u); +    u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); +    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) device_unlink_cb, u); +    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u); +    u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); +    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) device_unlink_cb, u);      u->main_entry_group = NULL; -    u->service_name = pa_xstrdup(pa_get_host_name(hn, sizeof(hn))); +    u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", pa_get_user_name(un, sizeof(un)), pa_get_host_name(hn, sizeof(hn))), AVAHI_LABEL_MAX);      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)); @@ -634,7 +525,7 @@ int pa__init(pa_core *c, pa_module*m) {      return 0;  fail: -    pa__done(c, m); +    pa__done(m);      if (ma)          pa_modargs_free(ma); @@ -642,41 +533,34 @@ fail:      return -1;  } -static void service_free(void *p, void *userdata) { -    struct service *s = p; -    struct userdata *u = userdata; - -    assert(s); -    assert(u); - -    if (s->entry_group) -        avahi_entry_group_free(s->entry_group); - -    pa_xfree(s->service_name); -    pa_xfree(s->name); -    pa_xfree(s); -} - -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata*u; -    assert(c && m); +    pa_assert(m);      if (!(u = m->userdata))          return; -    if (u->services) -        pa_hashmap_free(u->services, service_free, u); +    if (u->services) { +        struct service *s; -    if (u->subscription) -        pa_subscription_free(u->subscription); +        while ((s = pa_hashmap_get_first(u->services))) +            service_free(s); -    if (u->sink_dynarray) -        pa_dynarray_free(u->sink_dynarray, NULL, NULL); -    if (u->source_dynarray) -        pa_dynarray_free(u->source_dynarray, NULL, NULL); -    if (u->autoload_dynarray) -        pa_dynarray_free(u->autoload_dynarray, NULL, NULL); +        pa_hashmap_free(u->services, NULL, NULL); +    } +    if (u->sink_new_slot) +        pa_hook_slot_free(u->sink_new_slot); +    if (u->source_new_slot) +        pa_hook_slot_free(u->source_new_slot); +    if (u->sink_changed_slot) +        pa_hook_slot_free(u->sink_changed_slot); +    if (u->source_changed_slot) +        pa_hook_slot_free(u->source_changed_slot); +    if (u->sink_unlink_slot) +        pa_hook_slot_free(u->sink_unlink_slot); +    if (u->source_unlink_slot) +        pa_hook_slot_free(u->source_unlink_slot);      if (u->main_entry_group)          avahi_entry_group_free(u->main_entry_group); @@ -690,4 +574,3 @@ void pa__done(pa_core *c, pa_module*m) {      pa_xfree(u->service_name);      pa_xfree(u);  } - diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c index fb531468..25e45a35 100644 --- a/src/modules/oss-util.c +++ b/src/modules/oss-util.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <sys/soundcard.h>  #include <sys/ioctl.h>  #include <stdio.h> @@ -37,9 +36,11 @@  #include <sys/stat.h>  #include <fcntl.h> +#include <pulse/xmalloc.h>  #include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "oss-util.h" @@ -47,46 +48,43 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {      int fd = -1;      int caps; -    assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY)); +    pa_assert(device); +    pa_assert(mode); +    pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);      if(!pcaps)          pcaps = ∩︀      if (*mode == O_RDWR) { -        if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) { -            int dcaps, *tcaps; +        if ((fd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0) {              ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); -            tcaps = pcaps ? pcaps : &dcaps; - -            if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) { +            if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {                  pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));                  goto fail;              } -            if (*tcaps & DSP_CAP_DUPLEX) +            if (*pcaps & DSP_CAP_DUPLEX)                  goto success;              pa_log_warn("'%s' doesn't support full duplex", device); -            close(fd); +            pa_close(fd);          } -        if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) { -            if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) { +        if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY|O_NOCTTY)) < 0) { +            if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY|O_NOCTTY)) < 0) {                  pa_log("open('%s'): %s", device, pa_cstrerror(errno));                  goto fail;              }          }      } else { -        if ((fd = open(device, *mode|O_NDELAY)) < 0) { +        if ((fd = open(device, *mode|O_NDELAY|O_NOCTTY)) < 0) {              pa_log("open('%s'): %s", device, pa_cstrerror(errno));              goto fail;          }      } -success: -      *pcaps = 0;      if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { @@ -94,12 +92,14 @@ success:          goto fail;      } +success: +      pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",                   *pcaps & DSP_CAP_BATCH ? " BATCH" : "",  #ifdef DSP_CAP_BIND                   *pcaps & DSP_CAP_BIND ? " BIND" : "",  #else -		 "", +                 "",  #endif                   *pcaps & DSP_CAP_COPROC ? " COPROC" : "",                   *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "", @@ -122,7 +122,7 @@ success:  #ifdef DSP_CAP_MULTI                   *pcaps & DSP_CAP_MULTI ? " MULTI" : "",  #else -		 "", +                 "",  #endif  #ifdef DSP_CAP_OUTPUT                   *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "", @@ -142,13 +142,13 @@ success:  #endif                   *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : ""); -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(fd);      return fd;  fail:      if (fd >= 0) -        close(fd); +        pa_close(fd);      return -1;  } @@ -166,7 +166,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {          [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */      }; -    assert(fd >= 0 && ss); +    pa_assert(fd >= 0); +    pa_assert(ss);      orig_format = ss->format; @@ -199,7 +200,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {          pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));          return -1;      } -    assert(channels > 0); +    pa_assert(channels > 0);      if (ss->channels != channels) {          pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels); @@ -211,7 +212,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {          pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));          return -1;      } -    assert(speed > 0); +    pa_assert(speed > 0);      if (ss->rate != (unsigned) speed) {          pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed); @@ -248,27 +249,29 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {      return 0;  } -static int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) { +int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {      char cv[PA_CVOLUME_SNPRINT_MAX];      unsigned vol; -    assert(fd >= 0); -    assert(ss); -    assert(volume); +    pa_assert(fd >= 0); +    pa_assert(ss); +    pa_assert(volume);      if (ioctl(fd, mixer, &vol) < 0)          return -1; +    pa_cvolume_reset(volume, ss->channels); +      volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100; -    if ((volume->channels = ss->channels) >= 2) +    if (volume->channels >= 2)          volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;      pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));      return 0;  } -static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const pa_cvolume *volume) { +int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {      char cv[PA_CVOLUME_SNPRINT_MAX];      unsigned vol;      pa_volume_t l, r; @@ -289,40 +292,38 @@ static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const      return 0;  } -int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) { -    return pa_oss_get_volume(fd, SOUND_MIXER_READ_PCM, ss, volume); -} +static int get_device_number(const char *dev) { +    char buf[PATH_MAX]; +    const char *p, *e; -int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) { -    return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_PCM, ss, volume); -} +    if (readlink(dev, buf, sizeof(buf)) < 0) { +        if (errno != EINVAL && errno != ENOLINK) +            return -1; -int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) { -    return pa_oss_get_volume(fd, SOUND_MIXER_READ_IGAIN, ss, volume); -} +        p = dev; +    } else +        p = buf; + +    if ((e = strrchr(p, '/'))) +        p = e+1; -int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) { -    return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_IGAIN, ss, volume); +    if (p == 0) +        return 0; + +    p = strchr(p, 0) -1; + +    if (*p >= '0' && *p <= '9') +        return *p - '0'; + +    return -1;  }  int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {      FILE *f; -    const char *e = NULL;      int n, r = -1;      int b = 0; -    if (strncmp(dev, "/dev/dsp", 8) == 0) -        e = dev+8; -    else if (strncmp(dev, "/dev/adsp", 9) == 0) -        e = dev+9; -    else -        return -1; - -    if (*e == 0) -        n = 0; -    else if (*e >= '0' && *e <= '9' && *(e+1) == 0) -        n = *e - '0'; -    else +    if ((n = get_device_number(dev)) < 0)          return -1;      if (!(f = fopen("/dev/sndstat", "r")) && @@ -357,7 +358,7 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {          if (device == n) {              char *k = strchr(line, ':'); -            assert(k); +            pa_assert(k);              k++;              k += strspn(k, " "); @@ -373,3 +374,34 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {      fclose(f);      return r;  } + +static int open_mixer(const char *mixer) { +    int fd; + +    if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0) +        return fd; + +    return -1; +} + +int pa_oss_open_mixer_for_device(const char *device) { +    int n; +    char *fn; +    int fd; + +    if ((n = get_device_number(device)) < 0) +        return -1; + +    if (n == 0) +        if ((fd = open_mixer("/dev/mixer")) >= 0) +            return fd; + +    fn = pa_sprintf_malloc("/dev/mixer%i", n); +    fd = open_mixer(fn); +    pa_xfree(fn); + +    if (fd < 0) +        pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno)); + +    return fd; +} diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h index 087e0d22..259a622a 100644 --- a/src/modules/oss-util.h +++ b/src/modules/oss-util.h @@ -33,12 +33,11 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss);  int pa_oss_set_fragments(int fd, int frags, int frag_size); -int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume); -int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume); - -int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume); -int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume); +int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume); +int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume);  int pa_oss_get_hw_description(const char *dev, char *name, size_t l); +int pa_oss_open_mixer_for_device(const char *device); +  #endif diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index 62ef561f..6c018931 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -24,7 +24,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h>  #include <sys/socket.h>  #include <netinet/in.h> @@ -32,6 +31,7 @@  #include <errno.h>  #include <string.h>  #include <unistd.h> +#include <poll.h>  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> @@ -47,6 +47,10 @@  #include <pulsecore/modargs.h>  #include <pulsecore/namereg.h>  #include <pulsecore/sample-util.h> +#include <pulsecore/macro.h> +#include <pulsecore/atomic.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/atomic.h>  #include "module-rtp-recv-symdef.h" @@ -66,7 +70,7 @@ PA_MODULE_USAGE(  #define DEFAULT_SAP_ADDRESS "224.0.0.56"  #define MEMBLOCKQ_MAXLENGTH (1024*170)  #define MAX_SESSIONS 16 -#define DEATH_TIMEOUT 20000000 +#define DEATH_TIMEOUT 20  static const char* const valid_modargs[] = {      "sink", @@ -76,102 +80,126 @@ static const char* const valid_modargs[] = {  struct session {      struct userdata *userdata; +    PA_LLIST_FIELDS(struct session);      pa_sink_input *sink_input;      pa_memblockq *memblockq; -    pa_time_event *death_event; - -    int first_packet; +    pa_bool_t first_packet;      uint32_t ssrc;      uint32_t offset;      struct pa_sdp_info sdp_info;      pa_rtp_context rtp_context; -    pa_io_event* rtp_event; + +    pa_rtpoll_item *rtpoll_item; + +    pa_atomic_t timestamp;  };  struct userdata {      pa_module *module; -    pa_core *core;      pa_sap_context sap_context;      pa_io_event* sap_event; -    pa_hashmap *by_origin; +    pa_time_event *check_death_event;      char *sink_name; +    PA_LLIST_HEAD(struct session, sessions); +    pa_hashmap *by_origin;      int n_sessions;  }; -static void session_free(struct session *s, int from_hash); +static void session_free(struct session *s); -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { -    struct session *s; -    assert(i); -    s = i->userdata; +/* Called from I/O thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct session *s = PA_SINK_INPUT(o)->userdata; -    return pa_memblockq_peek(s->memblockq, chunk); +    switch (code) { +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: +            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +    } + +    return pa_sink_input_process_msg(o, code, data, offset, chunk);  } -static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { +/* Called from I/O thread context */ +static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) {      struct session *s; -    assert(i); -    s = i->userdata; +    pa_sink_input_assert_ref(i); +    pa_assert_se(s = i->userdata); -    pa_memblockq_drop(s->memblockq, chunk, length); +    return pa_memblockq_peek(s->memblockq, chunk);  } -static void sink_input_kill(pa_sink_input* i) { +/* Called from I/O thread context */ +static void sink_input_drop(pa_sink_input *i, size_t length) {      struct session *s; -    assert(i); -    s = i->userdata; +    pa_sink_input_assert_ref(i); +    pa_assert_se(s = i->userdata); -    session_free(s, 1); +    pa_memblockq_drop(s->memblockq, length);  } -static pa_usec_t sink_input_get_latency(pa_sink_input *i) { +/* Called from main context */ +static void sink_input_kill(pa_sink_input* i) {      struct session *s; -    assert(i); -    s = i->userdata; +    pa_sink_input_assert_ref(i); +    pa_assert_se(s = i->userdata); -    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec); +    session_free(s);  } -static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { -    struct session *s = userdata; +/* Called from I/O thread context */ +static int rtpoll_work_cb(pa_rtpoll_item *i) {      pa_memchunk chunk;      int64_t k, j, delta; -    struct timeval tv; +    struct timeval now; +    struct session *s; +    struct pollfd *p; -    assert(m); -    assert(e); -    assert(s); -    assert(fd == s->rtp_context.fd); -    assert(flags == PA_IO_EVENT_INPUT); +    pa_assert_se(s = pa_rtpoll_item_get_userdata(i)); -    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->core->mempool) < 0) -        return; +    p = pa_rtpoll_item_get_pollfd(i, NULL); + +    if (p->revents & (POLLERR|POLLNVAL|POLLHUP|POLLOUT)) { +        pa_log("poll() signalled bad revents."); +        return -1; +    } + +    if ((p->revents & POLLIN) == 0) +        return 0; + +    p->revents = 0; + +    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0) +        return 0;      if (s->sdp_info.payload != s->rtp_context.payload) {          pa_memblock_unref(chunk.memblock); -        return; +        return 0;      }      if (!s->first_packet) { -        s->first_packet = 1; +        s->first_packet = TRUE;          s->ssrc = s->rtp_context.ssrc;          s->offset = s->rtp_context.timestamp; -        if (s->ssrc == s->userdata->core->cookie) -            pa_log_warn("WARNING! Detected RTP packet loop!"); +        if (s->ssrc == s->userdata->module->core->cookie) +            pa_log_warn("Detected RTP packet loop!");      } else {          if (s->ssrc != s->rtp_context.ssrc) {              pa_memblock_unref(chunk.memblock); -            return; +            return 0;          }      } @@ -197,26 +225,49 @@ static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event      pa_memblock_unref(chunk.memblock); -    /* Reset death timer */ -    pa_gettimeofday(&tv); -    pa_timeval_add(&tv, DEATH_TIMEOUT); -    m->time_restart(s->death_event, &tv); +    pa_rtclock_get(&now); +    pa_atomic_store(&s->timestamp, now.tv_sec); + +    return 1;  } -static void death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) { -    struct session *s = userdata; +/* Called from I/O thread context */ +static void sink_input_attach(pa_sink_input *i) { +    struct session *s; +    struct pollfd *p; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(s = i->userdata); + +    pa_assert(!s->rtpoll_item); +    s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1); + +    p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL); +    p->fd = s->rtp_context.fd; +    p->events = POLLIN; +    p->revents = 0; + +    pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb); +    pa_rtpoll_item_set_userdata(s->rtpoll_item, s); +} -    assert(m); -    assert(t); -    assert(tv); -    assert(s); +/* Called from I/O thread context */ +static void sink_input_detach(pa_sink_input *i) { +    struct session *s; +    pa_sink_input_assert_ref(i); +    pa_assert_se(s = i->userdata); -    session_free(s, 1); +    pa_assert(s->rtpoll_item); +    pa_rtpoll_item_free(s->rtpoll_item); +    s->rtpoll_item = NULL;  }  static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {      int af, fd = -1, r, one; +    pa_assert(sa); +    pa_assert(salen > 0); +      af = sa->sa_family;      if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {          pa_log("Failed to create socket: %s", pa_cstrerror(errno)); @@ -262,27 +313,34 @@ fail:  static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {      struct session *s = NULL; -    struct timeval tv;      char *c;      pa_sink *sink;      int fd = -1;      pa_memblock *silence;      pa_sink_input_new_data data; +    struct timeval now; + +    pa_assert(u); +    pa_assert(sdp_info);      if (u->n_sessions >= MAX_SESSIONS) { -        pa_log("session limit reached."); +        pa_log("Session limit reached.");          goto fail;      } -    if (!(sink = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) { -        pa_log("sink does not exist."); +    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) { +        pa_log("Sink does not exist.");          goto fail;      }      s = pa_xnew0(struct session, 1);      s->userdata = u; -    s->first_packet = 0; +    s->first_packet = FALSE;      s->sdp_info = *sdp_info; +    s->rtpoll_item = NULL; + +    pa_rtclock_get(&now); +    pa_atomic_store(&s->timestamp, now.tv_sec);      if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)          goto fail; @@ -299,25 +357,27 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in      data.module = u->module;      pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec); -    s->sink_input = pa_sink_input_new(u->core, &data, 0); +    s->sink_input = pa_sink_input_new(u->module->core, &data, 0);      pa_xfree(c);      if (!s->sink_input) { -        pa_log("failed to create sink input."); +        pa_log("Failed to create sink input.");          goto fail;      }      s->sink_input->userdata = s; +    s->sink_input->parent.process_msg = sink_input_process_msg;      s->sink_input->peek = sink_input_peek;      s->sink_input->drop = sink_input_drop;      s->sink_input->kill = sink_input_kill; -    s->sink_input->get_latency = sink_input_get_latency; +    s->sink_input->attach = sink_input_attach; +    s->sink_input->detach = sink_input_detach; -    silence = pa_silence_memblock_new(s->userdata->core->mempool, -                                      &s->sink_input->sample_spec, -                                      (pa_bytes_per_second(&s->sink_input->sample_spec)/128/pa_frame_size(&s->sink_input->sample_spec))* -                                      pa_frame_size(&s->sink_input->sample_spec)); +    silence = pa_silence_memblock_new( +            s->userdata->module->core->mempool, +            &s->sink_input->sample_spec, +            pa_frame_align(pa_bytes_per_second(&s->sink_input->sample_spec)/128, &s->sink_input->sample_spec));      s->memblockq = pa_memblockq_new(              0, @@ -330,54 +390,44 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in      pa_memblock_unref(silence); -    s->rtp_event = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, rtp_event_cb, s); - -    pa_gettimeofday(&tv); -    pa_timeval_add(&tv, DEATH_TIMEOUT); -    s->death_event = u->core->mainloop->time_new(u->core->mainloop, &tv, death_event_cb, s); +    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));      pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s); +    u->n_sessions++; +    PA_LLIST_PREPEND(struct session, s->userdata->sessions, s); -    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec)); - -    pa_log_info("Found new session '%s'", s->sdp_info.session_name); +    pa_sink_input_put(s->sink_input); -    u->n_sessions++; +    pa_log_info("New session '%s'", s->sdp_info.session_name);      return s;  fail: -    if (s) { -        if (fd >= 0) -            close(fd); +    pa_xfree(s); -        pa_xfree(s); -    } +    if (fd >= 0) +        pa_close(fd);      return NULL;  } -static void session_free(struct session *s, int from_hash) { -    assert(s); +static void session_free(struct session *s) { +    pa_assert(s);      pa_log_info("Freeing session '%s'", s->sdp_info.session_name); -    s->userdata->core->mainloop->time_free(s->death_event); -    s->userdata->core->mainloop->io_free(s->rtp_event); - -    if (from_hash) -        pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin); - -    pa_sink_input_disconnect(s->sink_input); +    pa_sink_input_unlink(s->sink_input);      pa_sink_input_unref(s->sink_input); +    PA_LLIST_REMOVE(struct session, s->userdata->sessions, s); +    pa_assert(s->userdata->n_sessions >= 1); +    s->userdata->n_sessions--; +    pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin); +      pa_memblockq_free(s->memblockq);      pa_sdp_info_destroy(&s->sdp_info);      pa_rtp_context_destroy(&s->rtp_context); -    assert(s->userdata->n_sessions >= 1); -    s->userdata->n_sessions--; -      pa_xfree(s);  } @@ -387,11 +437,11 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event      pa_sdp_info info;      struct session *s; -    assert(m); -    assert(e); -    assert(u); -    assert(fd == u->sap_context.fd); -    assert(flags == PA_IO_EVENT_INPUT); +    pa_assert(m); +    pa_assert(e); +    pa_assert(u); +    pa_assert(fd == u->sap_context.fd); +    pa_assert(flags == PA_IO_EVENT_INPUT);      if (pa_sap_recv(&u->sap_context, &goodbye) < 0)          return; @@ -402,7 +452,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event      if (goodbye) {          if ((s = pa_hashmap_get(u->by_origin, info.origin))) -            session_free(s, 1); +            session_free(s);          pa_sdp_info_destroy(&info);      } else { @@ -412,18 +462,47 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event                  pa_sdp_info_destroy(&info);          } else { -            struct timeval tv; - -            pa_gettimeofday(&tv); -            pa_timeval_add(&tv, DEATH_TIMEOUT); -            m->time_restart(s->death_event, &tv); +            struct timeval now; +            pa_rtclock_get(&now); +            pa_atomic_store(&s->timestamp, now.tv_sec);              pa_sdp_info_destroy(&info);          }      }  } -int pa__init(pa_core *c, pa_module*m) { +static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) { +    struct session *s, *n; +    struct userdata *u = userdata; +    struct timeval now; +    struct timeval tv; + +    pa_assert(m); +    pa_assert(t); +    pa_assert(ptv); +    pa_assert(u); + +    pa_rtclock_get(&now); + +    pa_log_debug("Checking for dead streams ..."); + +    for (s = u->sessions; s; s = n) { +        int k; +        n = s->next; + +        k = pa_atomic_load(&s->timestamp); + +        if (k + DEATH_TIMEOUT < now.tv_sec) +            session_free(s); +    } + +    /* Restart timer */ +    pa_gettimeofday(&tv); +    pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC); +    m->time_restart(t, &tv); +} + +int pa__init(pa_module*m) {      struct userdata *u;      pa_modargs *ma = NULL;      struct sockaddr_in sa4; @@ -432,9 +511,9 @@ int pa__init(pa_core *c, pa_module*m) {      socklen_t salen;      const char *sap_address;      int fd = -1; +    struct timeval tv; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          pa_log("failed to parse module arguments"); @@ -454,7 +533,7 @@ int pa__init(pa_core *c, pa_module*m) {          sa = (struct sockaddr*) &sa4;          salen = sizeof(sa4);      } else { -        pa_log("invalid SAP address '%s'", sap_address); +        pa_log("Invalid SAP address '%s'", sap_address);          goto fail;      } @@ -464,15 +543,18 @@ int pa__init(pa_core *c, pa_module*m) {      u = pa_xnew(struct userdata, 1);      m->userdata = u;      u->module = m; -    u->core = c;      u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); -    u->n_sessions = 0; -    u->sap_event = c->mainloop->io_new(c->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u); +    u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u); +    pa_sap_context_init_recv(&u->sap_context, fd); +    PA_LLIST_HEAD_INIT(struct session, u->sessions); +    u->n_sessions = 0;      u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -    pa_sap_context_init_recv(&u->sap_context, fd); +    pa_gettimeofday(&tv); +    pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC); +    u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);      pa_modargs_free(ma); @@ -483,27 +565,34 @@ fail:          pa_modargs_free(ma);      if (fd >= 0) -        close(fd); +        pa_close(fd);      return -1;  } -static void free_func(void *p, PA_GCC_UNUSED void *userdata) { -    session_free(p, 0); -} - -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    struct session *s; + +    pa_assert(m);      if (!(u = m->userdata))          return; -    c->mainloop->io_free(u->sap_event); +    if (u->sap_event) +        m->core->mainloop->io_free(u->sap_event); + +    if (u->check_death_event) +        m->core->mainloop->time_free(u->check_death_event); +      pa_sap_context_destroy(&u->sap_context); -    pa_hashmap_free(u->by_origin, free_func, NULL); +    if (u->by_origin) { +        while ((s = pa_hashmap_get_first(u->by_origin))) +            session_free(s); + +        pa_hashmap_free(u->by_origin, NULL, NULL); +    }      pa_xfree(u->sink_name);      pa_xfree(u); diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 8c9e5f23..f36989bd 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h>  #include <sys/socket.h>  #include <netinet/in.h> @@ -48,6 +47,9 @@  #include <pulsecore/core-util.h>  #include <pulsecore/modargs.h>  #include <pulsecore/namereg.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/macro.h> +#include <pulsecore/socket-util.h>  #include "module-rtp-send-symdef.h" @@ -74,7 +76,7 @@ PA_MODULE_USAGE(  #define DEFAULT_DESTINATION "224.0.0.56"  #define MEMBLOCKQ_MAXLENGTH (1024*170)  #define DEFAULT_MTU 1280 -#define SAP_INTERVAL 5000000 +#define SAP_INTERVAL 5  static const char* const valid_modargs[] = {      "source", @@ -90,7 +92,6 @@ static const char* const valid_modargs[] = {  struct userdata {      pa_module *module; -    pa_core *core;      pa_source_output *source_output;      pa_memblockq *memblockq; @@ -102,56 +103,67 @@ struct userdata {      pa_time_event *sap_event;  }; +/* Called from I/O thread context */ +static int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u; +    pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata); + +    switch (code) { +        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: +            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +    } + +    return pa_source_output_process_msg(o, code, data, offset, chunk); +} + +/* Called from I/O thread context */  static void source_output_push(pa_source_output *o, const pa_memchunk *chunk) {      struct userdata *u; -    assert(o); -    u = o->userdata; +    pa_source_output_assert_ref(o); +    pa_assert_se(u = o->userdata);      if (pa_memblockq_push(u->memblockq, chunk) < 0) { -        pa_log("Failed to push chunk into memblockq."); +        pa_log_warn("Failed to push chunk into memblockq.");          return;      }      pa_rtp_send(&u->rtp_context, u->mtu, u->memblockq);  } +/* Called from main context */  static void source_output_kill(pa_source_output* o) {      struct userdata *u; -    assert(o); -    u = o->userdata; +    pa_source_output_assert_ref(o); +    pa_assert_se(u = o->userdata);      pa_module_unload_request(u->module); -    pa_source_output_disconnect(u->source_output); +    pa_source_output_unlink(u->source_output);      pa_source_output_unref(u->source_output);      u->source_output = NULL;  } -static pa_usec_t source_output_get_latency (pa_source_output *o) { -    struct userdata *u; -    assert(o); -    u = o->userdata; - -    return pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &o->sample_spec); -} -  static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {      struct userdata *u = userdata;      struct timeval next; -    assert(m); -    assert(t); -    assert(tv); -    assert(u); +    pa_assert(m); +    pa_assert(t); +    pa_assert(tv); +    pa_assert(u);      pa_sap_send(&u->sap_context, 0);      pa_gettimeofday(&next); -    pa_timeval_add(&next, SAP_INTERVAL); +    pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC);      m->time_restart(t, &next);  } -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) {      struct userdata *u;      pa_modargs *ma = NULL;      const char *dest; @@ -173,21 +185,20 @@ int pa__init(pa_core *c, pa_module*m) {      int loop = 0;      pa_source_output_new_data data; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -        pa_log("failed to parse module arguments"); +        pa_log("Failed to parse module arguments");          goto fail;      }      if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) { -        pa_log("source does not exist."); +        pa_log("Source does not exist.");          goto fail;      }      if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) { -        pa_log("failed to parse \"loop\" parameter."); +        pa_log("Failed to parse \"loop\" parameter.");          goto fail;      } @@ -195,12 +206,12 @@ int pa__init(pa_core *c, pa_module*m) {      pa_rtp_sample_spec_fixup(&ss);      cm = s->channel_map;      if (pa_modargs_get_sample_spec(ma, &ss) < 0) { -        pa_log("failed to parse sample specification"); +        pa_log("Failed to parse sample specification");          goto fail;      }      if (!pa_rtp_sample_spec_valid(&ss)) { -        pa_log("specified sample type not compatible with RTP"); +        pa_log("Specified sample type not compatible with RTP");          goto fail;      } @@ -209,10 +220,10 @@ int pa__init(pa_core *c, pa_module*m) {      payload = pa_rtp_payload_from_sample_spec(&ss); -    mtu = (DEFAULT_MTU/pa_frame_size(&ss))*pa_frame_size(&ss); +    mtu = 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."); +        pa_log("Invalid MTU.");          goto fail;      } @@ -223,7 +234,7 @@ int pa__init(pa_core *c, pa_module*m) {      }      if (port & 1) -        pa_log_warn("WARNING: port number not even as suggested in RFC3550!"); +        pa_log_warn("Port number not even as suggested in RFC3550!");      dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION); @@ -238,7 +249,7 @@ int pa__init(pa_core *c, pa_module*m) {          sap_sa4 = sa4;          sap_sa4.sin_port = htons(SAP_PORT);      } else { -        pa_log("invalid destination '%s'", dest); +        pa_log("Invalid destination '%s'", dest);          goto fail;      } @@ -268,6 +279,12 @@ int pa__init(pa_core *c, pa_module*m) {          goto fail;      } +    /* If the socket queue is full, let's drop packets */ +    pa_make_fd_nonblock(fd); +    pa_make_udp_socket_low_delay(fd); +    pa_make_fd_cloexec(fd); +    pa_make_fd_cloexec(sap_fd); +      pa_source_output_new_data_init(&data);      data.name = "RTP Monitor Stream";      data.driver = __FILE__; @@ -276,21 +293,20 @@ int pa__init(pa_core *c, pa_module*m) {      pa_source_output_new_data_set_sample_spec(&data, &ss);      pa_source_output_new_data_set_channel_map(&data, &cm); -    if (!(o = pa_source_output_new(c, &data, 0))) { +    if (!(o = pa_source_output_new(m->core, &data, 0))) {          pa_log("failed to create source output.");          goto fail;      } +    o->parent.process_msg = source_output_process_msg;      o->push = source_output_push;      o->kill = source_output_kill; -    o->get_latency = source_output_get_latency;      u = pa_xnew(struct userdata, 1);      m->userdata = u;      o->userdata = u;      u->module = m; -    u->core = c;      u->source_output = o;      u->memblockq = pa_memblockq_new( @@ -305,8 +321,7 @@ int pa__init(pa_core *c, pa_module*m) {      u->mtu = mtu;      k = sizeof(sa_dst); -    r = getsockname(fd, (struct sockaddr*) &sa_dst, &k); -    assert(r >= 0); +    pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);      n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn))); @@ -317,17 +332,19 @@ int pa__init(pa_core *c, pa_module*m) {      pa_xfree(n); -    pa_rtp_context_init_send(&u->rtp_context, fd, c->cookie, payload, pa_frame_size(&ss)); +    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("SDP-Data:\n%s\n"__FILE__": EOF", p); +    pa_log_info("SDP-Data:\n%s\nEOF", p);      pa_sap_send(&u->sap_context, 0);      pa_gettimeofday(&tv); -    pa_timeval_add(&tv, SAP_INTERVAL); -    u->sap_event = c->mainloop->time_new(c->mainloop, &tv, sap_event_cb, u); +    pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC); +    u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u); + +    pa_source_output_put(u->source_output);      pa_modargs_free(ma); @@ -338,31 +355,31 @@ fail:          pa_modargs_free(ma);      if (fd >= 0) -        close(fd); +        pa_close(fd);      if (sap_fd >= 0) -        close(sap_fd); +        pa_close(sap_fd);      if (o) { -        pa_source_output_disconnect(o); +        pa_source_output_unlink(o);          pa_source_output_unref(o);      }      return -1;  } -void pa__done(pa_core *c, pa_module*m) { +void pa__done(pa_module*m) {      struct userdata *u; -    assert(c); -    assert(m); +    pa_assert(m);      if (!(u = m->userdata))          return; -    c->mainloop->time_free(u->sap_event); +    if (u->sap_event) +        m->core->mainloop->time_free(u->sap_event);      if (u->source_output) { -        pa_source_output_disconnect(u->source_output); +        pa_source_output_unlink(u->source_output);          pa_source_output_unref(u->source_output);      } @@ -371,7 +388,8 @@ void pa__done(pa_core *c, pa_module*m) {      pa_sap_send(&u->sap_context, 1);      pa_sap_context_destroy(&u->sap_context); -    pa_memblockq_free(u->memblockq); +    if (u->memblockq) +        pa_memblockq_free(u->memblockq);      pa_xfree(u);  } diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c index 03a01412..997fcc34 100644 --- a/src/modules/rtp/rtp.c +++ b/src/modules/rtp/rtp.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <fcntl.h>  #include <stdlib.h>  #include <string.h> @@ -40,12 +39,14 @@  #include <pulsecore/core-error.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h>  #include "rtp.h"  pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size) { -    assert(c); -    assert(fd >= 0); +    pa_assert(c); +    pa_assert(fd >= 0);      c->fd = fd;      c->sequence = (uint16_t) (rand()*rand()); @@ -63,11 +64,11 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {      struct iovec iov[MAX_IOVECS];      pa_memblock* mb[MAX_IOVECS];      int iov_idx = 1; -    size_t n = 0, skip = 0; +    size_t n = 0; -    assert(c); -    assert(size > 0); -    assert(q); +    pa_assert(c); +    pa_assert(size > 0); +    pa_assert(q);      if (pa_memblockq_get_length(q) < size)          return 0; @@ -76,24 +77,26 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {          int r;          pa_memchunk chunk; +        pa_memchunk_reset(&chunk); +          if ((r = pa_memblockq_peek(q, &chunk)) >= 0) {              size_t k = n + chunk.length > size ? size - n : chunk.length; -            if (chunk.memblock) { -                iov[iov_idx].iov_base = (void*)((uint8_t*) chunk.memblock->data + chunk.index); -                iov[iov_idx].iov_len = k; -                mb[iov_idx] = chunk.memblock; -                iov_idx ++; +            pa_assert(chunk.memblock); -                n += k; -            } +            iov[iov_idx].iov_base = ((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index); +            iov[iov_idx].iov_len = k; +            mb[iov_idx] = chunk.memblock; +            iov_idx ++; -            skip += k; -            pa_memblockq_drop(q, &chunk, k); +            n += k; +            pa_memblockq_drop(q, k);          } -        if (r < 0 || !chunk.memblock || n >= size || iov_idx >= MAX_IOVECS) { +        pa_assert(n % c->frame_size == 0); + +        if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {              uint32_t header[3];              struct msghdr m;              int k, i; @@ -116,17 +119,19 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {                  k = sendmsg(c->fd, &m, MSG_DONTWAIT); -                for (i = 1; i < iov_idx; i++) +                for (i = 1; i < iov_idx; i++) { +                    pa_memblock_release(mb[i]);                      pa_memblock_unref(mb[i]); +                }                  c->sequence++;              } else                  k = 0; -            c->timestamp += skip/c->frame_size; +            c->timestamp += n/c->frame_size;              if (k < 0) { -                if (errno != EAGAIN) /* If the queue is full, just ignore it */ +                if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */                      pa_log("sendmsg() failed: %s", pa_cstrerror(errno));                  return -1;              } @@ -135,7 +140,6 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {                  break;              n = 0; -            skip = 0;              iov_idx = 1;          }      } @@ -144,7 +148,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {  }  pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size) { -    assert(c); +    pa_assert(c);      c->fd = fd;      c->frame_size = frame_size; @@ -159,13 +163,13 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      int cc;      ssize_t r; -    assert(c); -    assert(chunk); +    pa_assert(c); +    pa_assert(chunk); -    chunk->memblock = NULL; +    pa_memchunk_reset(chunk);      if (ioctl(c->fd, FIONREAD, &size) < 0) { -        pa_log("FIONREAD failed: %s", pa_cstrerror(errno)); +        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));          goto fail;      } @@ -174,7 +178,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      chunk->memblock = pa_memblock_new(pool, size); -    iov.iov_base = chunk->memblock->data; +    iov.iov_base = pa_memblock_acquire(chunk->memblock);      iov.iov_len = size;      m.msg_name = NULL; @@ -185,36 +189,41 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      m.msg_controllen = 0;      m.msg_flags = 0; -    if ((r = recvmsg(c->fd, &m, 0)) != size) { -        pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch"); +    r = recvmsg(c->fd, &m, 0); +    pa_memblock_release(chunk->memblock); + +    if (r != size) { +        if (r < 0 && errno != EAGAIN && errno != EINTR) +            pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch"); +          goto fail;      }      if (size < 12) { -        pa_log("RTP packet too short."); +        pa_log_warn("RTP packet too short.");          goto fail;      } -    memcpy(&header, chunk->memblock->data, sizeof(uint32_t)); -    memcpy(&c->timestamp, (uint8_t*) chunk->memblock->data + 4, sizeof(uint32_t)); -    memcpy(&c->ssrc, (uint8_t*) chunk->memblock->data + 8, sizeof(uint32_t)); +    memcpy(&header, iov.iov_base, sizeof(uint32_t)); +    memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t)); +    memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));      header = ntohl(header);      c->timestamp = ntohl(c->timestamp);      c->ssrc = ntohl(c->ssrc);      if ((header >> 30) != 2) { -        pa_log("Unsupported RTP version."); +        pa_log_warn("Unsupported RTP version.");          goto fail;      }      if ((header >> 29) & 1) { -        pa_log("RTP padding not supported."); +        pa_log_warn("RTP padding not supported.");          goto fail;      }      if ((header >> 28) & 1) { -        pa_log("RTP header extensions not supported."); +        pa_log_warn("RTP header extensions not supported.");          goto fail;      } @@ -223,7 +232,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      c->sequence = header & 0xFFFF;      if (12 + cc*4 > size) { -        pa_log("RTP packet too short. (CSRC)"); +        pa_log_warn("RTP packet too short. (CSRC)");          goto fail;      } @@ -231,7 +240,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      chunk->length = size - chunk->index;      if (chunk->length % c->frame_size != 0) { -        pa_log("Vad RTP packet size."); +        pa_log_warn("Bad RTP packet size.");          goto fail;      } @@ -245,7 +254,7 @@ fail:  }  uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) { -    assert(ss); +    pa_assert(ss);      if (ss->format == PA_SAMPLE_ULAW && ss->rate == 8000 && ss->channels == 1)          return 0; @@ -260,7 +269,7 @@ uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {  }  pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss) { -    assert(ss); +    pa_assert(ss);      switch (payload) {          case 0: @@ -295,17 +304,17 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec  }  pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) { -    assert(ss); +    pa_assert(ss);      if (!pa_rtp_sample_spec_valid(ss))          ss->format = PA_SAMPLE_S16BE; -    assert(pa_rtp_sample_spec_valid(ss)); +    pa_assert(pa_rtp_sample_spec_valid(ss));      return ss;  }  int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) { -    assert(ss); +    pa_assert(ss);      if (!pa_sample_spec_valid(ss))          return 0; @@ -318,9 +327,9 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {  }  void pa_rtp_context_destroy(pa_rtp_context *c) { -    assert(c); +    pa_assert(c); -    close(c->fd); +    pa_close(c->fd);  }  const char* pa_rtp_format_to_string(pa_sample_format_t f) { @@ -339,7 +348,7 @@ const char* pa_rtp_format_to_string(pa_sample_format_t f) {  }  pa_sample_format_t pa_rtp_string_to_format(const char *s) { -    assert(s); +    pa_assert(s);      if (!(strcmp(s, "L16")))          return PA_SAMPLE_S16BE; diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c index 82421807..ed7eb0be 100644 --- a/src/modules/rtp/sap.c +++ b/src/modules/rtp/sap.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <time.h>  #include <stdlib.h>  #include <sys/types.h> @@ -46,6 +45,7 @@  #include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "sap.h"  #include "sdp.h" @@ -53,9 +53,9 @@  #define MIME_TYPE "application/sdp"  pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) { -    assert(c); -    assert(fd >= 0); -    assert(sdp_data); +    pa_assert(c); +    pa_assert(fd >= 0); +    pa_assert(sdp_data);      c->fd = fd;      c->sdp_data = sdp_data; @@ -65,9 +65,9 @@ pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_da  }  void pa_sap_context_destroy(pa_sap_context *c) { -    assert(c); +    pa_assert(c); -    close(c->fd); +    pa_close(c->fd);      pa_xfree(c->sdp_data);  } @@ -85,7 +85,7 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {          return -1;      } -    assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); +    pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);      header = htonl(((uint32_t) 1 << 29) |                     (sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) | @@ -113,14 +113,14 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {      m.msg_flags = 0;      if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0) -        pa_log("sendmsg() failed: %s\n", pa_cstrerror(errno)); +        pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno));      return k;  }  pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) { -    assert(c); -    assert(fd >= 0); +    pa_assert(c); +    pa_assert(fd >= 0);      c->fd = fd;      c->sdp_data = NULL; @@ -136,11 +136,11 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {      int six, ac;      ssize_t r; -    assert(c); -    assert(goodbye); +    pa_assert(c); +    pa_assert(goodbye);      if (ioctl(c->fd, FIONREAD, &size) < 0) { -        pa_log("FIONREAD failed: %s", pa_cstrerror(errno)); +        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));          goto fail;      } @@ -159,12 +159,12 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {      m.msg_flags = 0;      if ((r = recvmsg(c->fd, &m, 0)) != size) { -        pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch"); +        pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");          goto fail;      }      if (size < 4) { -        pa_log("SAP packet too short."); +        pa_log_warn("SAP packet too short.");          goto fail;      } @@ -172,17 +172,17 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {      header = ntohl(header);      if (header >> 29 != 1) { -        pa_log("Unsupported SAP version."); +        pa_log_warn("Unsupported SAP version.");          goto fail;      }      if ((header >> 25) & 1) { -        pa_log("Encrypted SAP not supported."); +        pa_log_warn("Encrypted SAP not supported.");          goto fail;      }      if ((header >> 24) & 1) { -        pa_log("Compressed SAP not supported."); +        pa_log_warn("Compressed SAP not supported.");          goto fail;      } @@ -191,7 +191,7 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {      k = 4 + (six ? 16 : 4) + ac*4;      if (size < k) { -        pa_log("SAP packet too short (AD)."); +        pa_log_warn("SAP packet too short (AD).");          goto fail;      } @@ -202,7 +202,7 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {          e += sizeof(MIME_TYPE);          size -= sizeof(MIME_TYPE);      } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) { -        pa_log("Invalid SDP header."); +        pa_log_warn("Invalid SDP header.");          goto fail;      } diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index 8b0bd535..50ac157a 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <time.h>  #include <stdlib.h>  #include <sys/types.h> @@ -35,36 +34,33 @@  #include <string.h>  #include <pulse/xmalloc.h> +#include <pulse/util.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "sdp.h"  #include "rtp.h" -  char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {      uint32_t ntp; -    char buf_src[64], buf_dst[64]; +    char buf_src[64], buf_dst[64], un[64];      const char *u, *f, *a; -    assert(src); -    assert(dst); -    assert(af == AF_INET || af == AF_INET6); +    pa_assert(src); +    pa_assert(dst); +    pa_assert(af == AF_INET || af == AF_INET6); -    f = pa_rtp_format_to_string(ss->format); -    assert(f); +    pa_assert_se(f = pa_rtp_format_to_string(ss->format)); -    if (!(u = getenv("USER"))) -        if (!(u = getenv("USERNAME"))) -            u = "-"; +    if (!(u = pa_get_user_name(un, sizeof(un)))) +        u = "-";      ntp = time(NULL) + 2208988800U; -    a = inet_ntop(af, src, buf_src, sizeof(buf_src)); -    assert(a); -    a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst)); -    assert(a); +    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)));      return pa_sprintf_malloc(              PA_SDP_HEADER @@ -86,8 +82,8 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u  static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {      unsigned rate, channels; -    assert(ss); -    assert(c); +    pa_assert(ss); +    pa_assert(c);      if (pa_startswith(c, "L16/")) {          ss->format = PA_SAMPLE_S16BE; @@ -123,8 +119,8 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {      uint16_t port = 0;      int ss_valid = 0; -    assert(t); -    assert(i); +    pa_assert(t); +    pa_assert(i);      i->origin = i->session_name = NULL;      i->salen = 0; @@ -258,7 +254,7 @@ fail:  }  void pa_sdp_info_destroy(pa_sdp_info *i) { -    assert(i); +    pa_assert(i);      pa_xfree(i->origin);      pa_xfree(i->session_name); diff --git a/src/pulse/browser.c b/src/pulse/browser.c index ea2706e4..55e0b2cd 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -25,7 +25,6 @@  #include "config.h"  #endif -#include <assert.h>  #include <string.h>  #include <avahi-client/lookup.h> @@ -36,8 +35,9 @@  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> -  #include <pulsecore/avahi-wrap.h> +#include <pulsecore/refcnt.h> +#include <pulsecore/macro.h>  #include "browser.h" @@ -46,7 +46,8 @@  #define SERVICE_TYPE_SERVER "_pulse-server._tcp."  struct pa_browser { -    int ref; +    PA_REFCNT_DECLARE; +      pa_mainloop_api *mainloop;      AvahiPoll* avahi_poll; @@ -62,6 +63,7 @@ struct pa_browser {  };  static int map_to_opcode(const char *type, int new) { +      if (avahi_domain_equal(type, SERVICE_TYPE_SINK))          return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;      else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE)) @@ -97,7 +99,8 @@ static void resolve_callback(      int ss_valid = 0;      char *key = NULL, *value = NULL; -    assert(b); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      memset(&i, 0, sizeof(i));      i.name = name; @@ -109,13 +112,13 @@ static void resolve_callback(          goto fail;      opcode = map_to_opcode(type, 1); -    assert(opcode >= 0); +    pa_assert(opcode >= 0);      if (aa->proto == AVAHI_PROTO_INET) -        snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); +        pa_snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);      else { -        assert(aa->proto == AVAHI_PROTO_INET6); -        snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); +        pa_assert(aa->proto == AVAHI_PROTO_INET6); +        pa_snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);      }      i.server = a; @@ -146,7 +149,7 @@ static void resolve_callback(              value = NULL;              l = strlen(a); -            assert(l+1 <= sizeof(a)); +            pa_assert(l+1 <= sizeof(a));              strncat(a, " ", sizeof(a)-l-1);              strncat(a, i.fqdn, sizeof(a)-l-2);          } else if (!strcmp(key, "cookie")) { @@ -211,7 +214,9 @@ fail:  static void handle_failure(pa_browser *b) {      const char *e = NULL; -    assert(b); + +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      if (b->sink_browser)          avahi_service_browser_free(b->sink_browser); @@ -245,7 +250,9 @@ static void browse_callback(          void *userdata) {      pa_browser *b = userdata; -    assert(b); + +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      switch (event) {          case AVAHI_BROWSER_NEW: { @@ -276,7 +283,7 @@ static void browse_callback(                  i.name = name;                  opcode = map_to_opcode(type, 0); -                assert(opcode >= 0); +                pa_assert(opcode >= 0);                  b->callback(b, opcode, &i, b->userdata);              } @@ -295,7 +302,10 @@ static void browse_callback(  static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {      pa_browser *b = userdata; -    assert(s); + +    pa_assert(s); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      if (state == AVAHI_CLIENT_FAILURE)          handle_failure(b); @@ -311,14 +321,14 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla      pa_browser *b;      int error; -    assert(mainloop); +    pa_assert(mainloop);      if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)          return NULL;      b = pa_xnew(pa_browser, 1);      b->mainloop = mainloop; -    b->ref = 1; +    PA_REFCNT_INIT(b);      b->callback = NULL;      b->userdata = NULL;      b->error_callback = NULL; @@ -391,7 +401,8 @@ fail:  }  static void browser_free(pa_browser *b) { -    assert(b && b->mainloop); +    pa_assert(b); +    pa_assert(b->mainloop);      if (b->sink_browser)          avahi_service_browser_free(b->sink_browser); @@ -410,29 +421,32 @@ static void browser_free(pa_browser *b) {  }  pa_browser *pa_browser_ref(pa_browser *b) { -    assert(b); -    assert(b->ref >= 1); -    b->ref++; +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1); + +    PA_REFCNT_INC(b);      return b;  }  void pa_browser_unref(pa_browser *b) { -    assert(b); -    assert(b->ref >= 1); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1); -    if ((-- (b->ref)) <= 0) +    if (PA_REFCNT_DEC(b) <= 0)          browser_free(b);  }  void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { -    assert(b); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      b->callback = cb;      b->userdata = userdata;  }  void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) { -    assert(b); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) >= 1);      b->error_callback = cb;      b->error_userdata = userdata; diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index 922ad276..e1f23d25 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -41,4 +41,22 @@  #endif +#ifndef PA_GCC_PURE +#ifdef __GNUCC__ +#define PA_GCC_PURE __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list and global state **/ +#define PA_GCC_PURE +#endif +#endif + +#ifndef PA_GCC_CONST +#ifdef __GNUCC__ +#define PA_GCC_CONST __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list (stricter version of PA_GCC_CONST) **/ +#define PA_GCC_CONST +#endif +#endif +  #endif diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index d5b8f743..2b8ef2b0 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -27,12 +27,12 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <stdio.h>  #include <string.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "channelmap.h" @@ -90,18 +90,81 @@ const char *const table[] = {      [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", +    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",      [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",      [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right", -    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", +    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",      [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left", -    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right", -    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center" +    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right" +}; + +const char *const pretty_table[] = { +    [PA_CHANNEL_POSITION_MONO] = "Mono", + +    [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center", +    [PA_CHANNEL_POSITION_FRONT_LEFT] = "Front Left", +    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "Front Right", + +    [PA_CHANNEL_POSITION_REAR_CENTER] = "Rear Center", +    [PA_CHANNEL_POSITION_REAR_LEFT] = "Rear Left", +    [PA_CHANNEL_POSITION_REAR_RIGHT] = "Rear Right", + +    [PA_CHANNEL_POSITION_LFE] = "Low Frequency Emmiter", + +    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "Front Left-of-center", +    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "Front Right-of-center", + +    [PA_CHANNEL_POSITION_SIDE_LEFT] = "Side Left", +    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "Side Right", + +    [PA_CHANNEL_POSITION_AUX0] = "Auxiliary 0", +    [PA_CHANNEL_POSITION_AUX1] = "Auxiliary 1", +    [PA_CHANNEL_POSITION_AUX2] = "Auxiliary 2", +    [PA_CHANNEL_POSITION_AUX3] = "Auxiliary 3", +    [PA_CHANNEL_POSITION_AUX4] = "Auxiliary 4", +    [PA_CHANNEL_POSITION_AUX5] = "Auxiliary 5", +    [PA_CHANNEL_POSITION_AUX6] = "Auxiliary 6", +    [PA_CHANNEL_POSITION_AUX7] = "Auxiliary 7", +    [PA_CHANNEL_POSITION_AUX8] = "Auxiliary 8", +    [PA_CHANNEL_POSITION_AUX9] = "Auxiliary 9", +    [PA_CHANNEL_POSITION_AUX10] = "Auxiliary 10", +    [PA_CHANNEL_POSITION_AUX11] = "Auxiliary 11", +    [PA_CHANNEL_POSITION_AUX12] = "Auxiliary 12", +    [PA_CHANNEL_POSITION_AUX13] = "Auxiliary 13", +    [PA_CHANNEL_POSITION_AUX14] = "Auxiliary 14", +    [PA_CHANNEL_POSITION_AUX15] = "Auxiliary 15", +    [PA_CHANNEL_POSITION_AUX16] = "Auxiliary 16", +    [PA_CHANNEL_POSITION_AUX17] = "Auxiliary 17", +    [PA_CHANNEL_POSITION_AUX18] = "Auxiliary 18", +    [PA_CHANNEL_POSITION_AUX19] = "Auxiliary 19", +    [PA_CHANNEL_POSITION_AUX20] = "Auxiliary 20", +    [PA_CHANNEL_POSITION_AUX21] = "Auxiliary 21", +    [PA_CHANNEL_POSITION_AUX22] = "Auxiliary 22", +    [PA_CHANNEL_POSITION_AUX23] = "Auxiliary 23", +    [PA_CHANNEL_POSITION_AUX24] = "Auxiliary 24", +    [PA_CHANNEL_POSITION_AUX25] = "Auxiliary 25", +    [PA_CHANNEL_POSITION_AUX26] = "Auxiliary 26", +    [PA_CHANNEL_POSITION_AUX27] = "Auxiliary 27", +    [PA_CHANNEL_POSITION_AUX28] = "Auxiliary 28", +    [PA_CHANNEL_POSITION_AUX29] = "Auxiliary 29", +    [PA_CHANNEL_POSITION_AUX30] = "Auxiliary 30", +    [PA_CHANNEL_POSITION_AUX31] = "Auxiliary 31", + +    [PA_CHANNEL_POSITION_TOP_CENTER] = "Top Center", + +    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "Top Front Center", +    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "Top Front Left", +    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "Top Front Right", + +    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "Top Rear Center", +    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "Top Rear left", +    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "Top Rear Right"  };  pa_channel_map* pa_channel_map_init(pa_channel_map *m) {      unsigned c; -    assert(m); +    pa_assert(m);      m->channels = 0; @@ -112,7 +175,7 @@ pa_channel_map* pa_channel_map_init(pa_channel_map *m) {  }  pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { -    assert(m); +    pa_assert(m);      pa_channel_map_init(m); @@ -122,7 +185,7 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {  }  pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { -    assert(m); +    pa_assert(m);      pa_channel_map_init(m); @@ -133,9 +196,9 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {  }  pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { -    assert(m); -    assert(channels > 0); -    assert(channels <= PA_CHANNELS_MAX); +    pa_assert(m); +    pa_assert(channels > 0); +    pa_assert(channels <= PA_CHANNELS_MAX);      pa_channel_map_init(m); @@ -342,11 +405,18 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) {      return table[pos];  } +const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { +    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) +        return NULL; + +    return pretty_table[pos]; +} +  int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {      unsigned c; -    assert(a); -    assert(b); +    pa_assert(a); +    pa_assert(b);      if (a->channels != b->channels)          return 0; @@ -363,14 +433,14 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {      int first = 1;      char *e; -    assert(s); -    assert(l > 0); -    assert(map); +    pa_assert(s); +    pa_assert(l > 0); +    pa_assert(map);      *(e = s) = 0;      for (channel = 0; channel < map->channels && l > 1; channel++) { -        l -= snprintf(e, l, "%s%s", +        l -= pa_snprintf(e, l, "%s%s",                        first ? "" : ",",                        pa_channel_position_to_string(map->map[channel])); @@ -386,8 +456,8 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {      pa_channel_map map;      char *p; -    assert(rmap); -    assert(s); +    pa_assert(rmap); +    pa_assert(s);      memset(&map, 0, sizeof(map)); @@ -447,7 +517,7 @@ finish:  int pa_channel_map_valid(const pa_channel_map *map) {      unsigned c; -    assert(map); +    pa_assert(map);      if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)          return 0; diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index f0c8f475..a05e1911 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -172,7 +172,10 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);  pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);  /** Return a text label for the specified channel position */ -const char* pa_channel_position_to_string(pa_channel_position_t pos); +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() */  #define PA_CHANNEL_MAP_SNPRINT_MAX 336 @@ -184,10 +187,10 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);  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); +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 */ -int pa_channel_map_valid(const pa_channel_map *map); +int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;  PA_C_DECL_END diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index e8de9553..e240ba88 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -26,7 +26,6 @@  #endif  #include <string.h> -#include <assert.h>  #include <X11/Xlib.h>  #include <X11/Xatom.h> @@ -36,6 +35,7 @@  #include <pulsecore/x11prop.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "client-conf-x11.h" @@ -44,6 +44,8 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {      int ret = -1;      char t[1024]; +    pa_assert(c); +      if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0'))          goto finish; @@ -75,7 +77,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {              goto finish;          } -        assert(sizeof(cookie) == sizeof(c->cookie)); +        pa_assert(sizeof(cookie) == sizeof(c->cookie));          memcpy(c->cookie, cookie, sizeof(cookie));          c->cookie_valid = 1; diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index bb912335..abd277a6 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -27,14 +27,14 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <unistd.h>  #include <errno.h>  #include <string.h> -#include <pulsecore/core-error.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-error.h>  #include <pulsecore/log.h>  #include <pulsecore/conf-parser.h>  #include <pulsecore/core-util.h> @@ -42,13 +42,7 @@  #include "client-conf.h" -#ifndef OS_IS_WIN32 -# define PATH_SEP "/" -#else -# define PATH_SEP "\\" -#endif - -#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf"  #define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf"  #define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG" @@ -81,7 +75,7 @@ pa_client_conf *pa_client_conf_new(void) {  }  void pa_client_conf_free(pa_client_conf *c) { -    assert(c); +    pa_assert(c);      pa_xfree(c->daemon_binary);      pa_xfree(c->extra_arguments);      pa_xfree(c->default_sink); @@ -90,6 +84,7 @@ void pa_client_conf_free(pa_client_conf *c) {      pa_xfree(c->cookie_file);      pa_xfree(c);  } +  int pa_client_conf_load(pa_client_conf *c, const char *filename) {      FILE *f = NULL;      char *fn = NULL; @@ -122,7 +117,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {          pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");      if (!f && errno != EINTR) { -        pa_log("WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));          goto finish;      } @@ -175,7 +170,7 @@ int pa_client_conf_env(pa_client_conf *c) {  }  int pa_client_conf_load_cookie(pa_client_conf* c) { -    assert(c); +    pa_assert(c);      c->cookie_valid = 0; diff --git a/src/pulse/context.c b/src/pulse/context.c index 58a5a879..805cd44e 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <sys/types.h> @@ -67,6 +66,7 @@  #include <pulsecore/log.h>  #include <pulsecore/socket-util.h>  #include <pulsecore/creds.h> +#include <pulsecore/macro.h>  #include "internal.h" @@ -90,7 +90,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {  };  static void unlock_autospawn_lock_file(pa_context *c) { -    assert(c); +    pa_assert(c);      if (c->autospawn_lock_fd >= 0) {          char lf[PATH_MAX]; @@ -106,11 +106,11 @@ static void context_free(pa_context *c);  pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {      pa_context *c; -    assert(mainloop); -    assert(name); +    pa_assert(mainloop); +    pa_assert(name);      c = pa_xnew(pa_context, 1); -    c->ref = 1; +    PA_REFCNT_INIT(c);      c->name = pa_xstrdup(name);      c->mainloop = mainloop;      c->client = NULL; @@ -168,7 +168,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {  }  static void context_free(pa_context *c) { -    assert(c); +    pa_assert(c);      unlock_autospawn_lock_file(c); @@ -183,7 +183,7 @@ static void context_free(pa_context *c) {      if (c->pdispatch)          pa_pdispatch_unref(c->pdispatch);      if (c->pstream) { -        pa_pstream_close(c->pstream); +        pa_pstream_unlink(c->pstream);          pa_pstream_unref(c->pstream);      } @@ -206,24 +206,24 @@ static void context_free(pa_context *c) {  }  pa_context* pa_context_ref(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); -    c->ref++; +    PA_REFCNT_INC(c);      return c;  }  void pa_context_unref(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); -    if (--c->ref <= 0) +    if (PA_REFCNT_DEC(c) <= 0)          context_free(c);  }  void pa_context_set_state(pa_context *c, pa_context_state_t st) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      if (c->state == st)          return; @@ -250,7 +250,7 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {          c->pdispatch = NULL;          if (c->pstream) { -            pa_pstream_close(c->pstream); +            pa_pstream_unlink(c->pstream);              pa_pstream_unref(c->pstream);          }          c->pstream = NULL; @@ -264,16 +264,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {  }  void pa_context_fail(pa_context *c, int error) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_set_error(c, error);      pa_context_set_state(c, PA_CONTEXT_FAILED);  }  int pa_context_set_error(pa_context *c, int error) { -    assert(error >= 0); -    assert(error < PA_ERR_MAX); +    pa_assert(error >= 0); +    pa_assert(error < PA_ERR_MAX);      if (c)          c->error = error; @@ -284,8 +284,8 @@ int pa_context_set_error(pa_context *c, int error) {  static void pstream_die_callback(pa_pstream *p, void *userdata) {      pa_context *c = userdata; -    assert(p); -    assert(c); +    pa_assert(p); +    pa_assert(c);      pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);  } @@ -293,9 +293,9 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) {  static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {      pa_context *c = userdata; -    assert(p); -    assert(packet); -    assert(c); +    pa_assert(p); +    pa_assert(packet); +    pa_assert(c);      pa_context_ref(c); @@ -309,18 +309,19 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o      pa_context *c = userdata;      pa_stream *s; -    assert(p); -    assert(chunk); -    assert(chunk->memblock); -    assert(chunk->length); -    assert(c); -    assert(c->ref >= 1); +    pa_assert(p); +    pa_assert(chunk); +    pa_assert(chunk->memblock); +    pa_assert(chunk->length); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_ref(c);      if ((s = pa_dynarray_get(c->record_streams, channel))) { -        assert(seek == PA_SEEK_RELATIVE && offset == 0); +        pa_assert(seek == PA_SEEK_RELATIVE); +        pa_assert(offset == 0);          pa_memblockq_seek(s->record_memblockq, offset, seek);          pa_memblockq_push_align(s->record_memblockq, chunk); @@ -337,11 +338,11 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o  }  int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      if (command == PA_COMMAND_ERROR) { -        assert(t); +        pa_assert(t);          if (pa_tagstruct_getu32(t, &c->error) < 0) {              pa_context_fail(c, PA_ERR_PROTOCOL); @@ -361,9 +362,9 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {  static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata; -    assert(pd); -    assert(c); -    assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME); +    pa_assert(pd); +    pa_assert(c); +    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);      pa_context_ref(c); @@ -423,7 +424,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t              break;          default: -            assert(0); +            pa_assert(0);      }  finish: @@ -434,19 +435,19 @@ static void setup_context(pa_context *c, pa_iochannel *io) {      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(io); +    pa_assert(c); +    pa_assert(io);      pa_context_ref(c); -    assert(!c->pstream); +    pa_assert(!c->pstream);      c->pstream = pa_pstream_new(c->mainloop, io, c->mempool);      pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);      pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);      pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); -    assert(!c->pdispatch); +    pa_assert(!c->pdispatch);      c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);      if (!c->conf->cookie_valid) @@ -497,10 +498,10 @@ static int context_connect_spawn(pa_context *c) {          goto fail;      } -    pa_fd_set_cloexec(fds[0], 1); +    pa_make_fd_cloexec(fds[0]); -    pa_socket_low_delay(fds[0]); -    pa_socket_low_delay(fds[1]); +    pa_make_socket_low_delay(fds[0]); +    pa_make_socket_low_delay(fds[1]);      if (c->spawn_api.prefork)          c->spawn_api.prefork(); @@ -523,7 +524,7 @@ static int context_connect_spawn(pa_context *c) {          int n;          /* Not required, since fds[0] has CLOEXEC enabled anyway */ -        close(fds[0]); +        pa_assert_se(pa_close(fds[0]) == 0);          if (c->spawn_api.atfork)              c->spawn_api.atfork(); @@ -535,7 +536,7 @@ static int context_connect_spawn(pa_context *c) {          argv[n++] = c->conf->daemon_binary;          argv[n++] = "--daemonize=yes"; -        snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); +        pa_snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);          argv[n++] = strdup(t);          while (n < MAX_ARGS) { @@ -570,7 +571,7 @@ static int context_connect_spawn(pa_context *c) {          goto fail;      } -    close(fds[1]); +    pa_assert_se(pa_close(fds[1]) == 0);      c->is_local = 1; @@ -584,10 +585,7 @@ static int context_connect_spawn(pa_context *c) {      return 0;  fail: -    if (fds[0] != -1) -        close(fds[0]); -    if (fds[1] != -1) -        close(fds[1]); +    pa_close_pipe(fds);      unlock_autospawn_lock_file(c); @@ -602,8 +600,8 @@ static int try_next_connection(pa_context *c) {      char *u = NULL;      int r = -1; -    assert(c); -    assert(!c->client); +    pa_assert(c); +    pa_assert(!c->client);      for (;;) {          pa_xfree(u); @@ -648,9 +646,9 @@ finish:  static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {      pa_context *c = userdata; -    assert(client); -    assert(c); -    assert(c->state == PA_CONTEXT_CONNECTING); +    pa_assert(client); +    pa_assert(c); +    pa_assert(c->state == PA_CONTEXT_CONNECTING);      pa_context_ref(c); @@ -683,8 +681,8 @@ int pa_context_connect(      int r = -1; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID); @@ -695,7 +693,7 @@ int pa_context_connect(      pa_context_ref(c); -    assert(!c->server_list); +    pa_assert(!c->server_list);      if (server) {          if (!(c->server_list = pa_strlist_parse(server))) { @@ -735,7 +733,7 @@ int pa_context_connect(              pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));              pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1); -            assert(c->autospawn_lock_fd <= 0); +            pa_assert(c->autospawn_lock_fd <= 0);              c->autospawn_lock_fd = pa_lock_lockfile(lf);              if (api) @@ -755,37 +753,37 @@ finish:  }  void pa_context_disconnect(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_set_state(c, PA_CONTEXT_TERMINATED);  }  pa_context_state_t pa_context_get_state(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      return c->state;  }  int pa_context_errno(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      return c->error;  }  void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      c->state_callback = cb;      c->state_userdata = userdata;  }  int pa_context_is_pending(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY(c,                        c->state == PA_CONTEXT_CONNECTING || @@ -811,11 +809,11 @@ static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata)  static void set_dispatch_callbacks(pa_operation *o) {      int done = 1; -    assert(o); -    assert(o->ref >= 1); -    assert(o->context); -    assert(o->context->ref >= 1); -    assert(o->context->state == PA_CONTEXT_READY); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1); +    pa_assert(o->context); +    pa_assert(PA_REFCNT_VALUE(o->context) >= 1); +    pa_assert(o->context->state == PA_CONTEXT_READY);      pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);      pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); @@ -844,8 +842,8 @@ static void set_dispatch_callbacks(pa_operation *o) {  pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {      pa_operation *o; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE); @@ -860,9 +858,9 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U      pa_operation *o = userdata;      int success = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -892,8 +890,8 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb,      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -911,8 +909,8 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -930,8 +928,8 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -950,8 +948,8 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -966,7 +964,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_  }  int pa_context_is_local(pa_context *c) { -    assert(c); +    pa_assert(c);      return c->is_local;  } @@ -976,9 +974,9 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(name); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(name);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -997,8 +995,8 @@ const char* pa_get_library_version(void) {  }  const char* pa_context_get_server(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      if (!c->server)          return NULL; @@ -1016,8 +1014,8 @@ uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) {  }  uint32_t pa_context_get_server_protocol_version(pa_context *c) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      return c->version;  } @@ -1025,8 +1023,8 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {  pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {      pa_tagstruct *t; -    assert(c); -    assert(tag); +    pa_assert(c); +    pa_assert(tag);      t = pa_tagstruct_new(NULL, 0);      pa_tagstruct_putu32(t, command); diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 5b399aa2..b7a7537a 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -25,8 +25,6 @@  #include <config.h>  #endif -#include <assert.h> -  #include <pulse/xmalloc.h>  #include <pulse/timeval.h> diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 52354fdc..95593adb 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -41,13 +41,14 @@  #include <pulsecore/mcalign.h>  #include <pulsecore/memblockq.h>  #include <pulsecore/hashmap.h> +#include <pulsecore/refcnt.h>  #include "client-conf.h"  #define DEFAULT_TIMEOUT (30)  struct pa_context { -    int ref; +    PA_REFCNT_DECLARE;      char *name;      pa_mainloop_api* mainloop; @@ -96,7 +97,7 @@ typedef struct pa_index_correction {  } pa_index_correction;  struct pa_stream { -    int ref; +    PA_REFCNT_DECLARE;      pa_context *context;      pa_mainloop_api *mainloop;      PA_LLIST_FIELDS(pa_stream); @@ -116,6 +117,7 @@ struct pa_stream {      uint32_t requested_bytes;      pa_memchunk peek_memchunk; +    void *peek_data;      pa_memblockq *record_memblockq;      int corked; @@ -160,7 +162,8 @@ struct pa_stream {  typedef void (*pa_operation_cb_t)(void);  struct pa_operation { -    int ref; +    PA_REFCNT_DECLARE; +      pa_context *context;      pa_stream *stream; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 7f6406cf..6610a724 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -26,11 +26,10 @@  #include <config.h>  #endif -#include <assert.h> -  #include <pulse/context.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include <pulsecore/pstream-util.h>  #include "internal.h" @@ -43,9 +42,11 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU      pa_operation *o = userdata;      pa_stat_info i, *p = &i; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1); + +    memset(&i, 0, sizeof(i));      if (!o->context)          goto finish; @@ -59,8 +60,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU                 pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||                 pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||                 pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || -               pa_tagstruct_getu32(t, &i.scache_size) < 0 || -               !pa_tagstruct_eof(t)) { +               pa_tagstruct_getu32(t, &i.scache_size) < 0) {          pa_context_fail(o->context, PA_ERR_PROTOCOL);          goto finish;      } @@ -85,9 +85,11 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,      pa_operation *o = userdata;      pa_server_info i, *p = &i; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1); + +    memset(&i, 0, sizeof(i));      if (!o->context)          goto finish; @@ -104,8 +106,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,                 pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||                 pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||                 pa_tagstruct_gets(t, &i.default_source_name) < 0 || -               pa_tagstruct_getu32(t, &i.cookie) < 0 || -               !pa_tagstruct_eof(t)) { +               pa_tagstruct_getu32(t, &i.cookie) < 0) {          pa_context_fail(o->context, PA_ERR_PROTOCOL);          goto finish; @@ -131,9 +132,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -148,6 +149,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P          while (!pa_tagstruct_eof(t)) {              pa_sink_info i; +            memset(&i, 0, sizeof(i));              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 || @@ -195,9 +197,9 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -217,9 +219,9 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name,      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -241,9 +243,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -258,6 +260,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,          while (!pa_tagstruct_eof(t)) {              pa_source_info i;              uint32_t flags; +            memset(&i, 0, sizeof(i));              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 || @@ -305,9 +308,9 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -327,9 +330,9 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -351,9 +354,9 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -367,6 +370,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,          while (!pa_tagstruct_eof(t)) {              pa_client_info i; +            memset(&i, 0, sizeof(i));              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 || @@ -398,9 +402,9 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -425,9 +429,9 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -441,6 +445,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,          while (!pa_tagstruct_eof(t)) {              pa_module_info i; +            memset(&i, 0, sizeof(i));              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 || @@ -473,9 +478,9 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -500,9 +505,9 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -516,6 +521,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm          while (!pa_tagstruct_eof(t)) {              pa_sink_input_info i; +            memset(&i, 0, sizeof(i));              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 || @@ -528,7 +534,8 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm                  pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||                  pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||                  pa_tagstruct_gets(t, &i.resample_method) < 0 || -                pa_tagstruct_gets(t, &i.driver) < 0) { +                pa_tagstruct_gets(t, &i.driver) < 0 || +                (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0)) {                  pa_context_fail(o->context, PA_ERR_PROTOCOL);                  goto finish; @@ -556,9 +563,9 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -583,9 +590,9 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -600,6 +607,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c          while (!pa_tagstruct_eof(t)) {              pa_source_output_info i; +            memset(&i, 0, sizeof(i)); +              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 ||                  pa_tagstruct_getu32(t, &i.owner_module) < 0 || @@ -638,9 +647,9 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -666,9 +675,9 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(volume); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(volume);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -690,10 +699,10 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(name); -    assert(volume); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(name); +    pa_assert(volume);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -716,8 +725,8 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -738,9 +747,9 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name,      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(name); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(name);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -762,9 +771,9 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(volume); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(volume);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -781,14 +790,37 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons      return o;  } +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { +    pa_operation *o; +    pa_tagstruct *t; +    uint32_t tag; + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + +    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_MUTE, &tag); +    pa_tagstruct_putu32(t, idx); +    pa_tagstruct_put_boolean(t, mute); +    pa_pstream_send_tagstruct(c->pstream, t); +    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + +    return o; +} +  pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {      pa_operation *o;      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(volume); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(volume);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -810,10 +842,10 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(name); -    assert(volume); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(name); +    pa_assert(volume);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -836,8 +868,8 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -858,9 +890,9 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(name); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(name);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -883,9 +915,9 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -900,6 +932,8 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,          while (!pa_tagstruct_eof(t)) {              pa_sample_info i; +            memset(&i, 0, sizeof(i)); +              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 ||                  pa_tagstruct_get_cvolume(t, &i.volume) < 0 || @@ -936,9 +970,9 @@ pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -959,9 +993,9 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -986,8 +1020,8 @@ static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx,      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1018,9 +1052,9 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN      pa_operation *o = userdata;      uint32_t idx; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -1052,8 +1086,8 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1079,9 +1113,9 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman      pa_operation *o = userdata;      int eol = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -1096,6 +1130,8 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman          while (!pa_tagstruct_eof(t)) {              pa_autoload_info i; +            memset(&i, 0, sizeof(i)); +              if (pa_tagstruct_getu32(t, &i.index) < 0 ||                  pa_tagstruct_gets(t, &i.name) < 0 ||                  pa_tagstruct_getu32(t, &i.type) < 0 || @@ -1127,9 +1163,9 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1151,9 +1187,9 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx,      pa_operation *o;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); -    assert(cb); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(cb);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1177,8 +1213,8 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1203,8 +1239,8 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1226,8 +1262,8 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1247,8 +1283,8 @@ pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, ch      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1272,8 +1308,8 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1297,8 +1333,8 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx,      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1322,8 +1358,8 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1341,3 +1377,97 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx      return o;  } + +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { +    pa_operation *o; +    pa_tagstruct *t; +    uint32_t tag; + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); +    PA_CHECK_VALIDITY_RETURN_NULL(c, !sink_name || *sink_name, PA_ERR_INVALID); + +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + +    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag); +    pa_tagstruct_putu32(t, PA_INVALID_INDEX); +    pa_tagstruct_puts(t, sink_name); +    pa_tagstruct_put_boolean(t, suspend); +    pa_pstream_send_tagstruct(c->pstream, t); +    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + +    return o; +} + +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { +    pa_operation *o; +    pa_tagstruct *t; +    uint32_t tag; + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + +    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag); +    pa_tagstruct_putu32(t, idx); +    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL); +    pa_tagstruct_put_boolean(t, suspend); +    pa_pstream_send_tagstruct(c->pstream, t); +    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + +    return o; +} + +pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { +    pa_operation *o; +    pa_tagstruct *t; +    uint32_t tag; + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); +    PA_CHECK_VALIDITY_RETURN_NULL(c, !source_name || *source_name, PA_ERR_INVALID); + +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + +    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag); +    pa_tagstruct_putu32(t, PA_INVALID_INDEX); +    pa_tagstruct_puts(t, source_name); +    pa_tagstruct_put_boolean(t, suspend); +    pa_pstream_send_tagstruct(c->pstream, t); +    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + +    return o; +} + +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { +    pa_operation *o; +    pa_tagstruct *t; +    uint32_t tag; + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + +    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag); +    pa_tagstruct_putu32(t, idx); +    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL); +    pa_tagstruct_put_boolean(t, suspend); +    pa_pstream_send_tagstruct(c->pstream, t); +    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + +    return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 43e430b2..c148ee5e 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -331,6 +331,7 @@ typedef struct pa_sink_input_info {      pa_usec_t sink_usec;                 /**< Latency of the sink device, see pa_latency_info for details */      const char *resample_method;         /**< Thre resampling method used by this sink input. \since 0.7 */      const char *driver;                  /**< Driver name \since 0.8 */ +    int mute;                            /**< Stream muted \since 0.9.7 */  } pa_sink_input_info;  /** Callback prototype for pa_context_get_sink_input_info() and firends*/ @@ -381,6 +382,9 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name,  /** Set the volume of a sink input stream */  pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); +/** Set the mute switch of a sink input stream \since 0.9.7 */ +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); +  /** Set the volume of a source device specified by its index \since 0.8 */  pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); @@ -499,6 +503,18 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx,  /** Move the specified source output to a different source. \since 0.9.5 */  pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata); +/** Suspend/Resume a sink. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend,  pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a source. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); +  PA_C_DECL_END  #endif diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index 001ff314..b2ed3434 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -25,12 +25,12 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include "mainloop-api.h" @@ -41,32 +41,38 @@ struct once_info {  static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {      struct once_info *i = userdata; -    assert(m && i && i->callback); +    pa_assert(m); +    pa_assert(i); + +    pa_assert(i->callback);      i->callback(m, i->userdata); -    assert(m->defer_free); +    pa_assert(m->defer_free);      m->defer_free(e);  }  static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {      struct once_info *i = userdata; -    assert(m && i); + +    pa_assert(m); +    pa_assert(i);      pa_xfree(i);  }  void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {      struct once_info *i;      pa_defer_event *e; -    assert(m && callback); + +    pa_assert(m); +    pa_assert(callback);      i = pa_xnew(struct once_info, 1);      i->callback = callback;      i->userdata = userdata; -    assert(m->defer_new); -    e = m->defer_new(m, once_callback, i); -    assert(e); +    pa_assert(m->defer_new); +    pa_assert_se(e = m->defer_new(m, once_callback, i));      m->defer_set_destroy(e, free_callback);  } diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 28ddec49..7d3017e2 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <signal.h>  #include <errno.h>  #include <stdlib.h> @@ -39,12 +38,13 @@  #include <windows.h>  #endif -#include <pulsecore/core-error.h>  #include <pulse/xmalloc.h> +#include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include "mainloop-signal.h" @@ -74,11 +74,11 @@ static void signal_handler(int sig) {  }  static void dispatch(pa_mainloop_api*a, int sig) { -    pa_signal_event*s; +    pa_signal_event *s;      for (s = signals; s; s = s->next)          if (s->sig == sig) { -            assert(s->callback); +            pa_assert(s->callback);              s->callback(a, s, sig, s->userdata);              break;          } @@ -87,7 +87,12 @@ 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) {      ssize_t r;      int sig; -    assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); + +    pa_assert(a); +    pa_assert(e); +    pa_assert(f == PA_IO_EVENT_INPUT); +    pa_assert(e == io_event); +    pa_assert(fd == signal_pipe[0]);      if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) {          if (errno == EAGAIN) @@ -107,28 +112,34 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags  int pa_signal_init(pa_mainloop_api *a) { -    assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event); +    pa_assert(a); +    pa_assert(!api); +    pa_assert(signal_pipe[0] == -1); +    pa_assert(signal_pipe[1] == -1); +    pa_assert(!io_event);      if (pipe(signal_pipe) < 0) {          pa_log("pipe(): %s", pa_cstrerror(errno));          return -1;      } -    pa_make_nonblock_fd(signal_pipe[0]); -    pa_make_nonblock_fd(signal_pipe[1]); -    pa_fd_set_cloexec(signal_pipe[0], 1); -    pa_fd_set_cloexec(signal_pipe[1], 1); +    pa_make_fd_nonblock(signal_pipe[0]); +    pa_make_fd_nonblock(signal_pipe[1]); +    pa_make_fd_cloexec(signal_pipe[0]); +    pa_make_fd_cloexec(signal_pipe[1]);      api = a; -    io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); -    assert(io_event); +    pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL));      return 0;  }  void pa_signal_done(void) { -    assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event); +    pa_assert(api); +    pa_assert(signal_pipe[0] >= 0); +    pa_assert(signal_pipe[1] >= 0); +    pa_assert(io_event);      while (signals)          pa_signal_free(signals); @@ -136,9 +147,7 @@ void pa_signal_done(void) {      api->io_free(io_event);      io_event = NULL; -    close(signal_pipe[0]); -    close(signal_pipe[1]); -    signal_pipe[0] = signal_pipe[1] = -1; +    pa_close_pipe(signal_pipe);      api = NULL;  } @@ -150,13 +159,14 @@ pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api,      struct sigaction sa;  #endif -    assert(sig > 0 && _callback); +    pa_assert(sig > 0); +    pa_assert(_callback);      for (e = signals; e; e = e->next)          if (e->sig == sig)              goto fail; -    e = pa_xmalloc(sizeof(pa_signal_event)); +    e = pa_xnew(pa_signal_event, 1);      e->sig = sig;      e->callback = _callback;      e->userdata = userdata; @@ -186,7 +196,7 @@ fail:  }  void pa_signal_free(pa_signal_event *e) { -    assert(e); +    pa_assert(e);      if (e->next)          e->next->previous = e->previous; @@ -196,9 +206,9 @@ void pa_signal_free(pa_signal_event *e) {          signals = e->next;  #ifdef HAVE_SIGACTION -    sigaction(e->sig, &e->saved_sigaction, NULL); +    pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0);  #else -    signal(e->sig, e->saved_handler); +    pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler);  #endif      if (e->destroy_callback) @@ -208,6 +218,7 @@ void pa_signal_free(pa_signal_event *e) {  }  void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { -    assert(e); +    pa_assert(e); +      e->destroy_callback = _callback;  } diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 43cbb19f..ad4e4e97 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -31,29 +31,28 @@  #include <unistd.h>  #include <stdlib.h>  #include <string.h> -#include <assert.h>  #include <fcntl.h>  #include <errno.h> -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> +#ifdef HAVE_POLL_H +#include <poll.h>  #else -#include "../pulsecore/poll.h" +#include <pulsecore/poll.h>  #endif -#include "../pulsecore/winsock.h" -  #ifndef HAVE_PIPE -#include "../pulsecore/pipe.h" +#include <pulsecore/pipe.h>  #endif -#include <pulsecore/core-error.h>  #include <pulse/timeval.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-util.h>  #include <pulsecore/llist.h>  #include <pulsecore/log.h> +#include <pulsecore/core-error.h> +#include <pulsecore/winsock.h> +#include <pulsecore/macro.h>  #include "mainloop.h" @@ -161,13 +160,13 @@ static pa_io_event* mainloop_io_new(      pa_mainloop *m;      pa_io_event *e; -    assert(a); -    assert(a->userdata); -    assert(fd >= 0); -    assert(callback); +    pa_assert(a); +    pa_assert(a->userdata); +    pa_assert(fd >= 0); +    pa_assert(callback);      m = a->userdata; -    assert(a == &m->api); +    pa_assert(a == &m->api);      e = pa_xnew(pa_io_event, 1);      e->mainloop = m; @@ -195,7 +194,7 @@ static pa_io_event* mainloop_io_new(          if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,                      SELECT_TYPE_ARG5 &tv) == -1) &&               (WSAGetLastError() == WSAENOTSOCK)) { -            pa_log_warn("WARNING: cannot monitor non-socket file descriptors."); +            pa_log_warn("Cannot monitor non-socket file descriptors.");              e->dead = 1;          }      } @@ -211,8 +210,8 @@ static pa_io_event* mainloop_io_new(  }  static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      if (e->events == events)          return; @@ -228,8 +227,8 @@ static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {  }  static void mainloop_io_free(pa_io_event *e) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      e->dead = 1;      e->mainloop->io_events_please_scan ++; @@ -241,7 +240,7 @@ static void mainloop_io_free(pa_io_event *e) {  }  static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) { -    assert(e); +    pa_assert(e);      e->destroy_callback = callback;  } @@ -255,12 +254,12 @@ static pa_defer_event* mainloop_defer_new(      pa_mainloop *m;      pa_defer_event *e; -    assert(a); -    assert(a->userdata); -    assert(callback); +    pa_assert(a); +    pa_assert(a->userdata); +    pa_assert(callback);      m = a->userdata; -    assert(a == &m->api); +    pa_assert(a == &m->api);      e = pa_xnew(pa_defer_event, 1);      e->mainloop = m; @@ -281,11 +280,11 @@ static pa_defer_event* mainloop_defer_new(  }  static void mainloop_defer_enable(pa_defer_event *e, int b) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      if (e->enabled && !b) { -        assert(e->mainloop->n_enabled_defer_events > 0); +        pa_assert(e->mainloop->n_enabled_defer_events > 0);          e->mainloop->n_enabled_defer_events--;      } else if (!e->enabled && b) {          e->mainloop->n_enabled_defer_events++; @@ -296,21 +295,22 @@ static void mainloop_defer_enable(pa_defer_event *e, int b) {  }  static void mainloop_defer_free(pa_defer_event *e) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      e->dead = 1;      e->mainloop->defer_events_please_scan ++;      if (e->enabled) { -        assert(e->mainloop->n_enabled_defer_events > 0); +        pa_assert(e->mainloop->n_enabled_defer_events > 0);          e->mainloop->n_enabled_defer_events--; +        e->enabled = 0;      }  }  static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      e->destroy_callback = callback;  } @@ -325,12 +325,12 @@ static pa_time_event* mainloop_time_new(      pa_mainloop *m;      pa_time_event *e; -    assert(a); -    assert(a->userdata); -    assert(callback); +    pa_assert(a); +    pa_assert(a->userdata); +    pa_assert(callback);      m = a->userdata; -    assert(a == &m->api); +    pa_assert(a == &m->api);      e = pa_xnew(pa_time_event, 1);      e->mainloop = m; @@ -342,7 +342,7 @@ static pa_time_event* mainloop_time_new(          m->n_enabled_time_events++;          if (m->cached_next_time_event) { -            assert(m->cached_next_time_event->enabled); +            pa_assert(m->cached_next_time_event->enabled);              if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0)                  m->cached_next_time_event = e; @@ -362,11 +362,11 @@ static pa_time_event* mainloop_time_new(  }  static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      if (e->enabled && !tv) { -        assert(e->mainloop->n_enabled_time_events > 0); +        pa_assert(e->mainloop->n_enabled_time_events > 0);          e->mainloop->n_enabled_time_events--;      } else if (!e->enabled && tv)          e->mainloop->n_enabled_time_events++; @@ -377,7 +377,7 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {      }      if (e->mainloop->cached_next_time_event && e->enabled) { -        assert(e->mainloop->cached_next_time_event->enabled); +        pa_assert(e->mainloop->cached_next_time_event->enabled);          if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)              e->mainloop->cached_next_time_event = e; @@ -386,15 +386,16 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {  }  static void mainloop_time_free(pa_time_event *e) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      e->dead = 1;      e->mainloop->time_events_please_scan ++;      if (e->enabled) { -        assert(e->mainloop->n_enabled_time_events > 0); +        pa_assert(e->mainloop->n_enabled_time_events > 0);          e->mainloop->n_enabled_time_events--; +        e->enabled = 0;      }      if (e->mainloop->cached_next_time_event == e) @@ -404,8 +405,8 @@ static void mainloop_time_free(pa_time_event *e) {  }  static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) { -    assert(e); -    assert(!e->dead); +    pa_assert(e); +    pa_assert(!e->dead);      e->destroy_callback = callback;  } @@ -415,10 +416,10 @@ static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb  static void mainloop_quit(pa_mainloop_api*a, int retval) {      pa_mainloop *m; -    assert(a); -    assert(a->userdata); +    pa_assert(a); +    pa_assert(a->userdata);      m = a->userdata; -    assert(a == &m->api); +    pa_assert(a == &m->api);      pa_mainloop_quit(m, retval);  } @@ -456,8 +457,10 @@ pa_mainloop *pa_mainloop_new(void) {          return NULL;      } -    pa_make_nonblock_fd(m->wakeup_pipe[0]); -    pa_make_nonblock_fd(m->wakeup_pipe[1]); +    pa_make_fd_nonblock(m->wakeup_pipe[0]); +    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;      PA_LLIST_HEAD_INIT(pa_io_event, m->io_events); @@ -502,7 +505,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) {              PA_LLIST_REMOVE(pa_io_event, m->io_events, e);              if (e->dead) { -                assert(m->io_events_please_scan > 0); +                pa_assert(m->io_events_please_scan > 0);                  m->io_events_please_scan--;              } @@ -517,7 +520,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) {          e = n;      } -    assert(m->io_events_please_scan == 0); +    pa_assert(m->io_events_please_scan == 0);  }  static void cleanup_time_events(pa_mainloop *m, int force) { @@ -534,13 +537,14 @@ static void cleanup_time_events(pa_mainloop *m, int force) {              PA_LLIST_REMOVE(pa_time_event, m->time_events, e);              if (e->dead) { -                assert(m->time_events_please_scan > 0); +                pa_assert(m->time_events_please_scan > 0);                  m->time_events_please_scan--;              }              if (!e->dead && e->enabled) { -                assert(m->n_enabled_time_events > 0); +                pa_assert(m->n_enabled_time_events > 0);                  m->n_enabled_time_events--; +                e->enabled = 0;              }              if (e->destroy_callback) @@ -552,7 +556,7 @@ static void cleanup_time_events(pa_mainloop *m, int force) {          e = n;      } -    assert(m->time_events_please_scan == 0); +    pa_assert(m->time_events_please_scan == 0);  }  static void cleanup_defer_events(pa_mainloop *m, int force) { @@ -569,13 +573,14 @@ static void cleanup_defer_events(pa_mainloop *m, int force) {              PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e);              if (e->dead) { -                assert(m->defer_events_please_scan > 0); +                pa_assert(m->defer_events_please_scan > 0);                  m->defer_events_please_scan--;              }              if (!e->dead && e->enabled) { -                assert(m->n_enabled_defer_events > 0); +                pa_assert(m->n_enabled_defer_events > 0);                  m->n_enabled_defer_events--; +                e->enabled = 0;              }              if (e->destroy_callback) @@ -587,12 +592,12 @@ static void cleanup_defer_events(pa_mainloop *m, int force) {          e = n;      } -    assert(m->defer_events_please_scan == 0); +    pa_assert(m->defer_events_please_scan == 0);  }  void pa_mainloop_free(pa_mainloop* m) { -    assert(m); +    pa_assert(m);      cleanup_io_events(m, 1);      cleanup_defer_events(m, 1); @@ -600,16 +605,13 @@ void pa_mainloop_free(pa_mainloop* m) {      pa_xfree(m->pollfds); -    if (m->wakeup_pipe[0] >= 0) -        close(m->wakeup_pipe[0]); -    if (m->wakeup_pipe[1] >= 0) -        close(m->wakeup_pipe[1]); +    pa_close_pipe(m->wakeup_pipe);      pa_xfree(m);  }  static void scan_dead(pa_mainloop *m) { -    assert(m); +    pa_assert(m);      if (m->io_events_please_scan)          cleanup_io_events(m, 0); @@ -666,13 +668,14 @@ static int dispatch_pollfds(pa_mainloop *m) {      pa_io_event *e;      int r = 0, k; -    assert(m->poll_func_ret > 0); +    pa_assert(m->poll_func_ret > 0);      for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) {          if (e->dead || !e->pollfd || !e->pollfd->revents)              continue; -        assert(e->pollfd->fd == e->fd && e->callback); +        pa_assert(e->pollfd->fd == e->fd); +        pa_assert(e->callback);          e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata);          e->pollfd->revents = 0;          r++; @@ -694,7 +697,7 @@ static int dispatch_defer(pa_mainloop *m) {          if (e->dead || !e->enabled)              continue; -        assert(e->callback); +        pa_assert(e->callback);          e->callback(&m->api, e, e->userdata);          r++;      } @@ -704,7 +707,7 @@ static int dispatch_defer(pa_mainloop *m) {  static pa_time_event* find_next_time_event(pa_mainloop *m) {      pa_time_event *t, *n = NULL; -    assert(m); +    pa_assert(m);      if (m->cached_next_time_event)          return m->cached_next_time_event; @@ -736,7 +739,7 @@ static int calc_next_timeout(pa_mainloop *m) {          return -1;      t = find_next_time_event(m); -    assert(t); +    pa_assert(t);      if (t->timeval.tv_sec <= 0)          return 0; @@ -754,7 +757,7 @@ static int dispatch_timeout(pa_mainloop *m) {      pa_time_event *e;      struct timeval now;      int r = 0; -    assert(m); +    pa_assert(m);      if (m->n_enabled_time_events <= 0)          return 0; @@ -767,7 +770,7 @@ static int dispatch_timeout(pa_mainloop *m) {              continue;          if (pa_timeval_cmp(&e->timeval, &now) <= 0) { -            assert(e->callback); +            pa_assert(e->callback);              /* Disable time event */              mainloop_time_restart(e, NULL); @@ -783,7 +786,7 @@ static int dispatch_timeout(pa_mainloop *m) {  void pa_mainloop_wakeup(pa_mainloop *m) {      char c = 'W'; -    assert(m); +    pa_assert(m);      if (m->wakeup_pipe[1] >= 0 && m->state == STATE_POLLING) {          pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type); @@ -794,7 +797,7 @@ void pa_mainloop_wakeup(pa_mainloop *m) {  static void clear_wakeup(pa_mainloop *m) {      char c[10]; -    assert(m); +    pa_assert(m);      if (m->wakeup_pipe[0] < 0)          return; @@ -806,8 +809,8 @@ static void clear_wakeup(pa_mainloop *m) {  }  int pa_mainloop_prepare(pa_mainloop *m, int timeout) { -    assert(m); -    assert(m->state == STATE_PASSIVE); +    pa_assert(m); +    pa_assert(m->state == STATE_PASSIVE);      clear_wakeup(m);      scan_dead(m); @@ -833,8 +836,8 @@ quit:  }  int pa_mainloop_poll(pa_mainloop *m) { -    assert(m); -    assert(m->state == STATE_PREPARED); +    pa_assert(m); +    pa_assert(m->state == STATE_PREPARED);      if (m->quit)          goto quit; @@ -844,7 +847,7 @@ int pa_mainloop_poll(pa_mainloop *m) {      if (m->n_enabled_defer_events )          m->poll_func_ret = 0;      else { -        assert(!m->rebuild_pollfds); +        pa_assert(!m->rebuild_pollfds);          if (m->poll_func)              m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); @@ -870,8 +873,8 @@ quit:  int pa_mainloop_dispatch(pa_mainloop *m) {      int dispatched = 0; -    assert(m); -    assert(m->state == STATE_POLLED); +    pa_assert(m); +    pa_assert(m->state == STATE_POLLED);      if (m->quit)          goto quit; @@ -902,13 +905,13 @@ quit:  }  int pa_mainloop_get_retval(pa_mainloop *m) { -    assert(m); +    pa_assert(m);      return m->retval;  }  int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {      int r; -    assert(m); +    pa_assert(m);      if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)          goto quit; @@ -942,7 +945,7 @@ int pa_mainloop_run(pa_mainloop *m, int *retval) {  }  void pa_mainloop_quit(pa_mainloop *m, int retval) { -    assert(m); +    pa_assert(m);      m->quit = 1;      m->retval = retval; @@ -950,12 +953,12 @@ void pa_mainloop_quit(pa_mainloop *m, int retval) {  }  pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) { -    assert(m); +    pa_assert(m);      return &m->api;  }  void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) { -    assert(m); +    pa_assert(m);      m->poll_func = poll_func;      m->poll_func_userdata = userdata; diff --git a/src/pulse/operation.c b/src/pulse/operation.c index f23def50..ed5eb4aa 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -25,19 +25,18 @@  #include <config.h>  #endif -#include <assert.h> -  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include "internal.h"  #include "operation.h"  pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {      pa_operation *o; -    assert(c); +    pa_assert(c);      o = pa_xnew(pa_operation, 1); -    o->ref = 1; +    PA_REFCNT_INIT(o);      o->context = c;      o->stream = s; @@ -53,27 +52,27 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb  }  pa_operation *pa_operation_ref(pa_operation *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1); -    o->ref++; +    PA_REFCNT_INC(o);      return o;  }  void pa_operation_unref(pa_operation *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1); -    if ((--(o->ref)) == 0) { -        assert(!o->context); -        assert(!o->stream); +    if (PA_REFCNT_DEC(o) <= 0) { +        pa_assert(!o->context); +        pa_assert(!o->stream);          pa_xfree(o);      }  }  static void operation_set_state(pa_operation *o, pa_operation_state_t st) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (st == o->state)          return; @@ -85,7 +84,7 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) {      if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {          if (o->context) { -            assert(o->ref >= 2); +            pa_assert(PA_REFCNT_VALUE(o) >= 2);              PA_LLIST_REMOVE(pa_operation, o->context->operations, o);              pa_operation_unref(o); @@ -101,22 +100,22 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) {  }  void pa_operation_cancel(pa_operation *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      operation_set_state(o, PA_OPERATION_CANCELED);  }  void pa_operation_done(pa_operation *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      operation_set_state(o, PA_OPERATION_DONE);  }  pa_operation_state_t pa_operation_get_state(pa_operation *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      return o->state;  } diff --git a/src/pulse/sample.c b/src/pulse/sample.c index ffdeedf7..ae2a0b9f 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -27,57 +27,58 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <math.h>  #include <string.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> +  #include "sample.h"  size_t pa_sample_size(const pa_sample_spec *spec) { -    assert(spec); - -    switch (spec->format) { -        case PA_SAMPLE_U8: -        case PA_SAMPLE_ULAW: -        case PA_SAMPLE_ALAW: -            return 1; -        case PA_SAMPLE_S16LE: -        case PA_SAMPLE_S16BE: -            return 2; -        case PA_SAMPLE_FLOAT32LE: -        case PA_SAMPLE_FLOAT32BE: -            return 4; -        default: -            assert(0); -            return 0; -    } + +    static const size_t table[] = { +        [PA_SAMPLE_U8] = 1, +        [PA_SAMPLE_ULAW] = 1, +        [PA_SAMPLE_ALAW] = 1, +        [PA_SAMPLE_S16LE] = 2, +        [PA_SAMPLE_S16BE] = 2, +        [PA_SAMPLE_FLOAT32LE] = 4, +        [PA_SAMPLE_FLOAT32BE] = 4 +    }; + +    pa_assert(spec); +    pa_assert(spec->format >= 0); +    pa_assert(spec->format < PA_SAMPLE_MAX); + +    return table[spec->format];  }  size_t pa_frame_size(const pa_sample_spec *spec) { -    assert(spec); +    pa_assert(spec);      return pa_sample_size(spec) * spec->channels;  }  size_t pa_bytes_per_second(const pa_sample_spec *spec) { -    assert(spec); +    pa_assert(spec);      return spec->rate*pa_frame_size(spec);  }  pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { -    assert(spec); +    pa_assert(spec);      return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);  }  size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { -    assert(spec); +    pa_assert(spec);      return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec);  }  int pa_sample_spec_valid(const pa_sample_spec *spec) { -    assert(spec); +    pa_assert(spec);      if (spec->rate <= 0 ||          spec->rate > PA_RATE_MAX || @@ -91,7 +92,8 @@ int pa_sample_spec_valid(const pa_sample_spec *spec) {  }  int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { -    assert(a && b); +    pa_assert(a); +    pa_assert(b);      return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);  } @@ -107,37 +109,42 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {          [PA_SAMPLE_FLOAT32BE] = "float32be",      }; -    if (f >= PA_SAMPLE_MAX) +    if (f < 0 || f >= PA_SAMPLE_MAX)          return NULL;      return table[f];  }  char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { -    assert(s && l && spec); +    pa_assert(s); +    pa_assert(l); +    pa_assert(spec);      if (!pa_sample_spec_valid(spec)) -        snprintf(s, l, "Invalid"); +        pa_snprintf(s, l, "Invalid");      else -        snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); +        pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);      return s;  }  char* pa_bytes_snprint(char *s, size_t l, unsigned v) { +    pa_assert(s); +      if (v >= ((unsigned) 1024)*1024*1024) -        snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); +        pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);      else if (v >= ((unsigned) 1024)*1024) -        snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); +        pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);      else if (v >= (unsigned) 1024) -        snprintf(s, l, "%0.1f KiB", ((double) v)/1024); +        pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024);      else -        snprintf(s, l, "%u B", (unsigned) v); +        pa_snprintf(s, l, "%u B", (unsigned) v);      return s;  }  pa_sample_format_t pa_parse_sample_format(const char *format) { +    pa_assert(format);      if (strcasecmp(format, "s16le") == 0)          return PA_SAMPLE_S16LE; @@ -145,15 +152,19 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {          return PA_SAMPLE_S16BE;      else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)          return PA_SAMPLE_S16NE; +    else if (strcasecmp(format, "s16re") == 0) +        return PA_SAMPLE_S16RE;      else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)          return PA_SAMPLE_U8;      else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) -        return PA_SAMPLE_FLOAT32; +        return PA_SAMPLE_FLOAT32NE; +    else if (strcasecmp(format, "float32re") == 0) +        return PA_SAMPLE_FLOAT32RE;      else if (strcasecmp(format, "float32le") == 0)          return PA_SAMPLE_FLOAT32LE;      else if (strcasecmp(format, "float32be") == 0)          return PA_SAMPLE_FLOAT32BE; -    else if (strcasecmp(format, "ulaw") == 0) +    else if (strcasecmp(format, "ulaw") == 0 || strcasecmp(format, "mulaw") == 0)          return PA_SAMPLE_ULAW;      else if (strcasecmp(format, "alaw") == 0)          return PA_SAMPLE_ALAW; diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 683167cc..b307621e 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -155,31 +155,31 @@ typedef struct pa_sample_spec {  typedef uint64_t pa_usec_t;  /** Return the amount of bytes playback of a second of audio with the specified sample type takes */ -size_t pa_bytes_per_second(const pa_sample_spec *spec); +size_t pa_bytes_per_second(const pa_sample_spec *spec) PA_GCC_PURE;  /** Return the size of a frame with the specific sample type */ -size_t pa_frame_size(const pa_sample_spec *spec); +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); +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 */ -pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec); +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 */ -size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec); +size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE;  /** Return non-zero when the sample type specification is valid */ -int pa_sample_spec_valid(const pa_sample_spec *spec); +int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE;  /** Return non-zero when the two sample type specifications match */ -int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b); +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) PA_GCC_PURE;  /** Return a descriptive string for the specified sample format. \since 0.8 */ -const char *pa_sample_format_to_string(pa_sample_format_t f); +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_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;  /** Maximum required string length for pa_sample_spec_snprint() */  #define PA_SAMPLE_SPEC_SNPRINT_MAX 32 diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 09bc1078..186b0a3e 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -25,12 +25,12 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <stdio.h>  #include <string.h>  #include <pulsecore/pstream-util.h> +#include <pulsecore/macro.h>  #include "internal.h" @@ -40,7 +40,8 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {      pa_tagstruct *t;      uint32_t tag; -    assert(s); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); @@ -66,7 +67,9 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {  int pa_stream_finish_upload(pa_stream *s) {      pa_tagstruct *t;      uint32_t tag; -    assert(s); + +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -87,8 +90,8 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -115,8 +118,8 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); diff --git a/src/pulse/simple.c b/src/pulse/simple.c index 3cf862d2..1072fb4d 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -1,3 +1,4 @@ +  /* $Id$ */  /*** @@ -27,7 +28,6 @@  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <pulse/pulseaudio.h> @@ -36,6 +36,7 @@  #include <pulsecore/native-common.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "simple.h" @@ -83,8 +84,8 @@ if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \  static void context_state_cb(pa_context *c, void *userdata) {      pa_simple *p = userdata; -    assert(c); -    assert(p); +    pa_assert(c); +    pa_assert(p);      switch (pa_context_get_state(c)) {          case PA_CONTEXT_READY: @@ -103,8 +104,8 @@ static void context_state_cb(pa_context *c, void *userdata) {  static void stream_state_cb(pa_stream *s, void * userdata) {      pa_simple *p = userdata; -    assert(s); -    assert(p); +    pa_assert(s); +    pa_assert(p);      switch (pa_stream_get_state(s)) { @@ -122,7 +123,7 @@ static void stream_state_cb(pa_stream *s, void * userdata) {  static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {      pa_simple *p = userdata; -    assert(p); +    pa_assert(p);      pa_threaded_mainloop_signal(p->mainloop, 0);  } @@ -130,21 +131,21 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {  static void stream_latency_update_cb(pa_stream *s, void *userdata) {      pa_simple *p = userdata; -    assert(p); +    pa_assert(p);      pa_threaded_mainloop_signal(p->mainloop, 0);  }  pa_simple* pa_simple_new( -    const char *server, -    const char *name, -    pa_stream_direction_t dir, -    const char *dev, -    const char *stream_name, -    const pa_sample_spec *ss, -    const pa_channel_map *map, -    const pa_buffer_attr *attr, -    int *rerror) { +        const char *server, +        const char *name, +        pa_stream_direction_t dir, +        const char *dev, +        const char *stream_name, +        const pa_sample_spec *ss, +        const pa_channel_map *map, +        const pa_buffer_attr *attr, +        int *rerror) {      pa_simple *p;      int error = PA_ERR_INTERNAL, r; @@ -232,7 +233,7 @@ fail:  }  void pa_simple_free(pa_simple *s) { -    assert(s); +    pa_assert(s);      if (s->mainloop)          pa_threaded_mainloop_stop(s->mainloop); @@ -250,7 +251,7 @@ void pa_simple_free(pa_simple *s) {  }  int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { -    assert(p); +    pa_assert(p);      CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);      CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); @@ -289,7 +290,7 @@ unlock_and_fail:  }  int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { -    assert(p); +    pa_assert(p);      CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);      CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); @@ -346,8 +347,8 @@ unlock_and_fail:  static void success_cb(pa_stream *s, int success, void *userdata) {      pa_simple *p = userdata; -    assert(s); -    assert(p); +    pa_assert(s); +    pa_assert(p);      p->operation_success = success;      pa_threaded_mainloop_signal(p->mainloop, 0); @@ -356,7 +357,7 @@ static void success_cb(pa_stream *s, int success, void *userdata) {  int pa_simple_drain(pa_simple *p, int *rerror) {      pa_operation *o = NULL; -    assert(p); +    pa_assert(p);      CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -392,7 +393,7 @@ unlock_and_fail:  int pa_simple_flush(pa_simple *p, int *rerror) {      pa_operation *o = NULL; -    assert(p); +    pa_assert(p);      CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -429,7 +430,7 @@ pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {      pa_usec_t t;      int negative; -    assert(p); +    pa_assert(p);      pa_threaded_mainloop_lock(p->mainloop); diff --git a/src/pulse/simple.h b/src/pulse/simple.h index 128d2716..f76c1d67 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -51,7 +51,7 @@   * pa_simple *s;   * pa_sample_spec ss;   * - * ss.format = PA_SAMPLE_S16_NE; + * ss.format = PA_SAMPLE_S16NE;   * ss.channels = 2;   * ss.rate = 44100;   * diff --git a/src/pulse/stream.c b/src/pulse/stream.c index f20c17ae..47906a5c 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <string.h>  #include <stdio.h>  #include <string.h> @@ -38,6 +37,7 @@  #include <pulsecore/pstream-util.h>  #include <pulsecore/log.h>  #include <pulsecore/hashmap.h> +#include <pulsecore/macro.h>  #include "internal.h" @@ -47,13 +47,14 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *      pa_stream *s;      int i; -    assert(c); +    pa_assert(c); +    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, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);      s = pa_xnew(pa_stream, 1); -    s->ref = 1; +    PA_REFCNT_INIT(s);      s->context = c;      s->mainloop = c->mainloop; @@ -91,6 +92,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *      s->peek_memchunk.index = 0;      s->peek_memchunk.length = 0;      s->peek_memchunk.memblock = NULL; +    s->peek_data = NULL;      s->record_memblockq = NULL; @@ -118,15 +120,20 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *  }  static void stream_free(pa_stream *s) { -    assert(s && !s->context && !s->channel_valid); +    pa_assert(s); +    pa_assert(!s->context); +    pa_assert(!s->channel_valid);      if (s->auto_timing_update_event) { -        assert(s->mainloop); +        pa_assert(s->mainloop);          s->mainloop->time_free(s->auto_timing_update_event);      } -    if (s->peek_memchunk.memblock) +    if (s->peek_memchunk.memblock) { +        if (s->peek_data) +            pa_memblock_release(s->peek_memchunk.memblock);          pa_memblock_unref(s->peek_memchunk.memblock); +    }      if (s->record_memblockq)          pa_memblockq_free(s->record_memblockq); @@ -136,38 +143,38 @@ static void stream_free(pa_stream *s) {  }  void pa_stream_unref(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); -    if (--(s->ref) == 0) +    if (PA_REFCNT_DEC(s) <= 0)          stream_free(s);  }  pa_stream* pa_stream_ref(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); -    s->ref++; +    PA_REFCNT_INC(s);      return s;  }  pa_stream_state_t pa_stream_get_state(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return s->state;  }  pa_context* pa_stream_get_context(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return s->context;  }  uint32_t pa_stream_get_index(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); @@ -175,8 +182,8 @@ uint32_t pa_stream_get_index(pa_stream *s) {  }  void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      if (s->state == st)          return; @@ -214,6 +221,13 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {          s->channel_valid = 0;          s->context = NULL; + +        s->read_callback = NULL; +        s->write_callback = NULL; +        s->state_callback = NULL; +        s->overflow_callback = NULL; +        s->underflow_callback = NULL; +        s->latency_update_callback = NULL;      }      pa_stream_unref(s); @@ -224,10 +238,11 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED      pa_stream *s;      uint32_t channel; -    assert(pd); -    assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); -    assert(t); -    assert(c); +    pa_assert(pd); +    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); +    pa_assert(t); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_ref(c); @@ -252,10 +267,11 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32      pa_context *c = userdata;      uint32_t bytes, channel; -    assert(pd); -    assert(command == PA_COMMAND_REQUEST); -    assert(t); -    assert(c); +    pa_assert(pd); +    pa_assert(command == PA_COMMAND_REQUEST); +    pa_assert(t); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_ref(c); @@ -285,10 +301,11 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC      pa_context *c = userdata;      uint32_t channel; -    assert(pd); -    assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW); -    assert(t); -    assert(c); +    pa_assert(pd); +    pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW); +    pa_assert(t); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_ref(c); @@ -317,8 +334,8 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC  }  static void request_auto_timing_update(pa_stream *s, int force) { -    struct timeval next; -    assert(s); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))          return; @@ -335,13 +352,17 @@ static void request_auto_timing_update(pa_stream *s, int force) {          }      } -    pa_gettimeofday(&next); -    pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); -    s->mainloop->time_restart(s->auto_timing_update_event, &next); +    if (s->auto_timing_update_event) { +        struct timeval next; +        pa_gettimeofday(&next); +        pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); +        s->mainloop->time_restart(s->auto_timing_update_event, &next); +    }  }  static void invalidate_indexes(pa_stream *s, int r, int w) { -    assert(s); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);  /*     pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */ @@ -376,6 +397,9 @@ static void invalidate_indexes(pa_stream *s, int r, int w) {  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) {      pa_stream *s = userdata; +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +  /*     pa_log("time event");    */      pa_stream_ref(s); @@ -383,12 +407,32 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC      pa_stream_unref(s);  } +static void create_stream_complete(pa_stream *s) { +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(s->state == PA_STREAM_CREATING); + +    pa_stream_set_state(s, PA_STREAM_READY); + +    if (s->requested_bytes > 0 && s->write_callback) +        s->write_callback(s, s->requested_bytes, s->write_userdata); + +    if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { +        struct timeval tv; +        pa_gettimeofday(&tv); +        tv.tv_usec += 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); +    } +} +  void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s = userdata; -    assert(pd); -    assert(s); -    assert(s->state == PA_STREAM_CREATING); +    pa_assert(pd); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(s->state == PA_STREAM_CREATING);      pa_stream_ref(s); @@ -431,7 +475,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED      }      if (s->direction == PA_STREAM_RECORD) { -        assert(!s->record_memblockq); +        pa_assert(!s->record_memblockq);          s->record_memblockq = pa_memblockq_new(                  0, @@ -446,23 +490,16 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED      s->channel_valid = 1;      pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); -    pa_stream_set_state(s, PA_STREAM_READY); - -    if (s->direction != PA_STREAM_UPLOAD && -        s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { -        struct timeval tv; - -        pa_gettimeofday(&tv); -        tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ - -        assert(!s->auto_timing_update_event); -        s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); - +    if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { +        /* If automatic timing updates are active, we wait for the +         * first timing update before going to PA_STREAM_READY +         * state */ +        s->state = PA_STREAM_READY;          request_auto_timing_update(s, 1); -    } +        s->state = PA_STREAM_CREATING; -    if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback) -        s->write_callback(s, s->requested_bytes, s->write_userdata); +    } else +        create_stream_complete(s);  finish:      pa_stream_unref(s); @@ -480,8 +517,8 @@ static int create_stream(      pa_tagstruct *t;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ? @@ -503,12 +540,12 @@ static int create_stream(      if (attr)          s->buffer_attr = *attr;      else { -        /* half a second */ +        /* half a second, with minimum request of 10 ms */          s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;          s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; -        s->buffer_attr.minreq = s->buffer_attr.tlength/100; +        s->buffer_attr.minreq = s->buffer_attr.tlength/50;          s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; -        s->buffer_attr.fragsize = s->buffer_attr.tlength/100; +        s->buffer_attr.fragsize = s->buffer_attr.tlength/50;      }      if (!dev) @@ -565,8 +602,8 @@ int pa_stream_connect_playback(          pa_cvolume *volume,          pa_stream *sync_stream) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);  } @@ -577,8 +614,8 @@ int pa_stream_connect_record(          const pa_buffer_attr *attr,          pa_stream_flags_t flags) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);  } @@ -593,9 +630,9 @@ int pa_stream_write(      pa_memchunk chunk; -    assert(s); -    assert(s->ref >= 1); -    assert(data); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(data);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -608,8 +645,11 @@ int pa_stream_write(      if (free_cb)          chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1);      else { +        void *tdata;          chunk.memblock = pa_memblock_new(s->context->mempool, length); -        memcpy(chunk.memblock->data, data, length); +        tdata = pa_memblock_acquire(chunk.memblock); +        memcpy(tdata, data, length); +        pa_memblock_release(chunk.memblock);      }      chunk.index = 0; @@ -660,10 +700,10 @@ int pa_stream_write(  }  int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { -    assert(s); -    assert(s->ref >= 1); -    assert(data); -    assert(length); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(data); +    pa_assert(length);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); @@ -675,27 +715,32 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {              *length = 0;              return 0;          } + +        s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);      } -    *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; +    pa_assert(s->peek_data); +    *data = (uint8_t*) s->peek_data + s->peek_memchunk.index;      *length = s->peek_memchunk.length;      return 0;  }  int pa_stream_drop(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE); -    pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length); +    pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);      /* 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; +    pa_assert(s->peek_data); +    pa_memblock_release(s->peek_memchunk.memblock);      pa_memblock_unref(s->peek_memchunk.memblock);      s->peek_memchunk.length = 0;      s->peek_memchunk.index = 0; @@ -705,8 +750,8 @@ int pa_stream_drop(pa_stream *s) {  }  size_t pa_stream_writable_size(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);      PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); @@ -715,8 +760,8 @@ size_t pa_stream_writable_size(pa_stream *s) {  }  size_t pa_stream_readable_size(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);      PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); @@ -729,8 +774,8 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us      pa_tagstruct *t;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      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_PLAYBACK, PA_ERR_BADSTATE); @@ -750,8 +795,9 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,      struct timeval local, remote, now;      pa_timing_info *i; -    assert(pd); -    assert(o); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context || !o->stream)          goto finish; @@ -874,6 +920,10 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,          }      } +    /* First, let's complete the initialization, if necessary. */ +    if (o->stream->state == PA_STREAM_CREATING) +        create_stream_complete(o->stream); +      if (o->stream->latency_update_callback)          o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); @@ -895,8 +945,8 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t      struct timeval now;      int cidx = 0; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      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); @@ -938,9 +988,9 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t  void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s = userdata; -    assert(pd); -    assert(s); -    assert(s->ref >= 1); +    pa_assert(pd); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      pa_stream_ref(s); @@ -965,8 +1015,8 @@ int pa_stream_disconnect(pa_stream *s) {      pa_tagstruct *t;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -987,48 +1037,48 @@ int pa_stream_disconnect(pa_stream *s) {  }  void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->read_callback = cb;      s->read_userdata = userdata;  }  void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->write_callback = cb;      s->write_userdata = userdata;  }  void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->state_callback = cb;      s->state_userdata = userdata;  }  void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->overflow_callback = cb;      s->overflow_userdata = userdata;  }  void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->underflow_callback = cb;      s->underflow_userdata = userdata;  }  void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->latency_update_callback = cb;      s->latency_update_userdata = userdata; @@ -1038,9 +1088,9 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN      pa_operation *o = userdata;      int success = 1; -    assert(pd); -    assert(o); -    assert(o->ref >= 1); +    pa_assert(pd); +    pa_assert(o); +    pa_assert(PA_REFCNT_VALUE(o) >= 1);      if (!o->context)          goto finish; @@ -1070,8 +1120,8 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi      pa_tagstruct *t;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      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); @@ -1100,8 +1150,8 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command,      pa_operation *o;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); @@ -1118,6 +1168,9 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command,  pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {      pa_operation *o; +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +      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))) { @@ -1143,6 +1196,9 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use  pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {      pa_operation *o; +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); @@ -1155,6 +1211,9 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us  pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {      pa_operation *o; +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); @@ -1169,9 +1228,9 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe      pa_tagstruct *t;      uint32_t tag; -    assert(s); -    assert(s->ref >= 1); -    assert(name); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(name);      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); @@ -1193,8 +1252,8 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe  int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {      pa_usec_t usec = 0; -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1277,8 +1336,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {  }  static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      if (negative)          *negative = 0; @@ -1299,9 +1358,9 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {      int r;      int64_t cindex; -    assert(s); -    assert(s->ref >= 1); -    assert(r_usec); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(r_usec);      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1331,8 +1390,8 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {  }  const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      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); @@ -1342,22 +1401,22 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {  }  const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return &s->sample_spec;  }  const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      return &s->channel_map;  }  const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      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); diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index 5d8f1252..580038cc 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -25,10 +25,10 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include <pulsecore/pstream-util.h>  #include "internal.h" @@ -40,10 +40,11 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSE      pa_subscription_event_type_t e;      uint32_t idx; -    assert(pd); -    assert(command == PA_COMMAND_SUBSCRIBE_EVENT); -    assert(t); -    assert(c); +    pa_assert(pd); +    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT); +    pa_assert(t); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      pa_context_ref(c); @@ -67,8 +68,8 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c      pa_tagstruct *t;      uint32_t tag; -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -83,8 +84,8 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c  }  void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { -    assert(c); -    assert(c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1);      c->subscribe_callback = cb;      c->subscribe_userdata = userdata; diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 4f3cacc9..9dd47ae3 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -26,24 +26,24 @@  #include <config.h>  #endif -#include <assert.h>  #include <signal.h>  #include <stdio.h> -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> +#ifdef HAVE_POLL_H +#include <poll.h>  #else -#include "../pulsecore/poll.h" +#include <pulsecore/poll.h>  #endif  #include <pulse/xmalloc.h> +#include <pulse/mainloop.h>  #include <pulsecore/log.h>  #include <pulsecore/hashmap.h>  #include <pulsecore/thread.h>  #include <pulsecore/mutex.h> +#include <pulsecore/macro.h> -#include "mainloop.h"  #include "thread-mainloop.h"  struct pa_threaded_mainloop { @@ -63,7 +63,7 @@ static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void      pa_mutex *mutex = userdata;      int r; -    assert(mutex); +    pa_assert(mutex);      /* Before entering poll() we unlock the mutex, so that       * avahi_simple_poll_quit() can succeed from another thread. */ @@ -103,7 +103,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {          return NULL;      } -    m->mutex = pa_mutex_new(1); +    m->mutex = pa_mutex_new(TRUE, FALSE);      m->cond = pa_cond_new();      m->accept_cond = pa_cond_new();      m->thread = NULL; @@ -116,10 +116,10 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {  }  void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { -    assert(m); +    pa_assert(m);      /* Make sure that this function is not called from the helper thread */ -    assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m)); +    pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));      pa_threaded_mainloop_stop(m); @@ -136,9 +136,9 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {  }  int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m); -    assert(!m->thread || !pa_thread_is_running(m->thread)); +    pa_assert(!m->thread || !pa_thread_is_running(m->thread));      if (!(m->thread = pa_thread_new(thread, m)))          return -1; @@ -147,13 +147,13 @@ int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {  }  void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      if (!m->thread || !pa_thread_is_running(m->thread))          return;      /* Make sure that this function is not called from the helper thread */ -    assert(!in_worker(m)); +    pa_assert(!in_worker(m));      pa_mutex_lock(m->mutex);      pa_mainloop_quit(m->real_mainloop, 0); @@ -163,25 +163,25 @@ void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {  }  void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      /* Make sure that this function is not called from the helper thread */ -    assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); +    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));      pa_mutex_lock(m->mutex);  }  void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      /* Make sure that this function is not called from the helper thread */ -    assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); +    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));      pa_mutex_unlock(m->mutex);  }  void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { -    assert(m); +    pa_assert(m);      pa_cond_signal(m->cond, 1); @@ -190,36 +190,42 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {  }  void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      /* Make sure that this function is not called from the helper thread */ -    assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); +    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));      m->n_waiting ++;      pa_cond_wait(m->cond, m->mutex); -    assert(m->n_waiting > 0); +    pa_assert(m->n_waiting > 0);      m->n_waiting --;  }  void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      /* Make sure that this function is not called from the helper thread */ -    assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); +    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));      pa_cond_signal(m->accept_cond, 0);  }  int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { -    assert(m); +    pa_assert(m);      return pa_mainloop_get_retval(m->real_mainloop);  }  pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { -    assert(m); +    pa_assert(m);      return pa_mainloop_get_api(m->real_mainloop);  } + +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) { +    pa_assert(m); + +    return m->thread && pa_thread_self() == m->thread; +} diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index b78c1583..ea08f72a 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -297,6 +297,9 @@ int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);  /** Return the abstract main loop abstraction layer vtable for this main loop. */  pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m); +/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */ +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m); +  PA_C_DECL_END  #endif diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 78ece061..70ceb71e 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stddef.h>  #include <sys/time.h> @@ -34,15 +33,17 @@  #include <windows.h>  #endif -#include "../pulsecore/winsock.h" +#include <pulsecore/winsock.h> +#include <pulsecore/macro.h>  #include "timeval.h"  struct timeval *pa_gettimeofday(struct timeval *tv) {  #ifdef HAVE_GETTIMEOFDAY -    assert(tv); +    pa_assert(tv); -    return gettimeofday(tv, NULL) < 0 ? NULL : tv; +    pa_assert_se(gettimeofday(tv, NULL) == 0); +    return tv;  #elif defined(OS_IS_WIN32)      /*       * Copied from implementation by Steven Edwards (LGPL). @@ -59,7 +60,7 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {      LARGE_INTEGER   li;      __int64         t; -    assert(tv); +    pa_assert(tv);      GetSystemTimeAsFileTime(&ft);      li.LowPart  = ft.dwLowDateTime; @@ -67,8 +68,8 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {      t  = li.QuadPart;       /* In 100-nanosecond intervals */      t -= EPOCHFILETIME;     /* Offset to the Epoch time */      t /= 10;                /* In microseconds */ -    tv->tv_sec  = (long)(t / 1000000); -    tv->tv_usec = (long)(t % 1000000); +    tv->tv_sec  = (time_t) (t / PA_USEC_PER_SEC); +    tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC);      return tv;  #else @@ -78,9 +79,11 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {  pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {      pa_usec_t r; -    assert(a && b); -    /* Check which whan is the earlier time and swap the two arguments if reuqired. */ +    pa_assert(a); +    pa_assert(b); + +    /* Check which whan is the earlier time and swap the two arguments if required. */      if (pa_timeval_cmp(a, b) < 0) {          const struct timeval *c;          c = a; @@ -89,7 +92,7 @@ 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)* 1000000; +    r = ((pa_usec_t) a->tv_sec - b->tv_sec) * PA_USEC_PER_SEC;      /* Calculate the microsecond difference */      if (a->tv_usec > b->tv_usec) @@ -101,7 +104,8 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {  }  int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { -    assert(a && b); +    pa_assert(a); +    pa_assert(b);      if (a->tv_sec < b->tv_sec)          return -1; @@ -120,26 +124,43 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {  pa_usec_t pa_timeval_age(const struct timeval *tv) {      struct timeval now; -    assert(tv); +    pa_assert(tv);      return pa_timeval_diff(pa_gettimeofday(&now), tv);  }  struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {      unsigned long secs; -    assert(tv); +    pa_assert(tv); -    secs = (v/1000000); -    tv->tv_sec += (unsigned long) secs; -    v -= secs*1000000; +    secs = (unsigned long) (v/PA_USEC_PER_SEC); +    tv->tv_sec += secs; +    v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC; -    tv->tv_usec += v; +    tv->tv_usec += (suseconds_t) v;      /* Normalize */ -    while (tv->tv_usec >= 1000000) { +    while (tv->tv_usec >= PA_USEC_PER_SEC) {          tv->tv_sec++; -        tv->tv_usec -= 1000000; +        tv->tv_usec -= PA_USEC_PER_SEC;      }      return tv;  } + +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; + +    return tv; +} + +pa_usec_t pa_timeval_load(const struct timeval *tv) { +    pa_assert(tv); + +    return +        (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC + +        (pa_usec_t) tv->tv_usec; +} diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 1e5627e3..65a0e513 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -33,6 +33,11 @@  PA_C_DECL_BEGIN +#define PA_MSEC_PER_SEC 1000 +#define PA_USEC_PER_SEC 1000000 +#define PA_NSEC_PER_SEC 1000000000 +#define PA_USEC_PER_MSEC 1000 +  struct timeval;  /** Return the current timestamp, just like UNIX gettimeofday() */ @@ -40,16 +45,22 @@ struct timeval *pa_gettimeofday(struct timeval *tv);  /** Calculate the difference between the two specified timeval   * structs. */ -pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;  /** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ -int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;  /** Return the time difference between now and the specified timestamp */  pa_usec_t pa_timeval_age(const struct timeval *tv);  /** Add the specified time inmicroseconds to the specified timeval structure */ -struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v); +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) PA_GCC_PURE; + +/** Store the specified uec value in the timeval struct. \since 0.9.7 */ +struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v); + +/** Load the specified tv value and return it in usec. \since 0.9.7 */ +pa_usec_t pa_timeval_load(const struct timeval *tv);  PA_C_DECL_END diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 2ac2d106..b2f6c3bd 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -37,7 +37,7 @@   *   * 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 + * 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 @@ -50,7 +50,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <errno.h>  #include <stdlib.h>  #include <inttypes.h> @@ -60,12 +59,15 @@  #include <iconv.h>  #endif +#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +  #include "utf8.h" -#include "xmalloc.h"  #define FILTER_CHAR '_'  static inline int is_unicode_valid(uint32_t ch) { +      if (ch >= 0x110000) /* End of unicode space */          return 0;      if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ @@ -74,6 +76,7 @@ static inline int is_unicode_valid(uint32_t ch) {          return 0;      if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */          return 0; +      return 1;  } @@ -95,6 +98,8 @@ static char* utf8_validate(const char *str, char *output) {      int size;      uint8_t *o; +    pa_assert(str); +      o = (uint8_t*) output;      for (p = (const uint8_t*) str; *p; p++) {          if (*p < 128) { @@ -178,15 +183,15 @@ failure:      return NULL;  } -const char* pa_utf8_valid (const char *str) { +char* pa_utf8_valid (const char *str) {      return utf8_validate(str, NULL);  }  char* pa_utf8_filter (const char *str) {      char *new_str; +    pa_assert(str);      new_str = pa_xnew(char, strlen(str) + 1); -      return utf8_validate(str, new_str);  } @@ -195,22 +200,24 @@ char* pa_utf8_filter (const char *str) {  static char* iconv_simple(const char *str, const char *to, const char *from) {      char *new_str;      size_t len, inlen; -      iconv_t cd;      ICONV_CONST char *inbuf;      char *outbuf;      size_t res, inbytes, outbytes; +    pa_assert(str); +    pa_assert(to); +    pa_assert(from); +      cd = iconv_open(to, from);      if (cd == (iconv_t)-1)          return NULL;      inlen = len = strlen(str) + 1; -    new_str = pa_xmalloc(len); -    assert(new_str); +    new_str = pa_xnew(char, len); -    while (1) { -        inbuf = (ICONV_CONST char*)str; /* Brain dead prototype for iconv() */ +    for (;;) { +        inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */          inbytes = inlen;          outbuf = new_str;          outbytes = len; @@ -226,11 +233,10 @@ static char* iconv_simple(const char *str, const char *to, const char *from) {              break;          } -        assert(inbytes != 0); +        pa_assert(inbytes != 0);          len += inbytes;          new_str = pa_xrealloc(new_str, len); -        assert(new_str);      }      iconv_close(cd); @@ -249,10 +255,12 @@ char* pa_locale_to_utf8 (const char *str) {  #else  char* pa_utf8_to_locale (const char *str) { +    pa_assert(str);      return NULL;  }  char* pa_locale_to_utf8 (const char *str) { +    pa_assert(str);      return NULL;  } diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index ff8dc215..1e08047c 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -34,7 +34,7 @@  PA_C_DECL_BEGIN  /** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */ -const char *pa_utf8_valid(const char *str); +char *pa_utf8_valid(const char *str) PA_GCC_PURE;  /** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */  char *pa_utf8_filter(const char *str); diff --git a/src/pulse/util.c b/src/pulse/util.c index d561329c..5dbb670b 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <errno.h>  #include <limits.h>  #include <stdio.h> @@ -56,20 +55,14 @@  #include <sys/prctl.h>  #endif -#include "../pulsecore/winsock.h" - +#include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "util.h" -#ifndef OS_IS_WIN32 -#define PATH_SEP '/' -#else -#define PATH_SEP '\\' -#endif -  char *pa_get_user_name(char *s, size_t l) {      char *p;      char buf[1024]; @@ -78,7 +71,8 @@ char *pa_get_user_name(char *s, size_t l) {      struct passwd pw, *r;  #endif -    assert(s && l > 0); +    pa_assert(s); +    pa_assert(l > 0);      if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {  #ifdef HAVE_PWD_H @@ -90,7 +84,7 @@ char *pa_get_user_name(char *s, size_t l) {              * that do not support getpwuid_r. */          if ((r = getpwuid(getuid())) == NULL) {  #endif -            snprintf(s, l, "%lu", (unsigned long) getuid()); +            pa_snprintf(s, l, "%lu", (unsigned long) getuid());              return s;          } @@ -113,11 +107,15 @@ char *pa_get_user_name(char *s, size_t l) {  }  char *pa_get_host_name(char *s, size_t l) { -    assert(s && l > 0); + +    pa_assert(s); +    pa_assert(l > 0); +      if (gethostname(s, l) < 0) {          pa_log("gethostname(): %s", pa_cstrerror(errno));          return NULL;      } +      s[l-1] = 0;      return s;  } @@ -130,7 +128,8 @@ char *pa_get_home_dir(char *s, size_t l) {      struct passwd pw, *r;  #endif -    assert(s && l); +    pa_assert(s); +    pa_assert(l > 0);      if ((e = getenv("HOME")))          return pa_strlcpy(s, e, l); @@ -159,8 +158,8 @@ char *pa_get_home_dir(char *s, size_t l) {  char *pa_get_binary_name(char *s, size_t l) { -    assert(s); -    assert(l); +    pa_assert(s); +    pa_assert(l > 0);  #if defined(OS_IS_WIN32)      { @@ -171,7 +170,7 @@ char *pa_get_binary_name(char *s, size_t l) {      }  #endif -#ifdef HAVE_READLINK +#ifdef __linux__      {          int i;          char path[PATH_MAX]; @@ -206,13 +205,15 @@ char *pa_get_binary_name(char *s, size_t l) {      return NULL;  } -const char *pa_path_get_filename(const char *p) { +char *pa_path_get_filename(const char *p) {      char *fn; -    if ((fn = strrchr(p, PATH_SEP))) +    pa_assert(p); + +    if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))          return fn+1; -    return (const char*) p; +    return (char*) p;  }  char *pa_get_fqdn(char *s, size_t l) { @@ -221,6 +222,9 @@ char *pa_get_fqdn(char *s, size_t l) {      struct addrinfo *a, hints;  #endif +    pa_assert(s); +    pa_assert(l > 0); +      if (!pa_get_host_name(hn, sizeof(hn)))          return NULL; diff --git a/src/pulse/util.h b/src/pulse/util.h index 95bd86f3..764678e5 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -52,7 +52,7 @@ char *pa_get_binary_name(char *s, size_t l);  /** Return a pointer to the filename inside a path (which is the last   * component). */ -const char *pa_path_get_filename(const char *p); +char *pa_path_get_filename(const char *p);  /** Wait t milliseconds */  int pa_msleep(unsigned long t); diff --git a/src/pulse/volume.c b/src/pulse/volume.c index feb33f07..3688b847 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -25,16 +25,18 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h>  #include <string.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> +  #include "volume.h"  int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {      int i; -    assert(a); -    assert(b); +    pa_assert(a); +    pa_assert(b);      if (a->channels != b->channels)          return 0; @@ -49,9 +51,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {  pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {      int i; -    assert(a); -    assert(channels > 0); -    assert(channels <= PA_CHANNELS_MAX); +    pa_assert(a); +    pa_assert(channels > 0); +    pa_assert(channels <= PA_CHANNELS_MAX);      a->channels = channels; @@ -64,7 +66,7 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {  pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {      uint64_t sum = 0;      int i; -    assert(a); +    pa_assert(a);      for (i = 0; i < a->channels; i++)          sum += a->values[i]; @@ -118,14 +120,14 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {      int first = 1;      char *e; -    assert(s); -    assert(l > 0); -    assert(c); +    pa_assert(s); +    pa_assert(l > 0); +    pa_assert(c);      *(e = s) = 0;      for (channel = 0; channel < c->channels && l > 1; channel++) { -        l -= snprintf(e, l, "%s%u: %3u%%", +        l -= pa_snprintf(e, l, "%s%u: %3u%%",                        first ? "" : " ",                        channel,                        (c->values[channel]*100)/PA_VOLUME_NORM); @@ -140,7 +142,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {  /** Return non-zero if the volume of all channels is equal to the specified value */  int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {      unsigned c; -    assert(a); +    pa_assert(a);      for (c = 0; c < a->channels; c++)          if (a->values[c] != v) @@ -152,9 +154,9 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {  pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {      unsigned i; -    assert(dest); -    assert(a); -    assert(b); +    pa_assert(dest); +    pa_assert(a); +    pa_assert(b);      for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) { @@ -169,7 +171,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const  }  int pa_cvolume_valid(const pa_cvolume *v) { -    assert(v); +    pa_assert(v);      if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)          return 0; diff --git a/src/pulse/volume.h b/src/pulse/volume.h index a928ff71..22e5b8a4 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -113,7 +113,7 @@ typedef struct pa_cvolume {  } pa_cvolume;  /** Return non-zero when *a == *b */ -int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b); +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;  /** Set the volume of all channels to PA_VOLUME_NORM */  #define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) @@ -131,13 +131,13 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);  char *pa_cvolume_snprint(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_volume_t pa_cvolume_avg(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); +int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;  /** Return non-zero if the volume of all channels is equal to the specified value */ -int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); +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 muted */  #define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED) @@ -146,22 +146,22 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);  #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! */ -pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); +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! */ -pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;  /** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */ -pa_volume_t pa_sw_volume_from_dB(double f); +pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;  /** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */ -double pa_sw_volume_to_dB(pa_volume_t v); +double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;  /** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */ -pa_volume_t pa_sw_volume_from_linear(double v); +pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;  /** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */ -double pa_sw_volume_to_linear(pa_volume_t v); +double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;  #ifdef INFINITY  #define PA_DECIBEL_MININFTY (-INFINITY) diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 1f0734c2..5348dda4 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -27,12 +27,12 @@  #include <stdlib.h>  #include <signal.h> -#include <assert.h>  #include <unistd.h>  #include <string.h>  #include <pulsecore/core-util.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include "xmalloc.h" @@ -60,8 +60,8 @@ static void oom(void) {  void* pa_xmalloc(size_t size) {      void *p; -    assert(size > 0); -    assert(size < MAX_ALLOC_SIZE); +    pa_assert(size > 0); +    pa_assert(size < MAX_ALLOC_SIZE);      if (!(p = malloc(size)))          oom(); @@ -71,8 +71,8 @@ void* pa_xmalloc(size_t size) {  void* pa_xmalloc0(size_t size) {      void *p; -    assert(size > 0); -    assert(size < MAX_ALLOC_SIZE); +    pa_assert(size > 0); +    pa_assert(size < MAX_ALLOC_SIZE);      if (!(p = calloc(1, size)))          oom(); @@ -82,8 +82,8 @@ void* pa_xmalloc0(size_t size) {  void *pa_xrealloc(void *ptr, size_t size) {      void *p; -    assert(size > 0); -    assert(size < MAX_ALLOC_SIZE); +    pa_assert(size > 0); +    pa_assert(size < MAX_ALLOC_SIZE);      if (!(p = realloc(ptr, size)))          oom(); diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 2f6399c5..62a450dc 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -75,6 +75,15 @@ static inline void* pa_xnew0_internal(unsigned n, size_t k) {  /** Same as pa_xnew() but set the memory to zero */  #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) { +    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))) +  PA_C_DECL_END  #endif diff --git a/src/pulsecore/anotify.c b/src/pulsecore/anotify.c deleted file mode 100644 index 25c5fe7d..00000000 --- a/src/pulsecore/anotify.c +++ /dev/null @@ -1,145 +0,0 @@ -/* $Id$ */ - -/*** -  This file is part of PulseAudio. - -  Copyright 2006 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 <assert.h> -#include <unistd.h> -#include <fcntl.h> - -#include <pulse/xmalloc.h> - -#include "anotify.h" - -#define EVENTS_MAX 16 - -struct pa_anotify { -    pa_mainloop_api *api; -    pa_anotify_cb_t callback; -    void *userdata; -    int fds[2]; -    pa_io_event *io_event; -    pa_defer_event *defer_event; - -    uint8_t queued_events[EVENTS_MAX]; -    unsigned n_queued_events, queue_index; -}; - -static void dispatch_event(pa_anotify *a) { -    assert(a); -    assert(a->queue_index < a->n_queued_events); - -    a->callback(a->queued_events[a->queue_index++], a->userdata); - -    if (a->queue_index >= a->n_queued_events) { -        a->n_queued_events = 0; -        a->queue_index = 0; - -        a->api->io_enable(a->io_event, PA_IO_EVENT_INPUT); -        a->api->defer_enable(a->defer_event, 0); -    } else { -        a->api->io_enable(a->io_event, 0); -        a->api->defer_enable(a->defer_event, 1); -    } -} - -static void io_callback( -        pa_mainloop_api *api, -        pa_io_event *e, -        int fd, -        pa_io_event_flags_t events, -        void *userdata) { - -    pa_anotify *a = userdata; -    ssize_t r; - -    assert(a); -    assert(events == PA_IO_EVENT_INPUT); -    assert(a->n_queued_events == 0); - -    r = read(fd, a->queued_events, sizeof(a->queued_events)); -    assert(r > 0); - -    a->n_queued_events = (unsigned) r; -    a->queue_index = 0; - -    /* Only dispatch a single event */ -    dispatch_event(a); -} - -static void defer_callback(pa_mainloop_api *api, pa_defer_event *e, void *userdata) { -    pa_anotify *a = userdata; -    assert(a); - -    dispatch_event(a); -} - -pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata) { -    pa_anotify *a; - -    assert(api); -    assert(cb); - -    a = pa_xnew(pa_anotify, 1); - -    if (pipe(a->fds) < 0) { -        pa_xfree(a); -        return NULL; -    } - -    a->api = api; -    a->callback = cb; -    a->userdata = userdata; - -    a->io_event = api->io_new(api, a->fds[0], PA_IO_EVENT_INPUT, io_callback, a); -    a->defer_event = api->defer_new(api, defer_callback, a); -    a->api->defer_enable(a->defer_event, 0); - -    a->n_queued_events = 0; - -    return a; -} - -void pa_anotify_free(pa_anotify *a) { -    assert(a); - -    a->api->io_free(a->io_event); -    a->api->defer_free(a->defer_event); - -    if (a->fds[0] >= 0) -        close(a->fds[0]); -    if (a->fds[1] >= 0) -        close(a->fds[1]); - -    pa_xfree(a); -} - -int pa_anotify_signal(pa_anotify *a, uint8_t event) { -    ssize_t r; -    assert(a); - -    r = write(a->fds[1], &event, 1); -    return r != 1 ? -1 : 0; -} diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c new file mode 100644 index 00000000..96b43a71 --- /dev/null +++ b/src/pulsecore/asyncmsgq.c @@ -0,0 +1,303 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <unistd.h> +#include <errno.h> + +#include <pulsecore/atomic.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/semaphore.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulsecore/flist.h> +#include <pulse/xmalloc.h> + +#include "asyncmsgq.h" + +PA_STATIC_FLIST_DECLARE(asyncmsgq, 0, pa_xfree); +PA_STATIC_FLIST_DECLARE(semaphores, 0, (void(*)(void*)) pa_semaphore_free); + +struct asyncmsgq_item { +    int code; +    pa_msgobject *object; +    void *userdata; +    pa_free_cb_t free_cb; +    int64_t offset; +    pa_memchunk memchunk; +    pa_semaphore *semaphore; +    int ret; +}; + +struct pa_asyncmsgq { +    PA_REFCNT_DECLARE; +    pa_asyncq *asyncq; +    pa_mutex *mutex; /* only for the writer side */ + +    struct asyncmsgq_item *current; +}; + +pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) { +    pa_asyncmsgq *a; + +    a = pa_xnew(pa_asyncmsgq, 1); + +    PA_REFCNT_INIT(a); +    pa_assert_se(a->asyncq = pa_asyncq_new(size)); +    pa_assert_se(a->mutex = pa_mutex_new(FALSE, TRUE)); +    a->current = NULL; + +    return a; +} + +static void asyncmsgq_free(pa_asyncmsgq *a) { +    struct asyncmsgq_item *i; +    pa_assert(a); + +    while ((i = pa_asyncq_pop(a->asyncq, 0))) { + +        pa_assert(!i->semaphore); + +        if (i->object) +            pa_msgobject_unref(i->object); + +        if (i->memchunk.memblock) +            pa_memblock_unref(i->memchunk.memblock); + +        if (i->free_cb) +            i->free_cb(i->userdata); + +        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0) +            pa_xfree(i); +    } + +    pa_asyncq_free(a->asyncq, NULL); +    pa_mutex_free(a->mutex); +    pa_xfree(a); +} + +pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q) { +    pa_assert(PA_REFCNT_VALUE(q) > 0); + +    PA_REFCNT_INC(q); +    return q; +} + +void pa_asyncmsgq_unref(pa_asyncmsgq* q) { +    pa_assert(PA_REFCNT_VALUE(q) > 0); + +    if (PA_REFCNT_DEC(q) <= 0) +        asyncmsgq_free(q); +} + +void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) { +    struct asyncmsgq_item *i; +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq)))) +        i = pa_xnew(struct asyncmsgq_item, 1); + +    i->code = code; +    i->object = object ? pa_msgobject_ref(object) : NULL; +    i->userdata = (void*) userdata; +    i->free_cb = free_cb; +    i->offset = offset; +    if (chunk) { +        pa_assert(chunk->memblock); +        i->memchunk = *chunk; +        pa_memblock_ref(i->memchunk.memblock); +    } else +        pa_memchunk_reset(&i->memchunk); +    i->semaphore = NULL; + +    /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ +    pa_mutex_lock(a->mutex); +    pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0); +    pa_mutex_unlock(a->mutex); +} + +int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk) { +    struct asyncmsgq_item i; +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    i.code = code; +    i.object = object; +    i.userdata = (void*) userdata; +    i.free_cb = NULL; +    i.ret = -1; +    i.offset = offset; +    if (chunk) { +        pa_assert(chunk->memblock); +        i.memchunk = *chunk; +    } else +        pa_memchunk_reset(&i.memchunk); + +    if (!(i.semaphore = pa_flist_pop(PA_STATIC_FLIST_GET(semaphores)))) +        i.semaphore = pa_semaphore_new(0); + +    pa_assert_se(i.semaphore); + +    /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ +    pa_mutex_lock(a->mutex); +    pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0); +    pa_mutex_unlock(a->mutex); + +    pa_semaphore_wait(i.semaphore); + +    if (pa_flist_push(PA_STATIC_FLIST_GET(semaphores), i.semaphore) < 0) +        pa_semaphore_free(i.semaphore); + +    return i.ret; +} + +int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, int wait) { +    pa_assert(PA_REFCNT_VALUE(a) > 0); +    pa_assert(!a->current); + +    if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) { +/*         pa_log("failure"); */ +        return -1; +    } + +/*     pa_log("success"); */ + +    if (code) +        *code = a->current->code; +    if (userdata) +        *userdata = a->current->userdata; +    if (offset) +        *offset = a->current->offset; +    if (object) { +        if ((*object = a->current->object)) +            pa_msgobject_assert_ref(*object); +    } +    if (chunk) +        *chunk = a->current->memchunk; + +/*     pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", (void*) a, (void*) a->current->object, a->current->object ? a->current->object->parent.type_name : NULL, a->current->code, (void*) a->current->userdata, (unsigned long) a->current->memchunk.length); */ + +    return 0; +} + +void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) { +    pa_assert(PA_REFCNT_VALUE(a) > 0); +    pa_assert(a); +    pa_assert(a->current); + +    if (a->current->semaphore) { +        a->current->ret = ret; +        pa_semaphore_post(a->current->semaphore); +    } else { + +        if (a->current->free_cb) +            a->current->free_cb(a->current->userdata); + +        if (a->current->object) +            pa_msgobject_unref(a->current->object); + +        if (a->current->memchunk.memblock) +            pa_memblock_unref(a->current->memchunk.memblock); + +        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0) +            pa_xfree(a->current); +    } + +    a->current = NULL; +} + +int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) { +    int c; +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    pa_asyncmsgq_ref(a); + +    do { +        pa_msgobject *o; +        void *data; +        int64_t offset; +        pa_memchunk chunk; +        int ret; + +        if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0) +            return -1; + +        ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk); +        pa_asyncmsgq_done(a, ret); + +    } while (c != code); + +    pa_asyncmsgq_unref(a); + +    return 0; +} + +int pa_asyncmsgq_process_one(pa_asyncmsgq *a) { +    pa_msgobject *object; +    int code; +    void *data; +    pa_memchunk chunk; +    int64_t offset; +    int ret; + +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0) +        return 0; + +    pa_asyncmsgq_ref(a); +    ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk); +    pa_asyncmsgq_done(a, ret); +    pa_asyncmsgq_unref(a); + +    return 1; +} + +int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) { +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    return pa_asyncq_get_fd(a->asyncq); +} + +int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) { +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    return pa_asyncq_before_poll(a->asyncq); +} + +void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) { +    pa_assert(PA_REFCNT_VALUE(a) > 0); + +    pa_asyncq_after_poll(a->asyncq); +} + +int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) { + +    if (object) +        return object->process_msg(object, code, userdata, offset, memchunk); + +    return 0; +} diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h new file mode 100644 index 00000000..5d3867ba --- /dev/null +++ b/src/pulsecore/asyncmsgq.h @@ -0,0 +1,75 @@ +#ifndef foopulseasyncmsgqhfoo +#define foopulseasyncmsgqhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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 <sys/types.h> + +#include <pulsecore/asyncq.h> +#include <pulsecore/memchunk.h> +#include <pulsecore/msgobject.h> + +/* A simple asynchronous message queue, based on pa_asyncq. In + * contrast to pa_asyncq this one is multiple-writer safe, though + * still not multiple-reader safe. This queue is intended to be used + * for controlling real-time threads from normal-priority + * threads. Multiple-writer-safety is accomplished by using a mutex on + * the writer side. This queue is thus not useful for communication + * between several real-time threads. + * + * The queue takes messages consisting of: + *    "Object" for which this messages is intended (may be NULL) + *    A numeric message code + *    Arbitrary userdata pointer (may be NULL) + *    A memchunk (may be NULL) + * + * There are two functions for submitting messages: _post and + * _send. The former just enqueues the message asynchronously, the + * latter waits for completion, synchronously. */ + +enum { +    PA_MESSAGE_SHUTDOWN = -1/* A generic message to inform the handler of this queue to quit */ +}; + +typedef struct pa_asyncmsgq pa_asyncmsgq; + +pa_asyncmsgq* pa_asyncmsgq_new(unsigned size); +pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q); +void pa_asyncmsgq_unref(pa_asyncmsgq* q); + +void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb); +int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk); + +int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, int wait); +int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk); +void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret); +int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code); +int pa_asyncmsgq_process_one(pa_asyncmsgq *a); + +/* Just for the reading side */ +int pa_asyncmsgq_get_fd(pa_asyncmsgq *q); +int pa_asyncmsgq_before_poll(pa_asyncmsgq *a); +void pa_asyncmsgq_after_poll(pa_asyncmsgq *a); + +#endif diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c new file mode 100644 index 00000000..75b15c0e --- /dev/null +++ b/src/pulsecore/asyncq.c @@ -0,0 +1,213 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <unistd.h> +#include <errno.h> + +#include <pulsecore/atomic.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulse/xmalloc.h> + +#include "asyncq.h" +#include "fdsem.h" + +#define ASYNCQ_SIZE 128 + +/* For debugging purposes we can define _Y to put and extra thread + * yield between each operation. */ + +/* #define PROFILE */ + +#ifdef PROFILE +#define _Y pa_thread_yield() +#else +#define _Y do { } while(0) +#endif + +struct pa_asyncq { +    unsigned size; +    unsigned read_idx; +    unsigned write_idx; +    pa_fdsem *read_fdsem, *write_fdsem; +}; + +#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq)))) + +static int is_power_of_two(unsigned size) { +    return !(size & (size - 1)); +} + +static int reduce(pa_asyncq *l, int value) { +    return value & (unsigned) (l->size - 1); +} + +pa_asyncq *pa_asyncq_new(unsigned size) { +    pa_asyncq *l; + +    if (!size) +        size = ASYNCQ_SIZE; + +    pa_assert(is_power_of_two(size)); + +    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size)); + +    l->size = size; + +    if (!(l->read_fdsem = pa_fdsem_new())) { +        pa_xfree(l); +        return NULL; +    } + +    if (!(l->write_fdsem = pa_fdsem_new())) { +        pa_fdsem_free(l->read_fdsem); +        pa_xfree(l); +        return NULL; +    } + +    return l; +} + +void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) { +    pa_assert(l); + +    if (free_cb) { +        void *p; + +        while ((p = pa_asyncq_pop(l, 0))) +            free_cb(p); +    } + +    pa_fdsem_free(l->read_fdsem); +    pa_fdsem_free(l->write_fdsem); +    pa_xfree(l); +} + +int pa_asyncq_push(pa_asyncq*l, void *p, int wait) { +    int idx; +    pa_atomic_ptr_t *cells; + +    pa_assert(l); +    pa_assert(p); + +    cells = PA_ASYNCQ_CELLS(l); + +    _Y; +    idx = reduce(l, l->write_idx); + +    if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) { + +        if (!wait) +            return -1; + +/*         pa_log("sleeping on push"); */ + +        do { +            pa_fdsem_wait(l->read_fdsem); +        } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)); +    } + +    _Y; +    l->write_idx++; + +    pa_fdsem_post(l->write_fdsem); + +    return 0; +} + +void* pa_asyncq_pop(pa_asyncq*l, int wait) { +    int idx; +    void *ret; +    pa_atomic_ptr_t *cells; + +    pa_assert(l); + +    cells = PA_ASYNCQ_CELLS(l); + +    _Y; +    idx = reduce(l, l->read_idx); + +    if (!(ret = pa_atomic_ptr_load(&cells[idx]))) { + +        if (!wait) +            return NULL; + +/*         pa_log("sleeping on pop"); */ + +        do { +            pa_fdsem_wait(l->write_fdsem); +        } while (!(ret = pa_atomic_ptr_load(&cells[idx]))); +    } + +    pa_assert(ret); + +    /* Guaranteed to succeed if we only have a single reader */ +    pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL)); + +    _Y; +    l->read_idx++; + +    pa_fdsem_post(l->read_fdsem); + +    return ret; +} + +int pa_asyncq_get_fd(pa_asyncq *q) { +    pa_assert(q); + +    return pa_fdsem_get(q->write_fdsem); +} + +int pa_asyncq_before_poll(pa_asyncq *l) { +    int idx; +    pa_atomic_ptr_t *cells; + +    pa_assert(l); + +    cells = PA_ASYNCQ_CELLS(l); + +    _Y; +    idx = reduce(l, l->read_idx); + +    for (;;) { +        if (pa_atomic_ptr_load(&cells[idx])) +            return -1; + +        if (pa_fdsem_before_poll(l->write_fdsem) >= 0) +            return 0; +    } + +    return 0; +} + +void pa_asyncq_after_poll(pa_asyncq *l) { +    pa_assert(l); + +    pa_fdsem_after_poll(l->write_fdsem); +} diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h new file mode 100644 index 00000000..53d45866 --- /dev/null +++ b/src/pulsecore/asyncq.h @@ -0,0 +1,56 @@ +#ifndef foopulseasyncqhfoo +#define foopulseasyncqhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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 <sys/types.h> +#include <pulse/def.h> + +/* A simple, asynchronous, lock-free (if requested also wait-free) + * queue. Not multiple-reader/multiple-writer safe. If that is + * required both sides can be protected by a mutex each. --- Which is + * not a bad thing in most cases, since this queue is intended for + * communication between a normal thread and a single real-time + * thread. Only the real-time side needs to be lock-free/wait-free. + * + * If the queue is full and another entry shall be pushed, or when the + * queue is empty and another entry shall be popped and the "wait" + * argument is non-zero, the queue will block on a UNIX FIFO object -- + * that will probably require locking on the kernel side -- which + * however is probably not problematic, because we do it only on + * starvation or overload in which case we have to block anyway.  */ + +typedef struct pa_asyncq pa_asyncq; + +pa_asyncq* pa_asyncq_new(unsigned size); +void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb); + +void* pa_asyncq_pop(pa_asyncq *q, int wait); +int pa_asyncq_push(pa_asyncq *q, void *p, int wait); + +int pa_asyncq_get_fd(pa_asyncq *q); +int pa_asyncq_before_poll(pa_asyncq *a); +void pa_asyncq_after_poll(pa_asyncq *a); + +#endif diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index 013e8c20..c2c99888 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -24,48 +24,201 @@    USA.  ***/ -#include <atomic_ops.h> - -/* atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). +/* + * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).  It is + * not guaranteed however, that sizeof(AO_t) == sizeof(size_t). + * however very likely. + * + * For now we do only full memory barriers. Eventually we might want + * to support more elaborate memory barriers, in which case we will add + * suffixes to the function names.   * - * It is not guaranteed however, that sizeof(AO_t) == sizeof(size_t). - * however very likely. */ + * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use + * libatomic_ops + */ -typedef struct pa_atomic_int { -    volatile AO_t value; -} pa_atomic_int_t; +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif + +#ifdef HAVE_ATOMIC_BUILTINS + +/* __sync based implementation */ + +typedef struct pa_atomic { +    volatile int value; +} pa_atomic_t;  #define PA_ATOMIC_INIT(v) { .value = (v) } -/* For now we do only full memory barriers. Eventually we might want - * to support more elaborate memory barriers, in which case we will add - * suffixes to the function names */ +static inline int pa_atomic_load(const pa_atomic_t *a) { +    __sync_synchronize(); +    return a->value; +} + +static inline void pa_atomic_store(pa_atomic_t *a, int i) { +    a->value = i; +    __sync_synchronize(); +} + +/* Returns the previously set value */ +static inline int pa_atomic_add(pa_atomic_t *a, int i) { +    return __sync_fetch_and_add(&a->value, i); +} + +/* Returns the previously set value */ +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { +    return __sync_fetch_and_sub(&a->value, i); +} + +/* Returns the previously set value */ +static inline int pa_atomic_inc(pa_atomic_t *a) { +    return pa_atomic_add(a, 1); +} + +/* Returns the previously set value */ +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) { +    return __sync_bool_compare_and_swap(&a->value, old_i, new_i); +} + +typedef struct pa_atomic_ptr { +    volatile unsigned long value; +} pa_atomic_ptr_t; + +#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } + +static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { +    __sync_synchronize(); +    return (void*) a->value; +} + +static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { +    a->value = (unsigned long) p; +    __sync_synchronize(); +} + +static inline int 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." + +/* Addapted from glibc */ + +typedef struct pa_atomic { +    volatile int value; +} pa_atomic_t; + +#define PA_ATOMIC_INIT(v) { .value = (v) } + +static inline int pa_atomic_load(const pa_atomic_t *a) { +    return a->value; +} + +static inline void pa_atomic_store(pa_atomic_t *a, int i) { +    a->value = i; +} + +static inline int pa_atomic_add(pa_atomic_t *a, int i) { +    int result; + +    __asm __volatile ("lock; xaddl %0, %1" +                      : "=r" (result), "=m" (a->value) +                      : "0" (i), "m" (a->value)); + +    return result; +} + +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { +    return pa_atomic_add(a, -i); +} + +static inline int pa_atomic_inc(pa_atomic_t *a) { +    return pa_atomic_add(a, 1); +} -static inline int pa_atomic_load(const pa_atomic_int_t *a) { +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) { +    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; +} + +typedef struct pa_atomic_ptr { +    volatile unsigned long value; +} pa_atomic_ptr_t; + +#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } + +static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { +    return (void*) a->value; +} + +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) { +    void *result; + +    __asm__ __volatile__ ("lock; cmpxchgq %q2, %1" +                          : "=a" (result), "=m" (a->value) +                          : "r" (new_p), "m" (a->value), "0" (old_p)); + +    return result; +} + +#else + +/* libatomic_ops based implementation */ + +#include <atomic_ops.h> + +typedef struct pa_atomic { +    volatile AO_t value; +} pa_atomic_t; + +#define PA_ATOMIC_INIT(v) { .value = (v) } + +static inline int pa_atomic_load(const pa_atomic_t *a) {      return (int) AO_load_full((AO_t*) &a->value);  } -static inline void pa_atomic_store(pa_atomic_int_t *a, int i) { +static inline void pa_atomic_store(pa_atomic_t *a, int i) {      AO_store_full(&a->value, (AO_t) i);  } -static inline int pa_atomic_add(pa_atomic_int_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);  } -static inline int pa_atomic_sub(pa_atomic_int_t *a, int i) { +static inline int pa_atomic_sub(pa_atomic_t *a, int i) {      return AO_fetch_and_add_full(&a->value, (AO_t) -i);  } -static inline int pa_atomic_inc(pa_atomic_int_t *a) { +static inline int pa_atomic_inc(pa_atomic_t *a) {      return AO_fetch_and_add1_full(&a->value);  } -static inline int pa_atomic_dec(pa_atomic_int_t *a) { +static inline int pa_atomic_dec(pa_atomic_t *a) {      return AO_fetch_and_sub1_full(&a->value);  } -static inline int pa_atomic_cmpxchg(pa_atomic_int_t *a, int old_i, int new_i) { +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);  } @@ -73,6 +226,8 @@ typedef struct pa_atomic_ptr {      volatile AO_t value;  } pa_atomic_ptr_t; +#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) } +  static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {      return (void*) AO_load_full((AO_t*) &a->value);  } @@ -86,3 +241,5 @@ static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* n  }  #endif + +#endif diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c index 3b8304b2..54154500 100644 --- a/src/pulsecore/authkey-prop.c +++ b/src/pulsecore/authkey-prop.c @@ -21,44 +21,56 @@    USA.  ***/ -#include <assert.h> +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +  #include <string.h>  #include <pulse/xmalloc.h>  #include <pulsecore/props.h> +#include <pulsecore/macro.h>  #include <pulsecore/log.h> +#include <pulsecore/refcnt.h>  #include "authkey-prop.h"  struct authkey_data { -    int ref; +    PA_REFCNT_DECLARE;      size_t length;  };  int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {      struct authkey_data *a; -    assert(c && name && data && len > 0); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(data); +    pa_assert(len > 0);      if (!(a = pa_property_get(c, name)))          return -1; -    assert(a->length == len); -    memcpy(data, a+1, len); +    pa_assert(a->length == len); +    memcpy(data, (uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), len); +      return 0;  }  int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {      struct authkey_data *a; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if (pa_property_get(c, name))          return -1; -    a = pa_xmalloc(sizeof(struct authkey_data) + len); -    a->ref = 1; +    a = pa_xmalloc(PA_ALIGN(sizeof(struct authkey_data)) + len); +    PA_REFCNT_INIT(a);      a->length = len; -    memcpy(a+1, data, len); +    memcpy((uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), data, len);      pa_property_set(c, name, a); @@ -67,22 +79,27 @@ int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t l  void pa_authkey_prop_ref(pa_core *c, const char *name) {      struct authkey_data *a; -    assert(c && name); -    a = pa_property_get(c, name); -    assert(a && a->ref >= 1); +    pa_assert(c); +    pa_assert(name); -    a->ref++; +    a = pa_property_get(c, name); +    pa_assert(a); +    pa_assert(PA_REFCNT_VALUE(a) >= 1); +    PA_REFCNT_INC(a);  }  void pa_authkey_prop_unref(pa_core *c, const char *name) {      struct authkey_data *a; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      a = pa_property_get(c, name); -    assert(a && a->ref >= 1); +    pa_assert(a); +    pa_assert(PA_REFCNT_VALUE(a) >= 1); -    if (!(--a->ref)) { +    if (PA_REFCNT_DEC(a) <= 0) {          pa_property_remove(c, name);          pa_xfree(a);      } diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c index a6150d0e..80bc8576 100644 --- a/src/pulsecore/authkey.c +++ b/src/pulsecore/authkey.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <unistd.h>  #include <fcntl.h>  #include <string.h> @@ -43,21 +42,25 @@  #include <pulsecore/core-util.h>  #include <pulsecore/log.h>  #include <pulsecore/random.h> +#include <pulsecore/macro.h>  #include "authkey.h"  /* Generate a new authorization key, store it in file fd and return it in *data  */  static int generate(int fd, void *ret_data, size_t length) {      ssize_t r; -    assert(fd >= 0 && ret_data && length); + +    pa_assert(fd >= 0); +    pa_assert(ret_data); +    pa_assert(length > 0);      pa_random(ret_data, length);      lseek(fd, 0, SEEK_SET); -    ftruncate(fd, 0); +    (void) ftruncate(fd, 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)); +        pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));          return -1;      } @@ -68,6 +71,10 @@ static int generate(int fd, void *ret_data, size_t length) {  #define O_BINARY 0  #endif +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif +  /* Load an euthorization cookie from file fn and store it in data. If   * the cookie file doesn't exist, create it */  static int load(const char *fn, void *data, size_t length) { @@ -75,11 +82,15 @@ static int load(const char *fn, void *data, size_t length) {      int writable = 1;      int unlock = 0, ret = -1;      ssize_t r; -    assert(fn && data && length); -    if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) { -        if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) { -            pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); +    pa_assert(fn); +    pa_assert(data); +    pa_assert(length > 0); + +    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));              goto finish;          } else              writable = 0; @@ -88,15 +99,15 @@ static int load(const char *fn, void *data, size_t length) {      unlock = pa_lock_fd(fd, 1) >= 0;      if ((r = pa_loop_read(fd, data, length, NULL)) < 0) { -        pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno)); +        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));          goto finish;      }      if ((size_t) r != length) { -        pa_log_debug("got %d bytes from cookie file '%s', expected %d", (int)r, fn, (int)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("Unable to write cookie to read only file");              goto finish;          } @@ -113,7 +124,10 @@ finish:          if (unlock)              pa_lock_fd(fd, 0); -        close(fd); +        if (pa_close(fd) < 0) { +            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno)); +            ret = -1; +        }      }      return ret; @@ -123,13 +137,12 @@ finish:  int pa_authkey_load(const char *path, void *data, size_t length) {      int ret; -    assert(path && data && length); +    pa_assert(path); +    pa_assert(data); +    pa_assert(length > 0); -    ret = load(path, data, length); - -    if (ret < 0) -        pa_log("Failed to load authorization key '%s': %s", path, -               (ret == -1) ? pa_cstrerror(errno) : "file corrupt"); +    if ((ret = load(path, data, length)) < 0) +        pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");      return ret;  } @@ -137,7 +150,10 @@ int pa_authkey_load(const char *path, void *data, size_t length) {  /* If the specified file path starts with / return it, otherwise   * return path prepended with home directory */  static const char *normalize_path(const char *fn, char *s, size_t l) { -    assert(fn && s && l > 0); + +    pa_assert(fn); +    pa_assert(s); +    pa_assert(l > 0);  #ifndef OS_IS_WIN32      if (fn[0] != '/') { @@ -145,13 +161,14 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {      if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {  #endif          char homedir[PATH_MAX]; +          if (!pa_get_home_dir(homedir, sizeof(homedir)))              return NULL;  #ifndef OS_IS_WIN32 -        snprintf(s, l, "%s/%s", homedir, fn); +        pa_snprintf(s, l, "%s/%s", homedir, fn);  #else -        snprintf(s, l, "%s\\%s", homedir, fn); +        pa_snprintf(s, l, "%s\\%s", homedir, fn);  #endif          return s;      } @@ -164,7 +181,10 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {  int pa_authkey_load_auto(const char *fn, void *data, size_t length) {      char path[PATH_MAX];      const char *p; -    assert(fn && data && length); + +    pa_assert(fn); +    pa_assert(data); +    pa_assert(length > 0);      if (!(p = normalize_path(fn, path, sizeof(path))))          return -2; @@ -179,20 +199,23 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {      ssize_t r;      char path[PATH_MAX];      const char *p; -    assert(fn && data && length); + +    pa_assert(fn); +    pa_assert(data); +    pa_assert(length > 0);      if (!(p = normalize_path(fn, path, sizeof(path))))          return -2; -    if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { -        pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); +    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));          goto finish;      }      unlock = pa_lock_fd(fd, 1) >= 0;      if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) { -        pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno)); +        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));          goto finish;      } @@ -205,7 +228,10 @@ finish:          if (unlock)              pa_lock_fd(fd, 0); -        close(fd); +        if (pa_close(fd) < 0) { +            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno)); +            ret = -1; +        }      }      return ret; diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c index 6f888526..a1d3e02d 100644 --- a/src/pulsecore/autoload.c +++ b/src/pulsecore/autoload.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -36,13 +35,14 @@  #include <pulsecore/memchunk.h>  #include <pulsecore/sound-file.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include <pulsecore/core-scache.h>  #include <pulsecore/core-subscribe.h>  #include "autoload.h"  static void entry_free(pa_autoload_entry *e) { -    assert(e); +    pa_assert(e);      pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);      pa_xfree(e->name);      pa_xfree(e->module); @@ -51,7 +51,8 @@ static void entry_free(pa_autoload_entry *e) {  }  static void entry_remove_and_free(pa_autoload_entry *e) { -    assert(e && e->core); +    pa_assert(e); +    pa_assert(e->core);      pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);      pa_hashmap_remove(e->core->autoload_hashmap, e->name); @@ -60,12 +61,14 @@ static void entry_remove_and_free(pa_autoload_entry *e) {  static pa_autoload_entry* entry_new(pa_core *c, const char *name) {      pa_autoload_entry *e = NULL; -    assert(c && name); + +    pa_core_assert_ref(c); +    pa_assert(name);      if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))          return NULL; -    e = pa_xmalloc(sizeof(pa_autoload_entry)); +    e = pa_xnew(pa_autoload_entry, 1);      e->core = c;      e->name = pa_xstrdup(name);      e->module = e->argument = NULL; @@ -73,7 +76,7 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {      if (!c->autoload_hashmap)          c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -    assert(c->autoload_hashmap); +    pa_assert(c->autoload_hashmap);      pa_hashmap_put(c->autoload_hashmap, e->name, e); @@ -88,7 +91,11 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {  int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {      pa_autoload_entry *e = NULL; -    assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(module); +    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);      if (!(e = entry_new(c, name)))          return -1; @@ -105,7 +112,10 @@ int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const c  int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {      pa_autoload_entry *e; -    assert(c && name && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);      if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)          return -1; @@ -116,7 +126,9 @@ int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t ty  int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {      pa_autoload_entry *e; -    assert(c && idx != PA_IDXSET_INVALID); + +    pa_assert(c); +    pa_assert(idx != PA_IDXSET_INVALID);      if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))          return -1; @@ -128,7 +140,9 @@ int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {  void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {      pa_autoload_entry *e;      pa_module *m; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))          return; @@ -153,6 +167,7 @@ static void free_func(void *p, PA_GCC_UNUSED void *userdata) {  }  void pa_autoload_free(pa_core *c) { +      if (c->autoload_hashmap) {          pa_hashmap_free(c->autoload_hashmap, free_func, NULL);          c->autoload_hashmap = NULL; @@ -166,7 +181,9 @@ void pa_autoload_free(pa_core *c) {  const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {      pa_autoload_entry *e; -    assert(c && name); + +    pa_core_assert_ref(c); +    pa_assert(name);      if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)          return NULL; @@ -176,7 +193,9 @@ const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa  const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {      pa_autoload_entry *e; -    assert(c && idx != PA_IDXSET_INVALID); + +    pa_core_assert_ref(c); +    pa_assert(idx != PA_IDXSET_INVALID);      if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))          return NULL; diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c index 855ed567..fae54810 100644 --- a/src/pulsecore/avahi-wrap.c +++ b/src/pulsecore/avahi-wrap.c @@ -21,11 +21,14 @@    USA.  ***/ -#include <assert.h> +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif  #include <pulse/xmalloc.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "avahi-wrap.h" @@ -61,9 +64,9 @@ static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) {  static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {      AvahiWatch *w = userdata; -    assert(a); -    assert(e); -    assert(w); +    pa_assert(a); +    pa_assert(e); +    pa_assert(w);      w->current_event = translate_io_flags_back(events);      w->callback(w, fd, w->current_event, w->userdata); @@ -74,12 +77,10 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event      pa_avahi_poll *p;      AvahiWatch *w; -    assert(api); -    assert(fd >= 0); -    assert(callback); - -    p = api->userdata; -    assert(p); +    pa_assert(api); +    pa_assert(fd >= 0); +    pa_assert(callback); +    pa_assert_se(p = api->userdata);      w = pa_xnew(AvahiWatch, 1);      w->avahi_poll = p; @@ -92,19 +93,19 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event  }  static void watch_update(AvahiWatch *w, AvahiWatchEvent event) { -    assert(w); +    pa_assert(w);      w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event));  }  static AvahiWatchEvent watch_get_events(AvahiWatch *w) { -    assert(w); +    pa_assert(w);      return w->current_event;  }  static void watch_free(AvahiWatch *w) { -    assert(w); +    pa_assert(w);      w->avahi_poll->mainloop->io_free(w->io_event);      pa_xfree(w); @@ -120,9 +121,9 @@ struct AvahiTimeout {  static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {      AvahiTimeout *t = userdata; -    assert(a); -    assert(e); -    assert(t); +    pa_assert(a); +    pa_assert(e); +    pa_assert(t);      t->callback(t, t->userdata);  } @@ -131,11 +132,9 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,      pa_avahi_poll *p;      AvahiTimeout *t; -    assert(api); -    assert(callback); - -    p = api->userdata; -    assert(p); +    pa_assert(api); +    pa_assert(callback); +    pa_assert_se(p = api->userdata);      t = pa_xnew(AvahiTimeout, 1);      t->avahi_poll = p; @@ -148,7 +147,7 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,  }  static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { -    assert(t); +    pa_assert(t);      if (t->time_event && tv)          t->avahi_poll->mainloop->time_restart(t->time_event, tv); @@ -161,7 +160,7 @@ static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {  }  static void timeout_free(AvahiTimeout *t) { -    assert(t); +    pa_assert(t);      if (t->time_event)          t->avahi_poll->mainloop->time_free(t->time_event); @@ -171,7 +170,7 @@ static void timeout_free(AvahiTimeout *t) {  AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {      pa_avahi_poll *p; -    assert(m); +    pa_assert(m);      p = pa_xnew(pa_avahi_poll, 1); @@ -190,9 +189,8 @@ AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {  void pa_avahi_poll_free(AvahiPoll *api) {      pa_avahi_poll *p; -    assert(api); -    p = api->userdata; -    assert(p); +    pa_assert(api); +    pa_assert_se(p = api->userdata);      pa_xfree(p);  } diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 6989069d..539fd34d 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -28,7 +28,6 @@  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <errno.h>  #include <unistd.h> @@ -95,6 +94,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb  static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); @@ -114,6 +114,9 @@ static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf  static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);  /* A method table for all available commands */ @@ -130,13 +133,14 @@ static const struct command commands[] = {      { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },      { "ls",                      pa_cli_command_info,               NULL,                           1 },      { "list",                    pa_cli_command_info,               NULL,                           1 }, -    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)",                     3}, -    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index)",                             2}, -    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)",             3}, -    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index|name, volume)", 3}, +    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)", 3}, +    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index)", 2}, +    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)", 3}, +    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},      { "set-source-volume",       pa_cli_command_source_volume,      "Set the volume of a source (args: index|name, volume)", 3}, -    { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, mute)", 3}, -    { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, mute)", 3}, +    { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3}, +    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3}, +    { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},      { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},      { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},      { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2}, @@ -159,6 +163,9 @@ static const struct command commands[] = {      { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},      { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},      { "vacuum",                  pa_cli_command_vacuum,             NULL, 1}, +    { "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},      { NULL, NULL, NULL, 0 }  }; @@ -174,15 +181,23 @@ static uint32_t parse_index(const char *n) {      return idx;  } -static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) { -    assert(c && c->mainloop && t); +static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      c->mainloop->quit(c->mainloop, 0);      return 0;  } -static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const struct command*command; -    assert(c && t && buf); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      pa_strbuf_puts(buf, "Available commands:\n"); @@ -192,67 +207,91 @@ static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G      return 0;  } -static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_module_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_module_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_client_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_client_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_sink_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_sink_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_source_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_source_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_sink_input_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_sink_input_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_source_output_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_source_output_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s);      return 0;  } -static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char s[256];      const pa_mempool_stat *stat;      unsigned k; @@ -267,8 +306,10 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G          [PA_MEMBLOCK_IMPORTED] = "IMPORTED",      }; -    assert(c); -    assert(t); +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      stat = pa_mempool_get_stat(c->mempool); @@ -312,7 +353,11 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G  }  static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { -    assert(c && t); +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      pa_cli_command_stat(c, t, buf, fail);      pa_cli_command_modules(c, t, buf, fail);      pa_cli_command_sinks(c, t, buf, fail); @@ -325,10 +370,14 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int      return 0;  } -static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      pa_module *m;      const char *name; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(name = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); @@ -343,12 +392,16 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G      return 0;  } -static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      pa_module *m;      uint32_t idx;      const char *i;      char *e; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(i = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify the module index.\n"); @@ -365,12 +418,17 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA      return 0;  } -static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *v;      pa_sink *sink;      uint32_t volume;      pa_cvolume cvolume; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");          return -1; @@ -392,17 +450,22 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu      }      pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); -    pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume); +    pa_sink_set_volume(sink, &cvolume);      return 0;  } -static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *v;      pa_sink_input *si;      pa_volume_t volume;      pa_cvolume cvolume;      uint32_t idx; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");          return -1; @@ -433,12 +496,17 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb      return 0;  } -static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *v;      pa_source *source;      uint32_t volume;      pa_cvolume cvolume; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");          return -1; @@ -460,15 +528,20 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *      }      pa_cvolume_set(&cvolume, source->sample_spec.channels, volume); -    pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume); +    pa_source_set_volume(source, &cvolume);      return 0;  } -static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *m;      pa_sink *sink;      int mute; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");          return -1; @@ -489,15 +562,20 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,          return -1;      } -    pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute); +    pa_sink_set_mute(sink, mute);      return 0;  } -static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *m;      pa_source *source;      int mute; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");          return -1; @@ -518,13 +596,57 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu          return -1;      } -    pa_source_set_mute(source, PA_MIXER_HARDWARE, mute); +    pa_source_set_mute(source, mute);      return 0;  } -static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    const char *n, *v; +    pa_sink_input *si; +    uint32_t idx; +    int mute; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(n = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); +        return -1; +    } + +    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { +        pa_strbuf_puts(buf, "Failed to parse index.\n"); +        return -1; +    } + +    if (!(v = pa_tokenizer_get(t, 2))) { +        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); +        return -1; +    } + +    if (pa_atoi(v, &mute) < 0) { +        pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); +        return -1; +    } + +    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { +        pa_strbuf_puts(buf, "No sink input found with this index.\n"); +        return -1; +    } + +    pa_sink_input_set_mute(si, mute); +    return 0; +} + +static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); @@ -535,9 +657,13 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b      return 0;  } -static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); @@ -548,11 +674,15 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf      return 0;  } -static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n;      pa_client *client;      uint32_t idx; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); @@ -573,11 +703,15 @@ static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *bu      return 0;  } -static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n;      pa_sink_input *sink_input;      uint32_t idx; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); @@ -598,11 +732,15 @@ static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf      return 0;  } -static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n;      pa_source_output *source_output;      uint32_t idx; -    assert(c && t); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); @@ -623,20 +761,29 @@ static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_str      return 0;  } -static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_scache_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_scache_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s); +      return 0;  }  static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n, *sink_name;      pa_sink *sink; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {          pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); @@ -658,7 +805,11 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu  static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *n; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sample name.\n"); @@ -676,7 +827,11 @@ static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *  static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *fname, *n;      int r; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n"); @@ -696,7 +851,11 @@ static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *bu  static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *pname; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(pname = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a path name.\n"); @@ -714,7 +873,11 @@ static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf  static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *fname, *sink_name;      pa_sink *sink; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {          pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n"); @@ -732,7 +895,11 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,  static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *a, *b; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {          pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); @@ -746,7 +913,11 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b  static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      const char *name; -    assert(c && t && buf && fail); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      if (!(name = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a device name\n"); @@ -761,25 +932,36 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf      return 0;  } -static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      char *s; -    assert(c && t); -    s = pa_autoload_list_to_string(c); -    assert(s); + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    pa_assert_se(s = pa_autoload_list_to_string(c));      pa_strbuf_puts(buf, s);      pa_xfree(s); +      return 0;  } -static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { -    assert(c && t); +static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      pa_property_dump(c, buf);      return 0;  }  static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { -    assert(c); -    assert(t); +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      pa_mempool_vacuum(c->mempool); @@ -792,6 +974,11 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf      pa_sink *sink;      uint32_t idx; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");          return -1; @@ -830,6 +1017,11 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str      pa_source *source;      uint32_t idx; +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); +      if (!(n = pa_tokenizer_get(t, 1))) {          pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");          return -1; @@ -862,7 +1054,105 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str      return 0;  } -static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { +static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    const char *n, *m; +    pa_sink *sink; +    int suspend; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(n = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); +        return -1; +    } + +    if (!(m = pa_tokenizer_get(t, 2))) { +        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n"); +        return -1; +    } + +    if (pa_atoi(m, &suspend) < 0) { +        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); +        return -1; +    } + +    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { +        pa_strbuf_puts(buf, "No sink found by this name or index.\n"); +        return -1; +    } + +    pa_sink_suspend(sink, suspend); +    return 0; +} + +static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    const char *n, *m; +    pa_source *source; +    int suspend; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(n = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); +        return -1; +    } + +    if (!(m = pa_tokenizer_get(t, 2))) { +        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n"); +        return -1; +    } + +    if (pa_atoi(m, &suspend) < 0) { +        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); +        return -1; +    } + +    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { +        pa_strbuf_puts(buf, "No source found by this name or index.\n"); +        return -1; +    } + +    pa_source_suspend(source, suspend); +    return 0; +} + +static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { +    const char *m; +    int suspend; +    int ret; + +    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 suspend switch setting (0/1).\n"); +        return -1; +    } + +    if (pa_atoi(m, &suspend) < 0) { +        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); +        return -1; +    } + +    ret = - (pa_sink_suspend_all(c, suspend) < 0); +    if (pa_source_suspend_all(c, suspend) < 0) +        ret = -1; + +    if (ret < 0) +        pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n"); + +    return 0; +} + +static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {      pa_module *m;      pa_sink *sink;      pa_source *source; @@ -874,7 +1164,10 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G      void *i;      pa_autoload_entry *a; -    assert(c && t); +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail);      time(&now); @@ -884,7 +1177,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G      pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));  #endif -      for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {          if (m->auto_unload)              continue; @@ -900,7 +1192,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G      nl = 0;      for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { -        if (sink->owner && sink->owner->auto_unload) +        if (sink->module && sink->module->auto_unload)              continue;          if (!nl) { @@ -908,12 +1200,12 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G              nl = 1;          } -        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE))); -        pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE)); +        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 %d\n", sink->name, pa_sink_get_mute(sink));      }      for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { -        if (source->owner && source->owner->auto_unload) +        if (source->module && source->module->auto_unload)              continue;          if (!nl) { @@ -921,8 +1213,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G              nl = 1;          } -        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE))); -        pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE)); +        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 %d\n", source->name, pa_source_get_mute(source));      } @@ -972,6 +1264,10 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G  int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate) {      const char *cs; +    pa_assert(c); +    pa_assert(s); +    pa_assert(buf); +      cs = s+strspn(s, whitespace);      if (*cs == '#' || !*cs) @@ -1006,9 +1302,9 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b              if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {                  const char *filename = cs+l+strspn(cs+l, whitespace); -                  if (pa_cli_command_execute_file(c, filename, buf, fail) < 0) -                    if (*fail) return -1; +                    if (*fail) +                        return -1;              } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {                  if (!ifstate) {                      pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs); @@ -1020,6 +1316,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b                      const char *filename = cs+l+strspn(cs+l, whitespace);                      *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE; +                    pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");                  }              } else {                  pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); @@ -1034,14 +1331,13 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b          if (ifstate && *ifstate == IFSTATE_FALSE)               return 0; -          l = strcspn(cs, whitespace);          for (command = commands; command->name; command++)              if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {                  int ret;                  pa_tokenizer *t = pa_tokenizer_new(cs, command->args); -                assert(t); +                pa_assert(t);                  ret = command->proc(c, t, buf, fail);                  pa_tokenizer_free(t);                  unknown = 0; @@ -1072,9 +1368,9 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int      int ifstate = IFSTATE_NONE;      int ret = -1; -    assert(c); -    assert(fn); -    assert(buf); +    pa_assert(c); +    pa_assert(fn); +    pa_assert(buf);      if (!(f = fopen(fn, "r"))) {          pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno)); @@ -1104,9 +1400,9 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)      const char *p;      int ifstate = IFSTATE_NONE; -    assert(c); -    assert(s); -    assert(buf); +    pa_assert(c); +    pa_assert(s); +    pa_assert(buf);      p = s;      while (*p) { @@ -1125,3 +1421,4 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)      return 0;  } + diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 413f9334..6683e697 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <string.h>  #include <pulse/volume.h> @@ -41,6 +40,7 @@  #include <pulsecore/sample-util.h>  #include <pulsecore/core-scache.h>  #include <pulsecore/autoload.h> +#include <pulsecore/macro.h>  #include "cli-text.h" @@ -48,10 +48,9 @@ char *pa_module_list_to_string(pa_core *c) {      pa_strbuf *s;      pa_module *m;      uint32_t idx = PA_IDXSET_INVALID; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); @@ -72,10 +71,9 @@ char *pa_client_list_to_string(pa_core *c) {      pa_strbuf *s;      pa_client *client;      uint32_t idx = PA_IDXSET_INVALID; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); @@ -93,10 +91,15 @@ char *pa_sink_list_to_string(pa_core *c) {      pa_strbuf *s;      pa_sink *sink;      uint32_t idx = PA_IDXSET_INVALID; -    assert(c); +    static const char* const state_table[] = { +        [PA_SINK_RUNNING] = "RUNNING", +        [PA_SINK_SUSPENDED] = "SUSPENDED", +        [PA_SINK_IDLE] = "IDLE", +        [PA_SINK_UNLINKED] = "UNLINKED" +    }; +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); @@ -108,22 +111,35 @@ char *pa_sink_list_to_string(pa_core *c) {              "  %c index: %u\n"              "\tname: <%s>\n"              "\tdriver: <%s>\n" +            "\tflags: %s%s%s\n" +            "\tstate: %s\n"              "\tvolume: <%s>\n" +            "\tmute: <%i>\n"              "\tlatency: <%0.0f usec>\n" -            "\tmonitor_source: <%u>\n" +            "\tmonitor source: <%u>\n"              "\tsample spec: <%s>\n" -            "\tchannel map: <%s>\n", +            "\tchannel map: <%s>\n" +            "\tused by: <%u>\n" +            "\tlinked by: <%u>\n",              c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', -            sink->index, sink->name, +            sink->index, +            sink->name,              sink->driver, -            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)), +            sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", +            sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", +            sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", +            state_table[pa_sink_get_state(sink)], +            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), +            !!pa_sink_get_mute(sink),              (double) pa_sink_get_latency(sink),              sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,              pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), -            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map)); +            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), +            pa_sink_used_by(sink), +            pa_sink_linked_by(sink)); -        if (sink->owner) -            pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); +        if (sink->module) +            pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index);          if (sink->description)              pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);      } @@ -135,15 +151,20 @@ char *pa_source_list_to_string(pa_core *c) {      pa_strbuf *s;      pa_source *source;      uint32_t idx = PA_IDXSET_INVALID; -    assert(c); +    static const char* const state_table[] = { +        [PA_SOURCE_RUNNING] = "RUNNING", +        [PA_SOURCE_SUSPENDED] = "SUSPENDED", +        [PA_SOURCE_IDLE] = "IDLE", +        [PA_SOURCE_UNLINKED] = "UNLINKED" +    }; +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));      for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { -        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; +        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];          pa_strbuf_printf( @@ -151,21 +172,35 @@ char *pa_source_list_to_string(pa_core *c) {              "  %c index: %u\n"              "\tname: <%s>\n"              "\tdriver: <%s>\n" +            "\tflags: %s%s%s\n" +            "\tstate: %s\n" +            "\tvolume: <%s>\n" +            "\tmute: <%u>\n"              "\tlatency: <%0.0f usec>\n"              "\tsample spec: <%s>\n" -            "\tchannel map: <%s>\n", +            "\tchannel map: <%s>\n" +            "\tused by: <%u>\n" +            "\tlinked by: <%u>\n",              c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',              source->index,              source->name,              source->driver, +            source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", +            source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", +            source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", +            state_table[pa_source_get_state(source)], +            pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), +            !!pa_source_get_mute(source),              (double) pa_source_get_latency(source),              pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), -            pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map)); +            pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), +            pa_source_used_by(source), +            pa_source_linked_by(source));          if (source->monitor_of)              pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); -        if (source->owner) -            pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); +        if (source->module) +            pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index);          if (source->description)              pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);      } @@ -179,37 +214,41 @@ char *pa_source_output_list_to_string(pa_core *c) {      pa_source_output *o;      uint32_t idx = PA_IDXSET_INVALID;      static const char* const state_table[] = { -        "RUNNING", -        "CORKED", -        "DISCONNECTED" +        [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING", +        [PA_SOURCE_OUTPUT_CORKED] = "CORKED", +        [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"      }; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));      for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {          char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; -        assert(o->source); +        pa_assert(o->source);          pa_strbuf_printf(              s,              "    index: %u\n"              "\tname: '%s'\n"              "\tdriver: <%s>\n" +            "\tflags: %s%s\n"              "\tstate: %s\n"              "\tsource: <%u> '%s'\n" +            "\tlatency: <%0.0f usec>\n"              "\tsample spec: <%s>\n"              "\tchannel map: <%s>\n"              "\tresample method: %s\n",              o->index,              o->name,              o->driver, -            state_table[o->state], +            o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", +            o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "", +            state_table[pa_source_output_get_state(o)],              o->source->index, o->source->name, +            (double) pa_source_output_get_latency(o),              pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),              pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),              pa_resample_method_to_string(pa_source_output_get_resample_method(o))); @@ -227,30 +266,32 @@ char *pa_sink_input_list_to_string(pa_core *c) {      pa_sink_input *i;      uint32_t idx = PA_IDXSET_INVALID;      static const char* const state_table[] = { -        "RUNNING", -        "CORKED", -        "DISCONNECTED" +        [PA_SINK_INPUT_RUNNING] = "RUNNING", +        [PA_SINK_INPUT_DRAINED] = "DRAINED", +        [PA_SINK_INPUT_CORKED] = "CORKED", +        [PA_SINK_INPUT_UNLINKED] = "UNLINKED"      }; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));      for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {          char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; -        assert(i->sink); +        pa_assert(i->sink);          pa_strbuf_printf(              s,              "    index: %u\n"              "\tname: <%s>\n"              "\tdriver: <%s>\n" +            "\tflags: %s%s\n"              "\tstate: %s\n"              "\tsink: <%u> '%s'\n"              "\tvolume: <%s>\n" +            "\tmute: <%i>\n"              "\tlatency: <%0.0f usec>\n"              "\tsample spec: <%s>\n"              "\tchannel map: <%s>\n" @@ -258,16 +299,19 @@ char *pa_sink_input_list_to_string(pa_core *c) {              i->index,              i->name,              i->driver, -            state_table[i->state], +            i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", +            i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "", +            state_table[pa_sink_input_get_state(i)],              i->sink->index, i->sink->name,              pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), +            !!pa_sink_input_get_mute(i),              (double) pa_sink_input_get_latency(i),              pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),              pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),              pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));          if (i->module) -            pa_strbuf_printf(s, "\towner module: <%u>\n", i->module->index); +            pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index);          if (i->client)              pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);      } @@ -277,10 +321,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {  char *pa_scache_list_to_string(pa_core *c) {      pa_strbuf *s; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); @@ -326,10 +369,9 @@ char *pa_scache_list_to_string(pa_core *c) {  char *pa_autoload_list_to_string(pa_core *c) {      pa_strbuf *s; -    assert(c); +    pa_assert(c);      s = pa_strbuf_new(); -    assert(s);      pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0); diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c index ee05d7f9..3f3c9cde 100644 --- a/src/pulsecore/cli.c +++ b/src/pulsecore/cli.c @@ -27,7 +27,6 @@  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h> @@ -45,6 +44,7 @@  #include <pulsecore/cli-text.h>  #include <pulsecore/cli-command.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "cli.h" @@ -68,19 +68,17 @@ static void client_kill(pa_client *c);  pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {      char cname[256];      pa_cli *c; -    assert(io); +    pa_assert(io); -    c = pa_xmalloc(sizeof(pa_cli)); +    c = pa_xnew(pa_cli, 1);      c->core = core; -    c->line = pa_ioline_new(io); -    assert(c->line); +    pa_assert_se(c->line = pa_ioline_new(io));      c->userdata = NULL;      c->eof_callback = NULL;      pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); -    c->client = pa_client_new(core, __FILE__, cname); -    assert(c->client); +    pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));      c->client->kill = client_kill;      c->client->userdata = c;      c->client->owner = m; @@ -94,7 +92,8 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {  }  void pa_cli_free(pa_cli *c) { -    assert(c); +    pa_assert(c); +      pa_ioline_close(c->line);      pa_ioline_unref(c->line);      pa_client_free(c->client); @@ -103,8 +102,9 @@ void pa_cli_free(pa_cli *c) {  static void client_kill(pa_client *client) {      pa_cli *c; -    assert(client && client->userdata); -    c = client->userdata; + +    pa_assert(client); +    pa_assert_se(c = client->userdata);      pa_log_debug("CLI client killed.");      if (c->defer_kill) @@ -119,7 +119,9 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {      pa_strbuf *buf;      pa_cli *c = userdata;      char *p; -    assert(line && c); + +    pa_assert(line); +    pa_assert(c);      if (!s) {          pa_log_debug("CLI got EOF from user."); @@ -129,8 +131,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {          return;      } -    buf = pa_strbuf_new(); -    assert(buf); +    pa_assert_se(buf = pa_strbuf_new());      c->defer_kill++;      pa_cli_command_execute_line(c->core, s, buf, &c->fail);      c->defer_kill--; @@ -145,7 +146,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {  }  void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) { -    assert(c); +    pa_assert(c); +      c->eof_callback = cb;      c->userdata = userdata;  } diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 0d792bb4..319b8387 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -35,15 +34,16 @@  #include <pulsecore/core-subscribe.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "client.h"  pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {      pa_client *c; -    int r; -    assert(core); -    c = pa_xmalloc(sizeof(pa_client)); +    pa_core_assert_ref(core); + +    c = pa_xnew(pa_client, 1);      c->name = pa_xstrdup(name);      c->driver = pa_xstrdup(driver);      c->owner = NULL; @@ -52,10 +52,9 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {      c->kill = NULL;      c->userdata = NULL; -    r = pa_idxset_put(core->clients, c, &c->index); -    assert(c->index != PA_IDXSET_INVALID && r >= 0); +    pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); -    pa_log_info("created %u \"%s\"", c->index, c->name); +    pa_log_info("Created %u \"%s\"", c->index, c->name);      pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);      pa_core_check_quit(core); @@ -64,13 +63,14 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {  }  void pa_client_free(pa_client *c) { -    assert(c && c->core); +    pa_assert(c); +    pa_assert(c->core);      pa_idxset_remove_by_data(c->core->clients, c, NULL);      pa_core_check_quit(c->core); -    pa_log_info("freed %u \"%s\"", c->index, c->name); +    pa_log_info("Freed %u \"%s\"", c->index, c->name);      pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);      pa_xfree(c->name);      pa_xfree(c->driver); @@ -78,7 +78,8 @@ void pa_client_free(pa_client *c) {  }  void pa_client_kill(pa_client *c) { -    assert(c); +    pa_assert(c); +      if (!c->kill) {          pa_log_warn("kill() operation not implemented for client %u", c->index);          return; @@ -88,9 +89,9 @@ void pa_client_kill(pa_client *c) {  }  void pa_client_set_name(pa_client *c, const char *name) { -    assert(c); +    pa_assert(c); -    pa_log_info("client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name); +    pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);      pa_xfree(c->name);      c->name = pa_xstrdup(name); diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index 12efbd2c..0e0ba95a 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <string.h>  #include <stdio.h>  #include <errno.h> @@ -35,6 +34,7 @@  #include <pulsecore/core-error.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "conf-parser.h" @@ -43,7 +43,10 @@  /* Run the user supplied parser for an assignment */  static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { -    assert(filename && t && lvalue && rvalue); +    pa_assert(filename); +    pa_assert(t); +    pa_assert(lvalue); +    pa_assert(rvalue);      for (; t->parse; t++)          if (!strcmp(lvalue, t->lvalue)) @@ -56,7 +59,7 @@ static int next_assignment(const char *filename, unsigned line, const pa_config_  /* Returns non-zero when c is contained in s */  static int in_string(char c, const char *s) { -    assert(s); +    pa_assert(s);      for (; *s; s++)          if (*s == c) @@ -107,7 +110,9 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void      int r = -1;      unsigned line = 0;      int do_close = !f; -    assert(filename && t); + +    pa_assert(filename); +    pa_assert(t);      if (!f && !(f = fopen(filename, "r"))) {          if (errno == ENOENT) { @@ -115,7 +120,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void              goto finish;          } -        pa_log_warn("WARNING: failed to open configuration file '%s': %s", +        pa_log_warn("Failed to open configuration file '%s': %s",              filename, pa_cstrerror(errno));          goto finish;      } @@ -126,7 +131,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void              if (feof(f))                  break; -            pa_log_warn("WARNING: failed to read configuration file '%s': %s", +            pa_log_warn("Failed to read configuration file '%s': %s",                  filename, pa_cstrerror(errno));              goto finish;          } @@ -148,7 +153,11 @@ finish:  int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {      int *i = data;      int32_t k; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      if (pa_atoi(rvalue, &k) < 0) {          pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); @@ -161,7 +170,11 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,  int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {      int *b = data, k; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      if ((k = pa_parse_boolean(rvalue)) < 0) {          pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); @@ -175,7 +188,11 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue  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) {      char **s = data; -    assert(filename && lvalue && rvalue && data); + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data);      pa_xfree(*s);      *s = *rvalue ? pa_xstrdup(rvalue) : NULL; diff --git a/src/pulsecore/core-def.h b/src/pulsecore/core-def.h index 10a3be42..4bc05137 100644 --- a/src/pulsecore/core-def.h +++ b/src/pulsecore/core-def.h @@ -24,9 +24,6 @@    USA.  ***/ -typedef enum pa_mixer { -    PA_MIXER_SOFTWARE, -    PA_MIXER_HARDWARE -} pa_mixer_t; +/* FIXME: Remove this shit */  #endif diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c index 044bea12..8a61e726 100644 --- a/src/pulsecore/core-error.c +++ b/src/pulsecore/core-error.c @@ -31,178 +31,50 @@  #include <stdlib.h>  #include <string.h> -#ifdef HAVE_PTHREAD -#include <pthread.h> -#endif - -#ifdef HAVE_WINDOWS_H -#include <windows.h> -#endif -  #include <pulse/utf8.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-util.h>  #include <pulsecore/native-common.h> +#include <pulsecore/thread.h> +#include <pulsecore/macro.h> +#include <pulsecore/log.h>  #include "core-error.h" -#ifdef HAVE_PTHREAD - -static pthread_once_t cstrerror_once = PTHREAD_ONCE_INIT; -static pthread_key_t tlsstr_key; - -static void inittls(void) { -    int ret; - -    ret = pthread_key_create(&tlsstr_key, pa_xfree); -    if (ret) { -        fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", errno); -        exit(-1); -    } -} - -#elif HAVE_WINDOWS_H - -static DWORD tlsstr_key = TLS_OUT_OF_INDEXES; -static DWORD monitor_key = TLS_OUT_OF_INDEXES; - -static void inittls(void) { -    HANDLE mutex; -    char name[64]; - -    sprintf(name, "pulse%d", (int)GetCurrentProcessId()); - -    mutex = CreateMutex(NULL, FALSE, name); -    if (!mutex) { -        fprintf(stderr, __FILE__ ": CRITICAL: Unable to create named mutex (%d)\n", (int)GetLastError()); -        exit(-1); -    } - -    WaitForSingleObject(mutex, INFINITE); - -    if (tlsstr_key == TLS_OUT_OF_INDEXES) { -        tlsstr_key = TlsAlloc(); -        monitor_key = TlsAlloc(); -        if ((tlsstr_key == TLS_OUT_OF_INDEXES) || (monitor_key == TLS_OUT_OF_INDEXES)) { -            fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", (int)GetLastError()); -            exit(-1); -        } -    } - -    ReleaseMutex(mutex); - -    CloseHandle(mutex); -} - -/* - * This is incredibly brain dead, but this is necessary when dealing with - * the hell that is Win32. - */ -struct monitor_data { -    HANDLE thread; -    void *data; -}; - -static DWORD WINAPI monitor_thread(LPVOID param) { -    struct monitor_data *data; - -    data = (struct monitor_data*)param; -    assert(data); - -    WaitForSingleObject(data->thread, INFINITE); - -    CloseHandle(data->thread); -    pa_xfree(data->data); -    pa_xfree(data); - -    return 0; -} - -static void start_monitor(void) { -    HANDLE thread; -    struct monitor_data *data; - -    data = pa_xnew(struct monitor_data, 1); -    assert(data); - -    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), -        GetCurrentProcess(), &data->thread, 0, FALSE, DUPLICATE_SAME_ACCESS); - -    thread = CreateThread(NULL, 0, monitor_thread, data, 0, NULL); -    assert(thread); - -    TlsSetValue(monitor_key, data); - -    CloseHandle(thread); -} - -#else - -/* Unsafe, but we have no choice */ -static char *tlsstr; - -#endif +PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);  const char* pa_cstrerror(int errnum) { -    const char *origbuf; - -#ifdef HAVE_STRERROR_R +    const char *original = NULL; +    char *translated, *t;      char errbuf[128]; -#endif -#ifdef HAVE_PTHREAD -    char *tlsstr; +    if ((t = PA_STATIC_TLS_GET(cstrerror))) +        pa_xfree(t); -    pthread_once(&cstrerror_once, inittls); - -    tlsstr = pthread_getspecific(tlsstr_key); -#elif defined(HAVE_WINDOWS_H) -    char *tlsstr; -    struct monitor_data *data; - -    inittls(); - -    tlsstr = TlsGetValue(tlsstr_key); -    if (!tlsstr) -        start_monitor(); -    data = TlsGetValue(monitor_key); -#endif - -    if (tlsstr) -        pa_xfree(tlsstr); - -#ifdef HAVE_STRERROR_R - -#ifdef __GLIBC__ -    origbuf = strerror_r(errnum, errbuf, sizeof(errbuf)); -    if (origbuf == NULL) -        origbuf = ""; -#else +#if defined(HAVE_STRERROR_R) && defined(__GLIBC__) +    original = strerror_r(errnum, errbuf, sizeof(errbuf)); +#elif defined(HAVE_STRERROR_R)      if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) { -        origbuf = errbuf; -        errbuf[sizeof(errbuf) - 1] = '\0'; -    } else -        origbuf = ""; -#endif - +        errbuf[sizeof(errbuf) - 1] = 0; +        original = errbuf; +    }  #else      /* This might not be thread safe, but we hope for the best */ -    origbuf = strerror(errnum); +    original = strerror(errnum);  #endif -    tlsstr = pa_locale_to_utf8(origbuf); -    if (!tlsstr) { -        fprintf(stderr, "Unable to convert, filtering\n"); -        tlsstr = pa_utf8_filter(origbuf); +    if (!original) { +        pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum); +        original = errbuf;      } -#ifdef HAVE_PTHREAD -    pthread_setspecific(tlsstr_key, tlsstr); -#elif defined(HAVE_WINDOWS_H) -    TlsSetValue(tlsstr_key, tlsstr); -    data->data = tlsstr; -#endif +    if (!(translated = pa_locale_to_utf8(original))) { +        pa_log_warn("Unable to convert error string to locale, filtering."); +        translated = pa_utf8_filter(original); +    } + +    PA_STATIC_TLS_SET(cstrerror, translated); -    return tlsstr; +    return translated;  } diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index cb272784..732d90dd 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <stdio.h> @@ -60,6 +59,7 @@  #include <pulsecore/core-util.h>  #include <pulsecore/log.h>  #include <pulsecore/core-error.h> +#include <pulsecore/macro.h>  #include "core-scache.h" @@ -68,7 +68,10 @@  static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {      pa_core *c = userdata;      struct timeval ntv; -    assert(c && c->mainloop == m && c->scache_auto_unload_event == e); + +    pa_assert(c); +    pa_assert(c->mainloop == m); +    pa_assert(c->scache_auto_unload_event == e);      pa_scache_unload_unused(c); @@ -78,7 +81,8 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED  }  static void free_entry(pa_scache_entry *e) { -    assert(e); +    pa_assert(e); +      pa_namereg_unregister(e->core, e->name);      pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);      pa_xfree(e->name); @@ -90,7 +94,9 @@ static void free_entry(pa_scache_entry *e) {  static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {      pa_scache_entry *e; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {          if (e->memchunk.memblock) @@ -98,11 +104,11 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {          pa_xfree(e->filename); -        assert(e->core == c); +        pa_assert(e->core == c);          pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);      } else { -        e = pa_xmalloc(sizeof(pa_scache_entry)); +        e = pa_xnew(pa_scache_entry, 1);          if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {              pa_xfree(e); @@ -114,7 +120,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {          if (!c->scache) {              c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -            assert(c->scache); +            pa_assert(c->scache);          }          pa_idxset_put(c->scache, e, &e->index); @@ -139,7 +145,9 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {  int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {      pa_scache_entry *e;      char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)          return -1; @@ -164,9 +172,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c      if (idx)          *idx = e->index; -    pa_log_debug("created sample \"%s\" (#%d), %d bytes with sample spec %s", -        name, e->index, e->memchunk.length, -        pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec)); +    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s", +                 name, e->index, (unsigned long) e->memchunk.length, +                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));      return 0;  } @@ -184,6 +192,10 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3          filename = buf;  #endif +    pa_assert(c); +    pa_assert(name); +    pa_assert(filename); +      if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)          return -1; @@ -203,7 +215,9 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,          filename = buf;  #endif -    assert(c && name); +    pa_assert(c); +    pa_assert(name); +    pa_assert(filename);      if (!(e = scache_add_item(c, name)))          return -1; @@ -226,15 +240,17 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,  int pa_scache_remove_item(pa_core *c, const char *name) {      pa_scache_entry *e; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))          return -1;      if (pa_idxset_remove_by_data(c->scache, e, NULL) != e) -        assert(0); +        pa_assert(0); -    pa_log_debug("removed sample \"%s\"", name); +    pa_log_debug("Removed sample \"%s\"", name);      free_entry(e); @@ -243,12 +259,13 @@ int pa_scache_remove_item(pa_core *c, const char *name) {  static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {      pa_scache_entry *e = p; -    assert(e); +    pa_assert(e); +      free_entry(e);  }  void pa_scache_free(pa_core *c) { -    assert(c); +    pa_assert(c);      if (c->scache) {          pa_idxset_free(c->scache, free_cb, NULL); @@ -264,9 +281,9 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t      char *t;      pa_cvolume r; -    assert(c); -    assert(name); -    assert(sink); +    pa_assert(c); +    pa_assert(name); +    pa_assert(sink);      if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))          return -1; @@ -284,7 +301,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t      if (!e->memchunk.memblock)          return -1; -    pa_log_debug("playing sample \"%s\" on \"%s\"", name, sink->name); +    pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);      t = pa_sprintf_malloc("sample:%s", name); @@ -304,9 +321,23 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t      return 0;  } +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload) { +    pa_sink *sink; + +    pa_assert(c); +    pa_assert(name); + +    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload))) +        return -1; + +    return pa_scache_play_item(c, name, sink, volume); +} +  const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {      pa_scache_entry *e; -    assert(c && id != PA_IDXSET_INVALID); + +    pa_assert(c); +    pa_assert(id != PA_IDXSET_INVALID);      if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))          return NULL; @@ -316,7 +347,9 @@ const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {  uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {      pa_scache_entry *e; -    assert(c && name); + +    pa_assert(c); +    pa_assert(name);      if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))          return PA_IDXSET_INVALID; @@ -327,7 +360,8 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {  uint32_t pa_scache_total_size(pa_core *c) {      pa_scache_entry *e;      uint32_t idx, sum = 0; -    assert(c); + +    pa_assert(c);      if (!c->scache || !pa_idxset_size(c->scache))          return 0; @@ -343,7 +377,8 @@ void pa_scache_unload_unused(pa_core *c) {      pa_scache_entry *e;      time_t now;      uint32_t idx; -    assert(c); + +    pa_assert(c);      if (!c->scache || !pa_idxset_size(c->scache))          return; @@ -370,6 +405,9 @@ static void add_file(pa_core *c, const char *pathname) {      struct stat st;      const char *e; +    pa_core_assert_ref(c); +    pa_assert(pathname); +      e = pa_path_get_filename(pathname);      if (stat(pathname, &st) < 0) { @@ -385,7 +423,9 @@ static void add_file(pa_core *c, const char *pathname) {  int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {      DIR *dir; -    assert(c && pathname); + +    pa_core_assert_ref(c); +    pa_assert(pathname);      /* First try to open this as directory */      if (!(dir = opendir(pathname))) { @@ -415,7 +455,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {              if (e->d_name[0] == '.')                  continue; -            snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); +            pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);              add_file(c, p);          }      } diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h index bbf13f15..ab7ec0ef 100644 --- a/src/pulsecore/core-scache.h +++ b/src/pulsecore/core-scache.h @@ -55,6 +55,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);  int pa_scache_remove_item(pa_core *c, const char *name);  int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume); +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload);  void pa_scache_free(pa_core *c);  const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c index 6608d57a..06c5a4ad 100644 --- a/src/pulsecore/core-subscribe.c +++ b/src/pulsecore/core-subscribe.c @@ -26,12 +26,12 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <pulse/xmalloc.h>  #include <pulsecore/queue.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "core-subscribe.h" @@ -68,9 +68,9 @@ static void sched_event(pa_core *c);  pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {      pa_subscription *s; -    assert(c); -    assert(m); -    assert(callback); +    pa_assert(c); +    pa_assert(m); +    pa_assert(callback);      s = pa_xnew(pa_subscription, 1);      s->core = c; @@ -85,24 +85,24 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su  /* Free a subscription object, effectively marking it for deletion */  void pa_subscription_free(pa_subscription*s) { -    assert(s); -    assert(!s->dead); +    pa_assert(s); +    pa_assert(!s->dead);      s->dead = 1;      sched_event(s->core);  }  static void free_subscription(pa_subscription *s) { -    assert(s); -    assert(s->core); +    pa_assert(s); +    pa_assert(s->core);      PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);      pa_xfree(s);  }  static void free_event(pa_subscription_event *s) { -    assert(s); -    assert(s->core); +    pa_assert(s); +    pa_assert(s->core);      if (!s->next)          s->core->subscription_event_last = s->prev; @@ -113,7 +113,7 @@ static void free_event(pa_subscription_event *s) {  /* Free all subscription objects */  void pa_subscription_free_all(pa_core *c) { -    assert(c); +    pa_assert(c);      while (c->subscriptions)          free_subscription(c->subscriptions); @@ -160,9 +160,9 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {      pa_core *c = userdata;      pa_subscription *s; -    assert(c->mainloop == m); -    assert(c); -    assert(c->subscription_defer_event == de); +    pa_assert(c->mainloop == m); +    pa_assert(c); +    pa_assert(c->subscription_defer_event == de);      c->mainloop->defer_enable(c->subscription_defer_event, 0); @@ -196,20 +196,20 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {  /* Schedule an mainloop event so that a pending subscription event is dispatched */  static void sched_event(pa_core *c) { -    assert(c); +    pa_assert(c);      if (!c->subscription_defer_event) {          c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c); -        assert(c->subscription_defer_event); +        pa_assert(c->subscription_defer_event);      }      c->mainloop->defer_enable(c->subscription_defer_event, 1);  }  /* Append a new subscription event to the subscription event queue and schedule a main loop event */ -void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) { +void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {      pa_subscription_event *e; -    assert(c); +    pa_assert(c);      /* No need for queuing subscriptions of noone is listening */      if (!c->subscriptions) @@ -227,7 +227,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i                  continue;              /* not the same object */ -            if (i->index != index) +            if (i->index != idx)                  continue;              if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -253,7 +253,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i      e = pa_xnew(pa_subscription_event, 1);      e->core = c;      e->type = t; -    e->index = index; +    e->index = idx;      PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);      c->subscription_event_last = e; diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index e5766b2f..a644b664 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -31,7 +31,6 @@  #include <stdlib.h>  #include <signal.h>  #include <errno.h> -#include <assert.h>  #include <string.h>  #include <stdio.h>  #include <fcntl.h> @@ -43,6 +42,10 @@  #include <sys/stat.h>  #include <sys/time.h> +#ifdef HAVE_STRTOF_L +#include <locale.h> +#endif +  #ifdef HAVE_SCHED_H  #include <sched.h>  #endif @@ -55,6 +58,10 @@  #include <sys/capability.h>  #endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +  #ifdef HAVE_PTHREAD  #include <pthread.h>  #endif @@ -75,14 +82,19 @@  #include <grp.h>  #endif +#ifdef HAVE_LIBSAMPLERATE  #include <samplerate.h> +#endif  #include <pulse/xmalloc.h>  #include <pulse/util.h> +#include <pulse/utf8.h>  #include <pulsecore/core-error.h>  #include <pulsecore/winsock.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread.h>  #include "core-util.h" @@ -93,10 +105,8 @@  #ifndef OS_IS_WIN32  #define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-" -#define PATH_SEP '/'  #else  #define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-" -#define PATH_SEP '\\'  #endif  #ifdef OS_IS_WIN32 @@ -111,7 +121,7 @@ int pa_set_root(HANDLE handle) {      if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))          return 0; -    sep = strrchr(library_path, '\\'); +    sep = strrchr(library_path, PA_PATH_SEP_CHAR);      if (sep)          *sep = '\0'; @@ -124,23 +134,42 @@ int pa_set_root(HANDLE handle) {  #endif  /** Make a file descriptor nonblock. Doesn't do any error checking */ -void pa_make_nonblock_fd(int fd) { +void pa_make_fd_nonblock(int fd) { +  #ifdef O_NONBLOCK      int v; -    assert(fd >= 0); +    pa_assert(fd >= 0); + +    pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0); + +    if (!(v & O_NONBLOCK)) +        pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0); -    if ((v = fcntl(fd, F_GETFL)) >= 0) -        if (!(v & O_NONBLOCK)) -            fcntl(fd, F_SETFL, v|O_NONBLOCK);  #elif defined(OS_IS_WIN32)      u_long arg = 1;      if (ioctlsocket(fd, FIONBIO, &arg) < 0) { -        if (WSAGetLastError() == WSAENOTSOCK) -            pa_log_warn("WARNING: Only sockets can be made non-blocking!"); +        pa_assert_se(WSAGetLastError() == WSAENOTSOCK); +        pa_log_warn("Only sockets can be made non-blocking!");      }  #else -    pa_log_warn("WARNING: Non-blocking I/O not supported.!"); +    pa_log_warn("Non-blocking I/O not supported.!");  #endif + +} + +/* Set the FD_CLOEXEC flag for a fd */ +void pa_make_fd_cloexec(int fd) { + +#ifdef FD_CLOEXEC +    int v; +    pa_assert(fd >= 0); + +    pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0); + +    if (!(v & FD_CLOEXEC)) +        pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0); +#endif +  }  /** Creates a directory securely */ @@ -148,7 +177,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {      struct stat st;      int r; -    assert(dir); +    pa_assert(dir);  #ifdef OS_IS_WIN32      r = mkdir(dir); @@ -169,7 +198,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {          uid = getuid();      if (gid == (gid_t)-1)          gid = getgid(); -    chown(dir, uid, gid); +    (void) chown(dir, uid, gid);  #endif  #ifdef HAVE_CHMOD @@ -295,9 +324,9 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {      ssize_t ret = 0;      int _type; -    assert(fd >= 0); -    assert(data); -    assert(size); +    pa_assert(fd >= 0); +    pa_assert(data); +    pa_assert(size);      if (!type) {          _type = 0; @@ -326,9 +355,9 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {      ssize_t ret = 0;      int _type; -    assert(fd >= 0); -    assert(data); -    assert(size); +    pa_assert(fd >= 0); +    pa_assert(data); +    pa_assert(size);      if (!type) {          _type = 0; @@ -354,13 +383,12 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {  /** Platform independent read function. Necessary since not all   * systems treat all file descriptors equal. */ -int pa_close(int fd) -{ +int pa_close(int fd) { +  #ifdef OS_IS_WIN32      int ret; -    ret = closesocket(fd); -    if (ret == 0) +    if ((ret = closesocket(fd)) == 0)          return 0;      if (WSAGetLastError() != WSAENOTSOCK) { @@ -407,9 +435,9 @@ void pa_check_signal_is_blocked(int sig) {      if (sa.sa_handler != SIG_DFL)          return; -    pa_log("WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig)); +    pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));  #else /* HAVE_SIGACTION */ -    pa_log("WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig)); +    pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));  #endif  } @@ -419,7 +447,7 @@ char *pa_sprintf_malloc(const char *format, ...) {      int  size = 100;      char *c = NULL; -    assert(format); +    pa_assert(format);      for(;;) {          int r; @@ -431,6 +459,8 @@ char *pa_sprintf_malloc(const char *format, ...) {          r = vsnprintf(c, size, format, ap);          va_end(ap); +        c[size-1] = 0; +          if (r > -1 && r < size)              return c; @@ -447,19 +477,20 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {      int  size = 100;      char *c = NULL; -    assert(format); +    pa_assert(format);      for(;;) {          int r;          va_list aq; -        va_copy(aq, ap); -          c = pa_xrealloc(c, size); -        r = vsnprintf(c, size, format, aq); +        va_copy(aq, ap); +        r = vsnprintf(c, size, format, aq);          va_end(aq); +        c[size-1] = 0; +          if (r > -1 && r < size)              return c; @@ -472,36 +503,47 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {  /* Similar to OpenBSD's strlcpy() function */  char *pa_strlcpy(char *b, const char *s, size_t l) { -    assert(b && s && l > 0); +    pa_assert(b); +    pa_assert(s); +    pa_assert(l > 0);      strncpy(b, s, l);      b[l-1] = 0;      return b;  } -#define NICE_LEVEL (-15) +/* Make the current thread a realtime thread*/ +void pa_make_realtime(void) { -/* Raise the priority of the current process as much as possible and -sensible: set the nice level to -15 and enable realtime scheduling if -supported.*/ -void pa_raise_priority(void) { -#if defined(HAVE_SYS_CAPABILITY_H) -    cap_t caps; - -    /* Temporarily acquire CAP_SYS_NICE in the effective set */ -    if ((caps = cap_get_proc())) { -        cap_t caps_new; -        cap_value_t nice_cap = CAP_SYS_NICE; - -        if ((caps_new = cap_dup(caps))) { -            cap_set_flag(caps_new, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET); -            cap_set_flag(caps_new, CAP_PERMITTED, 1, &nice_cap, CAP_SET); -            cap_set_proc(caps_new); -            cap_free(caps_new); -        } +#ifdef _POSIX_PRIORITY_SCHEDULING +    struct sched_param sp; +    int r, policy; + +    memset(&sp, 0, sizeof(sp)); +    policy = 0; + +    if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) { +        pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r)); +        return; +    } + +    sp.sched_priority = 1; +    if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) { +        pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r)); +        return;      } + +    pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread.");  #endif +} + +#define NICE_LEVEL (-11) + +/* Raise the priority of the current process as much as possible and +sensible: set the nice level to -15.*/ +void pa_raise_priority(void) { +  #ifdef HAVE_SYS_RESOURCE_H      if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)          pa_log_warn("setpriority(): %s", pa_cstrerror(errno)); @@ -509,86 +551,26 @@ void pa_raise_priority(void) {          pa_log_info("Successfully gained nice level %i.", NICE_LEVEL);  #endif -#ifdef _POSIX_PRIORITY_SCHEDULING -    { -        struct sched_param sp; - -        if (sched_getparam(0, &sp) < 0) { -            pa_log("sched_getparam(): %s", pa_cstrerror(errno)); -            goto fail; -        } - -        sp.sched_priority = 1; -        if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { -            pa_log_warn("sched_setscheduler(): %s", pa_cstrerror(errno)); -            goto fail; -        } - -        pa_log_info("Successfully enabled SCHED_FIFO scheduling."); -    } -#endif -  #ifdef OS_IS_WIN32      if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))          pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());      else          pa_log_info("Successfully gained high priority class.");  #endif - -fail: - -#if defined(HAVE_SYS_CAPABILITY_H) -    if (caps) { -        /* Restore original caps */ -        cap_set_proc(caps); -        cap_free(caps); -    } -#endif - -    ; /* We put this here to get the code to compile when -       * HAVE_SYS_CAPABILITY_H is not defined. Don't remove unless you -       * know what you do */  } -/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ +/* Reset the priority to normal, inverting the changes made by + * pa_raise_priority() */  void pa_reset_priority(void) {  #ifdef OS_IS_WIN32      SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);  #endif -#ifdef _POSIX_PRIORITY_SCHEDULING -    { -        struct sched_param sp; -        sched_getparam(0, &sp); -        sp.sched_priority = 0; -        sched_setscheduler(0, SCHED_OTHER, &sp); -    } -#endif -  #ifdef HAVE_SYS_RESOURCE_H      setpriority(PRIO_PROCESS, 0, 0);  #endif  } -/* Set the FD_CLOEXEC flag for a fd */ -int pa_fd_set_cloexec(int fd, int b) { - -#ifdef FD_CLOEXEC -    int v; -    assert(fd >= 0); - -    if ((v = fcntl(fd, F_GETFD, 0)) < 0) -        return -1; - -    v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0); - -    if (fcntl(fd, F_SETFD, v) < 0) -        return -1; -#endif - -    return 0; -} -  /* Try to parse a boolean string value.*/  int pa_parse_boolean(const char *v) { @@ -639,31 +621,134 @@ char *pa_split_spaces(const char *c, const char **state) {      return pa_xstrndup(current, l);  } -/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ -const char *pa_strsignal(int sig) { +PA_STATIC_TLS_DECLARE(signame, pa_xfree); + +/* Return the name of an UNIX signal. Similar to Solaris sig2str() */ +const char *pa_sig2str(int sig) { +    char *t; + +    if (sig <= 0) +        goto fail; + +#ifdef NSIG +    if (sig >= NSIG) +        goto fail; +#endif + +#ifdef HAVE_SIG2STR +    { +        char buf[SIG2STR_MAX]; + +        if (sig2str(sig, buf) == 0) { +            pa_xfree(PA_STATIC_TLS_GET(signame)); +            t = pa_sprintf_malloc("SIG%s", buf); +            PA_STATIC_TLS_SET(signame, t); +            return t; +        } +    } +#else +      switch(sig) { -        case SIGINT: return "SIGINT"; -        case SIGTERM: return "SIGTERM"; +#ifdef SIGHUP +        case SIGHUP:    return "SIGHUP"; +#endif +        case SIGINT:    return "SIGINT"; +#ifdef SIGQUIT +        case SIGQUIT:   return "SIGQUIT"; +#endif +        case SIGILL:    return "SIGULL"; +#ifdef SIGTRAP +        case SIGTRAP:   return "SIGTRAP"; +#endif +        case SIGABRT:   return "SIGABRT"; +#ifdef SIGBUS +        case SIGBUS:    return "SIGBUS"; +#endif +        case SIGFPE:    return "SIGFPE"; +#ifdef SIGKILL +        case SIGKILL:   return "SIGKILL"; +#endif  #ifdef SIGUSR1 -        case SIGUSR1: return "SIGUSR1"; +        case SIGUSR1:   return "SIGUSR1";  #endif +        case SIGSEGV:   return "SIGSEGV";  #ifdef SIGUSR2 -        case SIGUSR2: return "SIGUSR2"; -#endif -#ifdef SIGXCPU -        case SIGXCPU: return "SIGXCPU"; +        case SIGUSR2:   return "SIGUSR2";  #endif  #ifdef SIGPIPE -        case SIGPIPE: return "SIGPIPE"; +        case SIGPIPE:   return "SIGPIPE"; +#endif +#ifdef SIGALRM +        case SIGALRM:   return "SIGALRM"; +#endif +        case SIGTERM:   return "SIGTERM"; +#ifdef SIGSTKFLT +        case SIGSTKFLT: return "SIGSTKFLT";  #endif  #ifdef SIGCHLD -        case SIGCHLD: return "SIGCHLD"; +        case SIGCHLD:   return "SIGCHLD";  #endif -#ifdef SIGHUP -        case SIGHUP: return "SIGHUP"; +#ifdef SIGCONT +        case SIGCONT:   return "SIGCONT"; +#endif +#ifdef SIGSTOP +        case SIGSTOP:   return "SIGSTOP"; +#endif +#ifdef SIGTSTP +        case SIGTSTP:   return "SIGTSTP"; +#endif +#ifdef SIGTTIN +        case SIGTTIN:   return "SIGTTIN"; +#endif +#ifdef SIGTTOU +        case SIGTTOU:   return "SIGTTOU"; +#endif +#ifdef SIGURG +        case SIGURG:    return "SIGURG"; +#endif +#ifdef SIGXCPU +        case SIGXCPU:   return "SIGXCPU"; +#endif +#ifdef SIGXFSZ +        case SIGXFSZ:   return "SIGXFSZ";  #endif -        default: return "UNKNOWN SIGNAL"; +#ifdef SIGVTALRM +        case SIGVTALRM: return "SIGVTALRM"; +#endif +#ifdef SIGPROF +        case SIGPROF:   return "SIGPROF"; +#endif +#ifdef SIGWINCH +        case SIGWINCH:  return "SIGWINCH"; +#endif +#ifdef SIGIO +        case SIGIO:     return "SIGIO"; +#endif +#ifdef SIGPWR +        case SIGPWR:    return "SIGPWR"; +#endif +#ifdef SIGSYS +        case SIGSYS:    return "SIGSYS"; +#endif +    } + +#ifdef SIGRTMIN +    if (sig >= SIGRTMIN && sig <= SIGRTMAX) { +        pa_xfree(PA_STATIC_TLS_GET(signame)); +        t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN); +        PA_STATIC_TLS_SET(signame, t); +        return t;      } +#endif + +#endif + +fail: + +    pa_xfree(PA_STATIC_TLS_GET(signame)); +    t = pa_sprintf_malloc("SIG%i", sig); +    PA_STATIC_TLS_SET(signame, t); +    return t;  }  #ifdef HAVE_GRP_H @@ -715,7 +800,7 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) {      int n = sysconf(_SC_NGROUPS_MAX);      int r = -1, i; -    assert(n > 0); +    pa_assert(n > 0);      gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); @@ -861,8 +946,7 @@ int pa_lock_fd(int fd, int b) {              return 0;      } -    pa_log("%slock: %s", !b? "un" : "", -        pa_cstrerror(errno)); +    pa_log("%slock: %s", !b? "un" : "", pa_cstrerror(errno));  #endif  #ifdef OS_IS_WIN32 @@ -881,7 +965,7 @@ int pa_lock_fd(int fd, int b) {  /* Remove trailing newlines from a string */  char* pa_strip_nl(char *s) { -    assert(s); +    pa_assert(s);      s[strcspn(s, "\r\n")] = 0;      return s; @@ -890,38 +974,46 @@ char* pa_strip_nl(char *s) {  /* Create a temporary lock file and lock it. */  int pa_lock_lockfile(const char *fn) {      int fd = -1; -    assert(fn); +    pa_assert(fn);      for (;;) {          struct stat st; -        if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { -            pa_log("failed to create lock file '%s': %s", fn, -                pa_cstrerror(errno)); +        if ((fd = open(fn, O_CREAT|O_RDWR +#ifdef O_NOCTTY +                       |O_NOCTTY +#endif +#ifdef O_NOFOLLOW +                       |O_NOFOLLOW +#endif +                       , S_IRUSR|S_IWUSR)) < 0) { +            pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno));              goto fail;          }          if (pa_lock_fd(fd, 1) < 0) { -            pa_log("failed to lock file '%s'.", fn); +            pa_log_warn("Failed to lock file '%s'.", fn);              goto fail;          }          if (fstat(fd, &st) < 0) { -            pa_log("failed to fstat() file '%s'.", fn); +            pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));              goto fail;          } -        /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */ +        /* Check wheter the file has been removed meanwhile. When yes, +         * restart this loop, otherwise, we're done */          if (st.st_nlink >= 1)              break;          if (pa_lock_fd(fd, 0) < 0) { -            pa_log("failed to unlock file '%s'.", fn); +            pa_log_warn("Failed to unlock file '%s'.", fn);              goto fail;          } -        if (close(fd) < 0) { -            pa_log("failed to close file '%s'.", fn); +        if (pa_close(fd) < 0) { +            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno)); +            fd = -1;              goto fail;          } @@ -933,7 +1025,7 @@ int pa_lock_lockfile(const char *fn) {  fail:      if (fd >= 0) -        close(fd); +        pa_close(fd);      return -1;  } @@ -941,22 +1033,21 @@ fail:  /* Unlock a temporary lcok file */  int pa_unlock_lockfile(const char *fn, int fd) {      int r = 0; -    assert(fn && fd >= 0); +    pa_assert(fn); +    pa_assert(fd >= 0);      if (unlink(fn) < 0) { -        pa_log_warn("WARNING: unable to remove lock file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));          r = -1;      }      if (pa_lock_fd(fd, 0) < 0) { -        pa_log_warn("WARNING: failed to unlock file '%s'.", fn); +        pa_log_warn("Failed to unlock file '%s'.", fn);          r = -1;      } -    if (close(fd) < 0) { -        pa_log_warn("WARNING: failed to close lock file '%s': %s", -            fn, pa_cstrerror(errno)); +    if (pa_close(fd) < 0) { +        pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));          r = -1;      } @@ -1019,10 +1110,8 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env                  return f;              } -            if (errno != ENOENT) { -                pa_log_warn("WARNING: failed to open configuration file '%s': %s", -                    lfn, pa_cstrerror(errno)); -            } +            if (errno != ENOENT) +                pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));              pa_xfree(lfn);          } @@ -1051,7 +1140,10 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env  char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {      size_t i = 0, j = 0;      const char hex[] = "0123456789abcdef"; -    assert(d && s && slength > 0); + +    pa_assert(d); +    pa_assert(s); +    pa_assert(slength > 0);      while (i < dlength && j+3 <= slength) {          s[j++] = hex[*d >> 4]; @@ -1082,7 +1174,9 @@ static int hexc(char c) {  /* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */  size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {      size_t j = 0; -    assert(p && d); + +    pa_assert(p); +    pa_assert(d);      while (j < dlength && *p) {          int b; @@ -1109,8 +1203,8 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {  int pa_startswith(const char *s, const char *pfx) {      size_t l; -    assert(s); -    assert(pfx); +    pa_assert(s); +    pa_assert(pfx);      l = strlen(pfx); @@ -1121,8 +1215,8 @@ int pa_startswith(const char *s, const char *pfx) {  int pa_endswith(const char *s, const char *sfx) {      size_t l1, l2; -    assert(s); -    assert(sfx); +    pa_assert(s); +    pa_assert(sfx);      l1 = strlen(s);      l2 = strlen(sfx); @@ -1146,17 +1240,17 @@ char *pa_runtime_path(const char *fn, char *s, size_t l) {      if ((e = getenv("PULSE_RUNTIME_PATH"))) {          if (fn) -            snprintf(s, l, "%s%c%s", e, PATH_SEP, fn); +            pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);          else -            snprintf(s, l, "%s", e); +            pa_snprintf(s, l, "%s", e);      } else {          char u[256];          if (fn) -            snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn); +            pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);          else -            snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); +            pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));      } @@ -1175,11 +1269,17 @@ char *pa_runtime_path(const char *fn, char *s, size_t l) {  int pa_atoi(const char *s, int32_t *ret_i) {      char *x = NULL;      long l; -    assert(s && ret_i); +    pa_assert(s); +    pa_assert(ret_i); + +    errno = 0;      l = strtol(s, &x, 0); -    if (!x || *x) +    if (!x || *x || errno != 0) +        return -1; + +    if ((int32_t) l != l)          return -1;      *ret_i = (int32_t) l; @@ -1191,14 +1291,219 @@ int pa_atoi(const char *s, int32_t *ret_i) {  int pa_atou(const char *s, uint32_t *ret_u) {      char *x = NULL;      unsigned long l; -    assert(s && ret_u); +    pa_assert(s); +    pa_assert(ret_u); + +    errno = 0;      l = strtoul(s, &x, 0); -    if (!x || *x) +    if (!x || *x || errno != 0) +        return -1; + +    if ((uint32_t) l != l)          return -1;      *ret_u = (uint32_t) l;      return 0;  } + +#ifdef HAVE_STRTOF_L +static locale_t c_locale = NULL; + +static void c_locale_destroy(void) { +    freelocale(c_locale); +} +#endif + +int pa_atof(const char *s, float *ret_f) { +    char *x = NULL; +    float f; +    int r = 0; + +    pa_assert(s); +    pa_assert(ret_f); + +    /* This should be locale independent */ + +#ifdef HAVE_STRTOF_L + +    PA_ONCE_BEGIN { + +        if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL))) +            atexit(c_locale_destroy); + +    } PA_ONCE_END; + +    if (c_locale) { +        errno = 0; +        f = strtof_l(s, &x, c_locale); +    } else +#endif +    { +        errno = 0; +#ifdef HAVE_STRTOF +        f = strtof(s, &x); +#else +        f = strtod(s, &x); +#endif +    } + +    if (!x || *x || errno != 0) +        r =  -1; +    else +        *ret_f = f; + +    return r; +} + +/* Same as snprintf, but guarantees NUL-termination on every platform */ +int pa_snprintf(char *str, size_t size, const char *format, ...) { +    int ret; +    va_list ap; + +    pa_assert(str); +    pa_assert(size > 0); +    pa_assert(format); + +    va_start(ap, format); +    ret = vsnprintf(str, size, format, ap); +    va_end(ap); + +    str[size-1] = 0; + +    return ret; +} + +/* Truncate the specified string, but guarantee that the string + * returned still validates as UTF8 */ +char *pa_truncate_utf8(char *c, size_t l) { +    pa_assert(c); +    pa_assert(pa_utf8_valid(c)); + +    if (strlen(c) <= l) +        return c; + +    c[l] = 0; + +    while (l > 0 && !pa_utf8_valid(c)) +        c[--l] = 0; + +    return c; +} + +char *pa_getcwd(void) { +    size_t l = 128; + +    for (;;) { +        char *p = pa_xnew(char, l); +        if (getcwd(p, l)) +            return p; + +        if (errno != ERANGE) +            return NULL; + +        pa_xfree(p); +        l *= 2; +    } +} + +char *pa_make_path_absolute(const char *p) { +    char *r; +    char *cwd; + +    pa_assert(p); + +    if (p[0] == '/') +        return pa_xstrdup(p); + +    if (!(cwd = pa_getcwd())) +        return pa_xstrdup(p); + +    r = pa_sprintf_malloc("%s/%s", cwd, p); +    pa_xfree(cwd); +    return r; +} + +void *pa_will_need(const void *p, size_t l) { +#ifdef RLIMIT_MEMLOCK +    struct rlimit rlim; +#endif +    const void *a; +    size_t size; +    int r; +    size_t bs; + +    pa_assert(p); +    pa_assert(l > 0); + +    a = PA_PAGE_ALIGN_PTR(p); +    size = (const uint8_t*) p + l - (const uint8_t*) a; + +#ifdef HAVE_POSIX_MADVISE +    if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) { +        pa_log_debug("posix_madvise() worked fine!"); +        return (void*) p; +    } +#endif + +    /* Most likely the memory was not mmap()ed from a file and thus +     * madvise() didn't work, so let's misuse mlock() do page this +     * stuff back into RAM. Yeah, let's fuck with the MM!  It's so +     * inviting, the man page of mlock() tells us: "All pages that +     * contain a part of the specified address range are guaranteed to +     * be resident in RAM when the call returns successfully." */ + +#ifdef RLIMIT_MEMLOCK +    pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0); + +    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)); +        return (void*) p; +    } + +    bs = PA_PAGE_ALIGN(rlim.rlim_cur); +#else +    bs = PA_PAGE_SIZE*4; +#endif + +    pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r)); + +#ifdef HAVE_MLOCK +    while (size > 0 && bs > 0) { + +        if (bs > size) +            bs = size; + +        if (mlock(a, bs) < 0) { +            bs = PA_PAGE_ALIGN(bs / 2); +            continue; +        } + +        pa_assert_se(munlock(a, bs) == 0); + +        a = (const uint8_t*) a + bs; +        size -= bs; +    } +#endif + +    if (bs <= 0) +        pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno)); +    else +        pa_log_debug("mlock() worked fine!"); + +    return (void*) p; +} + +void pa_close_pipe(int fds[2]) { +    pa_assert(fds); + +    if (fds[0] >= 0) +        pa_assert_se(pa_close(fds[0]) == 0); + +    if (fds[1] >= 0) +        pa_assert_se(pa_close(fds[1]) == 0); + +    fds[0] = fds[1] = -1; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 1d921e03..0fe865ec 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -34,7 +34,8 @@  struct timeval; -void pa_make_nonblock_fd(int fd); +void pa_make_fd_nonblock(int fd); +void pa_make_fd_cloexec(int fd);  int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid);  int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid); @@ -55,19 +56,18 @@ char *pa_strlcpy(char *b, const char *s, size_t l);  char *pa_parent_dir(const char *fn); +void pa_make_realtime(void);  void pa_raise_priority(void);  void pa_reset_priority(void); -int pa_fd_set_cloexec(int fd, int b); - -int pa_parse_boolean(const char *s); +int pa_parse_boolean(const char *s) PA_GCC_PURE;  char *pa_split(const char *c, const char*delimiters, const char **state);  char *pa_split_spaces(const char *c, const char **state);  char *pa_strip_nl(char *s); -const char *pa_strsignal(int sig); +const char *pa_sig2str(int sig) PA_GCC_PURE;  int pa_own_uid_in_group(const char *name, gid_t *gid);  int pa_uid_in_group(uid_t uid, const char *name); @@ -84,12 +84,42 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env  char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);  size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); -int pa_startswith(const char *s, const char *pfx); -int pa_endswith(const char *s, const char *sfx); +int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE; +int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;  char *pa_runtime_path(const char *fn, char *s, size_t l);  int pa_atoi(const char *s, int32_t *ret_i);  int pa_atou(const char *s, uint32_t *ret_u); +int pa_atof(const char *s, float *ret_f); + +int pa_snprintf(char *str, size_t size, const char *format, ...); + +char *pa_truncate_utf8(char *c, size_t l); + +char *pa_getcwd(void); +char *pa_make_path_absolute(const char *p); + +void *pa_will_need(const void *p, size_t l); + +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; + +    if (pa_is_power_of_two(n)) +        return n; + +    while (j) { +        j = j >> 1; +        n = n | j; +    } + +    return n + 1; +} + +void pa_close_pipe(int fds[2]);  #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 31b6c188..e9008833 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -27,7 +27,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <stdio.h>  #include <signal.h> @@ -45,12 +44,36 @@  #include <pulsecore/props.h>  #include <pulsecore/random.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "core.h" +static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject); + +static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_core *c = PA_CORE(o); + +    pa_core_assert_ref(c); + +    switch (code) { + +        case PA_CORE_MESSAGE_UNLOAD_MODULE: +            pa_module_unload(c, userdata); +            return 0; + +        default: +            return -1; +    } +} + +static void core_free(pa_object *o); +  pa_core* pa_core_new(pa_mainloop_api *m, int shared) {      pa_core* c;      pa_mempool *pool; +    int j; + +    pa_assert(m);      if (shared) {          if (!(pool = pa_mempool_new(shared))) { @@ -66,7 +89,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {          }      } -    c = pa_xnew(pa_core, 1); +    c = pa_msgobject_new(pa_core); +    c->parent.parent.free = core_free; +    c->parent.process_msg = core_process_msg;      c->mainloop = m;      c->clients = pa_idxset_new(NULL, NULL); @@ -87,6 +112,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {      c->default_sample_spec.format = PA_SAMPLE_S16NE;      c->default_sample_spec.rate = 44100;      c->default_sample_spec.channels = 2; +    c->default_n_fragments = 4; +    c->default_fragment_size_msec = 25;      c->module_auto_unload_event = NULL;      c->module_defer_unload_event = NULL; @@ -99,22 +126,21 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {      c->mempool = pool; -    c->disallow_module_loading = 0; -      c->quit_event = NULL;      c->exit_idle_time = -1;      c->module_idle_time = 20;      c->scache_idle_time = 20; -    c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST; +    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE;      c->is_system_instance = 0; +    c->disallow_module_loading = 0; +    c->high_priority = 0; + -    pa_hook_init(&c->hook_sink_input_new, c); -    pa_hook_init(&c->hook_sink_disconnect, c); -    pa_hook_init(&c->hook_source_output_new, c); -    pa_hook_init(&c->hook_source_disconnect, c); +    for (j = 0; j < PA_CORE_HOOK_MAX; j++) +        pa_hook_init(&c->hooks[j], c);      pa_property_init(c); @@ -123,28 +149,31 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {  #ifdef SIGPIPE      pa_check_signal_is_blocked(SIGPIPE);  #endif +      return c;  } -void pa_core_free(pa_core *c) { -    assert(c); +static void core_free(pa_object *o) { +    pa_core *c = PA_CORE(o); +    int j; +    pa_assert(c);      pa_module_unload_all(c); -    assert(!c->modules); +    pa_assert(!c->modules); -    assert(pa_idxset_isempty(c->clients)); +    pa_assert(pa_idxset_isempty(c->clients));      pa_idxset_free(c->clients, NULL, NULL); -    assert(pa_idxset_isempty(c->sinks)); +    pa_assert(pa_idxset_isempty(c->sinks));      pa_idxset_free(c->sinks, NULL, NULL); -    assert(pa_idxset_isempty(c->sources)); +    pa_assert(pa_idxset_isempty(c->sources));      pa_idxset_free(c->sources, NULL, NULL); -    assert(pa_idxset_isempty(c->source_outputs)); +    pa_assert(pa_idxset_isempty(c->source_outputs));      pa_idxset_free(c->source_outputs, NULL, NULL); -    assert(pa_idxset_isempty(c->sink_inputs)); +    pa_assert(pa_idxset_isempty(c->sink_inputs));      pa_idxset_free(c->sink_inputs, NULL, NULL);      pa_scache_free(c); @@ -162,23 +191,21 @@ void pa_core_free(pa_core *c) {      pa_property_cleanup(c); -    pa_hook_free(&c->hook_sink_input_new); -    pa_hook_free(&c->hook_sink_disconnect); -    pa_hook_free(&c->hook_source_output_new); -    pa_hook_free(&c->hook_source_disconnect); +    for (j = 0; j < PA_CORE_HOOK_MAX; j++) +        pa_hook_free(&c->hooks[j]);      pa_xfree(c);  }  static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {      pa_core *c = userdata; -    assert(c->quit_event = e); +    pa_assert(c->quit_event == e);      m->quit(m, 0);  }  void pa_core_check_quit(pa_core *c) { -    assert(c); +    pa_assert(c);      if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {          struct timeval tv; diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 51a18b62..dfa80f8d 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -34,17 +34,51 @@  #include <pulsecore/queue.h>  #include <pulsecore/llist.h>  #include <pulsecore/hook-list.h> +#include <pulsecore/asyncmsgq.h>  typedef struct pa_core pa_core;  #include <pulsecore/core-subscribe.h>  #include <pulsecore/sink-input.h> +#include <pulsecore/msgobject.h> + +typedef enum pa_core_hook { +    PA_CORE_HOOK_SINK_NEW_POST, +    PA_CORE_HOOK_SINK_UNLINK, +    PA_CORE_HOOK_SINK_UNLINK_POST, +    PA_CORE_HOOK_SINK_STATE_CHANGED, +    PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED, +    PA_CORE_HOOK_SOURCE_NEW_POST, +    PA_CORE_HOOK_SOURCE_UNLINK, +    PA_CORE_HOOK_SOURCE_UNLINK_POST, +    PA_CORE_HOOK_SOURCE_STATE_CHANGED, +    PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED, +    PA_CORE_HOOK_SINK_INPUT_NEW, +    PA_CORE_HOOK_SINK_INPUT_PUT, +    PA_CORE_HOOK_SINK_INPUT_UNLINK, +    PA_CORE_HOOK_SINK_INPUT_UNLINK_POST, +    PA_CORE_HOOK_SINK_INPUT_MOVE, +    PA_CORE_HOOK_SINK_INPUT_MOVE_POST, +    PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED, +    PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, +    PA_CORE_HOOK_SOURCE_OUTPUT_NEW, +    PA_CORE_HOOK_SOURCE_OUTPUT_PUT, +    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK, +    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST, +    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE, +    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST, +    PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED, +    PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, +    PA_CORE_HOOK_MAX +} pa_core_hook_t;  /* The core structure of PulseAudio. Every PulseAudio daemon contains   * exactly one of these. It is used for storing kind of global   * variables for the daemon. */  struct pa_core { +    pa_msgobject parent; +      /* A random value which may be used to identify this instance of       * PulseAudio. Not cryptographically secure in any way. */      uint32_t cookie; @@ -61,6 +95,8 @@ struct pa_core {      char *default_source_name, *default_sink_name;      pa_sample_spec default_sample_spec; +    unsigned default_n_fragments, default_fragment_size_msec; +      pa_time_event *module_auto_unload_event;      pa_defer_event *module_defer_unload_event; @@ -71,27 +107,30 @@ struct pa_core {      pa_mempool *mempool; -    int disallow_module_loading, running_as_daemon;      int exit_idle_time, module_idle_time, scache_idle_time;      pa_time_event *quit_event;      pa_time_event *scache_auto_unload_event; +    int disallow_module_loading, running_as_daemon;      pa_resample_method_t resample_method; -      int is_system_instance; +    int high_priority;      /* hooks */ -    pa_hook -        hook_sink_input_new, -        hook_sink_disconnect, -        hook_source_output_new, -        hook_source_disconnect; +    pa_hook hooks[PA_CORE_HOOK_MAX]; +}; + +PA_DECLARE_CLASS(pa_core); +#define PA_CORE(o) pa_core_cast(o) + +enum { +    PA_CORE_MESSAGE_UNLOAD_MODULE, +    PA_CORE_MESSAGE_MAX  };  pa_core* pa_core_new(pa_mainloop_api *m, int shared); -void pa_core_free(pa_core*c);  /* Check whether noone is connected to this core */  void pa_core_check_quit(pa_core *c); diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h index e0a025bd..51dfc33d 100644 --- a/src/pulsecore/creds.h +++ b/src/pulsecore/creds.h @@ -26,7 +26,9 @@  #include <sys/types.h> -/* config.h must be included before this file */ +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif  #ifdef HAVE_SYS_SOCKET_H  #include <sys/socket.h> diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c index 944e3570..8bdb46fa 100644 --- a/src/pulsecore/dynarray.c +++ b/src/pulsecore/dynarray.c @@ -26,10 +26,10 @@  #endif  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include "dynarray.h" @@ -52,7 +52,7 @@ pa_dynarray* pa_dynarray_new(void) {  void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {      unsigned i; -    assert(a); +    pa_assert(a);      if (func)          for (i = 0; i < a->n_entries; i++) @@ -64,7 +64,7 @@ void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), voi  }  void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) { -    assert(a); +    pa_assert(a);      if (i >= a->n_allocated) {          unsigned n; @@ -85,21 +85,27 @@ void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {  }  unsigned pa_dynarray_append(pa_dynarray*a, void *p) { -    unsigned i = a->n_entries; +    unsigned i; + +    pa_assert(a); + +    i = a->n_entries;      pa_dynarray_put(a, i, p);      return i;  }  void *pa_dynarray_get(pa_dynarray*a, unsigned i) { -    assert(a); +    pa_assert(a); +      if (i >= a->n_entries)          return NULL; -    assert(a->data); +    pa_assert(a->data);      return a->data[i];  }  unsigned pa_dynarray_size(pa_dynarray*a) { -    assert(a); +    pa_assert(a); +      return a->n_entries;  } diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h index c0c3a6d8..8f7cfade 100644 --- a/src/pulsecore/endianmacros.h +++ b/src/pulsecore/endianmacros.h @@ -27,54 +27,68 @@  #include <inttypes.h> -#ifdef HAVE_CONFIG_H -#include <config.h> +#ifndef PACKAGE +#error "Please include config.h before including this file!"  #endif -#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) -#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#endif + +#ifdef HAVE_BYTESWAP_H +#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x)) +#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x)) +#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x)) +#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x)) +#else +#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#endif + +#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x) +#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x) -#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x) -#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x) +#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x) +#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)  #ifdef WORDS_BIGENDIAN - #define INT16_FROM_LE(x) INT16_SWAP(x) - #define INT16_FROM_BE(x) ((int16_t)(x)) + #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x) + #define PA_INT16_FROM_BE(x) ((int16_t)(x)) - #define INT16_TO_LE(x) INT16_SWAP(x) - #define INT16_TO_BE(x) ((int16_t)(x)) + #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x) + #define PA_INT16_TO_BE(x) ((int16_t)(x)) - #define UINT16_FROM_LE(x) UINT16_SWAP(x) - #define UINT16_FROM_BE(x) ((uint16_t)(x)) + #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x) + #define PA_UINT16_FROM_BE(x) ((uint16_t)(x)) - #define INT32_FROM_LE(x) INT32_SWAP(x) - #define INT32_FROM_BE(x) ((int32_t)(x)) + #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x) + #define PA_INT32_FROM_BE(x) ((int32_t)(x)) - #define UINT32_FROM_LE(x) UINT32_SWAP(x) - #define UINT32_FROM_BE(x) ((uint32_t)(x)) + #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x) + #define PA_UINT32_FROM_BE(x) ((uint32_t)(x)) - #define UINT32_TO_LE(x) UINT32_SWAP(x) - #define UINT32_TO_BE(x) ((uint32_t)(x)) + #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x) + #define PA_UINT32_TO_BE(x) ((uint32_t)(x))  #else - #define INT16_FROM_LE(x) ((int16_t)(x)) - #define INT16_FROM_BE(x) INT16_SWAP(x) + #define PA_INT16_FROM_LE(x) ((int16_t)(x)) + #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x) - #define INT16_TO_LE(x) ((int16_t)(x)) - #define INT16_TO_BE(x) INT16_SWAP(x) + #define PA_INT16_TO_LE(x) ((int16_t)(x)) + #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x) - #define UINT16_FROM_LE(x) ((uint16_t)(x)) - #define UINT16_FROM_BE(x) UINT16_SWAP(x) + #define PA_UINT16_FROM_LE(x) ((uint16_t)(x)) + #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x) - #define INT32_FROM_LE(x) ((int32_t)(x)) - #define INT32_FROM_BE(x) INT32_SWAP(x) + #define PA_INT32_FROM_LE(x) ((int32_t)(x)) + #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x) - #define UINT32_FROM_LE(x) ((uint32_t)(x)) - #define UINT32_FROM_BE(x) UINT32_SWAP(x) + #define PA_UINT32_FROM_LE(x) ((uint32_t)(x)) + #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x) - #define UINT32_TO_LE(x) ((uint32_t)(x)) - #define UINT32_TO_BE(x) UINT32_SWAP(x) + #define PA_UINT32_TO_LE(x) ((uint32_t)(x)) + #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)  #endif  #endif diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h index 3778a535..ea6a5665 100644 --- a/src/pulsecore/esound.h +++ b/src/pulsecore/esound.h @@ -205,7 +205,7 @@ typedef int esd_client_state_t;  /* the endian key is transferred in binary, if it's read into int, */  /* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */  /* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */ -#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY)) +#define ESD_SWAP_ENDIAN_KEY (PA_UINT32_SWAP(ESD_ENDIAN_KEY))  #endif diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c new file mode 100644 index 00000000..927bf00c --- /dev/null +++ b/src/pulsecore/fdsem.c @@ -0,0 +1,276 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 + +#ifdef HAVE_SYS_SYSCALL_H +#include <sys/syscall.h> +#endif + +#include <unistd.h> +#include <errno.h> + +#include <pulsecore/atomic.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulse/xmalloc.h> + +#ifndef HAVE_PIPE +#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(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 +#endif + +#include "fdsem.h" + +struct pa_fdsem { +    int fds[2]; +#ifdef HAVE_EVENTFD +    int efd; +#endif +    pa_atomic_t waiting; +    pa_atomic_t signalled; +    pa_atomic_t in_pipe; +}; + +pa_fdsem *pa_fdsem_new(void) { +    pa_fdsem *f; + +    f = pa_xnew(pa_fdsem, 1); + +#ifdef HAVE_EVENTFD +    if ((f->efd = eventfd(0)) >= 0) { +        pa_make_fd_cloexec(f->efd); +        f->fds[0] = f->fds[1] = -1; + +    } else +#endif +    { +        if (pipe(f->fds) < 0) { +            pa_xfree(f); +            return NULL; +        } + +        pa_make_fd_cloexec(f->fds[0]); +        pa_make_fd_cloexec(f->fds[1]); +    } + +    pa_atomic_store(&f->waiting, 0); +    pa_atomic_store(&f->signalled, 0); +    pa_atomic_store(&f->in_pipe, 0); + +    return f; +} + +void pa_fdsem_free(pa_fdsem *f) { +    pa_assert(f); + +#ifdef HAVE_EVENTFD +    if (f->efd >= 0) +        pa_close(f->efd); +#endif +    pa_close_pipe(f->fds); + +    pa_xfree(f); +} + +static void flush(pa_fdsem *f) { +    ssize_t r; +    pa_assert(f); + +    if (pa_atomic_load(&f->in_pipe) <= 0) +        return; + +    do { +        char x[10]; + +#ifdef HAVE_EVENTFD +        if (f->efd >= 0) { +            uint64_t u; + +            if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) { +                pa_assert(r < 0 && errno == EINTR); +                continue; +            } +            r = (ssize_t) u; +        } else +#endif + +        if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) { +            pa_assert(r < 0 && errno == EINTR); +            continue; +        } + +    } while (pa_atomic_sub(&f->in_pipe, r) > r); +} + +void pa_fdsem_post(pa_fdsem *f) { +    pa_assert(f); + +    if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) { + +        if (pa_atomic_load(&f->waiting)) { +            ssize_t r; +            char x = 'x'; + +            pa_atomic_inc(&f->in_pipe); + +            for (;;) { + +#ifdef HAVE_EVENTFD +                if (f->efd >= 0) { +                    uint64_t u = 1; + +                    if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) { +                        pa_assert(r < 0 && errno == EINTR); +                        continue; +                    } +                } else +#endif + +                if ((r = write(f->fds[1], &x, 1)) != 1) { +                    pa_assert(r < 0 && errno == EINTR); +                    continue; +                } + +                break; +            } +        } +    } +} + +void pa_fdsem_wait(pa_fdsem *f) { +    pa_assert(f); + +    flush(f); + +    if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) +        return; + +    pa_atomic_inc(&f->waiting); + +    while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) { +        char x[10]; +        ssize_t r; + +#ifdef HAVE_EVENTFD +        if (f->efd >= 0) { +            uint64_t u; + +            if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) { +                pa_assert(r < 0 && errno == EINTR); +                continue; +            } + +            r = (ssize_t) u; +        } else +#endif + +        if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) { +            pa_assert(r < 0 && errno == EINTR); +            continue; +        } + +        pa_atomic_sub(&f->in_pipe, r); +    } + +    pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); +} + +int pa_fdsem_try(pa_fdsem *f) { +    pa_assert(f); + +    flush(f); + +    if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) +        return 1; + +    return 0; +} + +int pa_fdsem_get(pa_fdsem *f) { +    pa_assert(f); + +#ifdef HAVE_EVENTFD +    if (f->efd >= 0) +        return f->efd; +#endif + +    return f->fds[0]; +} + +int pa_fdsem_before_poll(pa_fdsem *f) { +    pa_assert(f); + +    flush(f); + +    if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) +        return -1; + +    pa_atomic_inc(&f->waiting); + +    if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) { +        pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); +        return -1; +    } +    return 0; +} + +int pa_fdsem_after_poll(pa_fdsem *f) { +    pa_assert(f); + +    pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); + +    flush(f); + +    if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) +        return 1; + +    return 0; +} diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h new file mode 100644 index 00000000..f38ef205 --- /dev/null +++ b/src/pulsecore/fdsem.h @@ -0,0 +1,49 @@ +#ifndef foopulsefdsemhfoo +#define foopulsefdsemhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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 <sys/types.h> +#include <pulse/def.h> + +/* A simple, asynchronous semaphore which uses fds for sleeping. In + * the best case all functions are lock-free unless sleeping is + * required.  */ + +typedef struct pa_fdsem pa_fdsem; + +pa_fdsem *pa_fdsem_new(void); +void pa_fdsem_free(pa_fdsem *f); + +void pa_fdsem_post(pa_fdsem *f); +void pa_fdsem_wait(pa_fdsem *f); +int pa_fdsem_try(pa_fdsem *f); + +int pa_fdsem_get(pa_fdsem *f); + +int pa_fdsem_before_poll(pa_fdsem *f); +int pa_fdsem_after_poll(pa_fdsem *f); + + +#endif diff --git a/src/pulsecore/ffmpeg/Makefile b/src/pulsecore/ffmpeg/Makefile new file mode 100644 index 00000000..316beb72 --- /dev/null +++ b/src/pulsecore/ffmpeg/Makefile @@ -0,0 +1,13 @@ +# 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 diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h new file mode 100644 index 00000000..696fc986 --- /dev/null +++ b/src/pulsecore/ffmpeg/avcodec.h @@ -0,0 +1,82 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_H +#define AVCODEC_H + +/* Just a heavily bastardized version of the original file from + * ffmpeg, just enough to get resample2.c to compile without + * modification -- Lennart */ + +#if !defined(PACKAGE) && defined(HAVE_CONFIG_H) +#include <config.h> +#endif + +#include <sys/types.h> +#include <inttypes.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#define av_mallocz(l) calloc(1, (l)) +#define av_malloc(l) malloc(l) +#define av_realloc(p,l) realloc((p),(l)) +#define av_free(p) free(p) + +static inline void av_freep(void *k) { +    void **p = k; + +    if (p) { +        free(*p); +        *p = NULL; +    } +} + +static inline int av_clip(int a, int amin, int amax) +{ +    if (a < amin)      return amin; +    else if (a > amax) return amax; +    else               return a; +} + +#define av_log(a,b,c) + +#define FFABS(a) ((a) >= 0 ? (a) : (-(a))) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) + +struct AVResampleContext; +struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff); +int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx); +void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance); +void av_resample_close(struct AVResampleContext *c); +void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type); + +/* + * crude lrintf for non-C99 systems. + */ +#ifndef HAVE_LRINTF +#define lrintf(x) ((long int)(x)) +#endif + +#endif /* AVCODEC_H */ diff --git a/src/pulsecore/ffmpeg/dsputil.h b/src/pulsecore/ffmpeg/dsputil.h new file mode 100644 index 00000000..8da742d0 --- /dev/null +++ b/src/pulsecore/ffmpeg/dsputil.h @@ -0,0 +1 @@ +/* empty file, just here to allow us to compile an unmodified resampler2.c */ diff --git a/src/pulsecore/ffmpeg/resample2.c b/src/pulsecore/ffmpeg/resample2.c new file mode 100644 index 00000000..da1443d9 --- /dev/null +++ b/src/pulsecore/ffmpeg/resample2.c @@ -0,0 +1,324 @@ +/* + * audio resampling + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file resample2.c + * audio resampling + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#include "avcodec.h" +#include "dsputil.h" + +#ifndef CONFIG_RESAMPLE_HP +#define FILTER_SHIFT 15 + +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define FELEM_MAX INT16_MAX +#define FELEM_MIN INT16_MIN +#define WINDOW_TYPE 9 +#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE) +#define FILTER_SHIFT 30 + +#define FELEM int32_t +#define FELEM2 int64_t +#define FELEML int64_t +#define FELEM_MAX INT32_MAX +#define FELEM_MIN INT32_MIN +#define WINDOW_TYPE 12 +#else +#define FILTER_SHIFT 0 + +#define FELEM double +#define FELEM2 double +#define FELEML double +#define WINDOW_TYPE 24 +#endif + + +typedef struct AVResampleContext{ +    FELEM *filter_bank; +    int filter_length; +    int ideal_dst_incr; +    int dst_incr; +    int index; +    int frac; +    int src_incr; +    int compensation_distance; +    int phase_shift; +    int phase_mask; +    int linear; +}AVResampleContext; + +/** + * 0th order modified bessel function of the first kind. + */ +static double bessel(double x){ +    double v=1; +    double t=1; +    int i; + +    x= x*x/4; +    for(i=1; i<50; i++){ +        t *= x/(i*i); +        v += t; +    } +    return v; +} + +/** + * builds a polyphase filterbank. + * @param factor resampling factor + * @param scale wanted sum of coefficients for each filter + * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16 + */ +void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){ +    int ph, i; +    double x, y, w, tab[tap_count]; +    const int center= (tap_count-1)/2; + +    /* if upsampling, only need to interpolate, no filter */ +    if (factor > 1.0) +        factor = 1.0; + +    for(ph=0;ph<phase_count;ph++) { +        double norm = 0; +        for(i=0;i<tap_count;i++) { +            x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor; +            if (x == 0) y = 1.0; +            else        y = sin(x) / x; +            switch(type){ +            case 0:{ +                const float d= -0.5; //first order derivative = -0.5 +                x = fabs(((double)(i - center) - (double)ph / phase_count) * factor); +                if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*(            -x*x + x*x*x); +                else      y=                       d*(-4 + 8*x - 5*x*x + x*x*x); +                break;} +            case 1: +                w = 2.0*x / (factor*tap_count) + M_PI; +                y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w); +                break; +            default: +                w = 2.0*x / (factor*tap_count*M_PI); +                y *= bessel(type*sqrt(FFMAX(1-w*w, 0))); +                break; +            } + +            tab[i] = y; +            norm += y; +        } + +        /* normalize so that an uniform color remains the same */ +        for(i=0;i<tap_count;i++) { +#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE +            filter[ph * tap_count + i] = tab[i] / norm; +#else +            filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX); +#endif +        } +    } +#if 0 +    { +#define LEN 1024 +        int j,k; +        double sine[LEN + tap_count]; +        double filtered[LEN]; +        double maxff=-2, minff=2, maxsf=-2, minsf=2; +        for(i=0; i<LEN; i++){ +            double ss=0, sf=0, ff=0; +            for(j=0; j<LEN+tap_count; j++) +                sine[j]= cos(i*j*M_PI/LEN); +            for(j=0; j<LEN; j++){ +                double sum=0; +                ph=0; +                for(k=0; k<tap_count; k++) +                    sum += filter[ph * tap_count + k] * sine[k+j]; +                filtered[j]= sum / (1<<FILTER_SHIFT); +                ss+= sine[j + center] * sine[j + center]; +                ff+= filtered[j] * filtered[j]; +                sf+= sine[j + center] * filtered[j]; +            } +            ss= sqrt(2*ss/LEN); +            ff= sqrt(2*ff/LEN); +            sf= 2*sf/LEN; +            maxff= FFMAX(maxff, ff); +            minff= FFMIN(minff, ff); +            maxsf= FFMAX(maxsf, sf); +            minsf= FFMIN(minsf, sf); +            if(i%11==0){ +                av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf); +                minff=minsf= 2; +                maxff=maxsf= -2; +            } +        } +    } +#endif +} + +/** + * Initializes an audio resampler. + * Note, if either rate is not an integer then simply scale both rates up so they are. + */ +AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){ +    AVResampleContext *c= av_mallocz(sizeof(AVResampleContext)); +    double factor= FFMIN(out_rate * cutoff / in_rate, 1.0); +    int phase_count= 1<<phase_shift; + +    c->phase_shift= phase_shift; +    c->phase_mask= phase_count-1; +    c->linear= linear; + +    c->filter_length= FFMAX((int)ceil(filter_size/factor), 1); +    c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM)); +    av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE); +    memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM)); +    c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1]; + +    c->src_incr= out_rate; +    c->ideal_dst_incr= c->dst_incr= in_rate * phase_count; +    c->index= -phase_count*((c->filter_length-1)/2); + +    return c; +} + +void av_resample_close(AVResampleContext *c){ +    av_freep(&c->filter_bank); +    av_freep(&c); +} + +/** + * Compensates samplerate/timestamp drift. The compensation is done by changing + * the resampler parameters, so no audible clicks or similar distortions ocur + * @param compensation_distance distance in output samples over which the compensation should be performed + * @param sample_delta number of output samples which should be output less + * + * example: av_resample_compensate(c, 10, 500) + * here instead of 510 samples only 500 samples would be output + * + * note, due to rounding the actual compensation might be slightly different, + * especially if the compensation_distance is large and the in_rate used during init is small + */ +void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){ +//    sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr; +    c->compensation_distance= compensation_distance; +    c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance; +} + +/** + * resamples. + * @param src an array of unconsumed samples + * @param consumed the number of samples of src which have been consumed are returned here + * @param src_size the number of unconsumed samples available + * @param dst_size the amount of space in samples available in dst + * @param update_ctx if this is 0 then the context wont be modified, that way several channels can be resampled with the same context + * @return the number of samples written in dst or -1 if an error occured + */ +int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){ +    int dst_index, i; +    int index= c->index; +    int frac= c->frac; +    int dst_incr_frac= c->dst_incr % c->src_incr; +    int dst_incr=      c->dst_incr / c->src_incr; +    int compensation_distance= c->compensation_distance; + +  if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){ +        int64_t index2= ((int64_t)index)<<32; +        int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr; +        dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr); + +        for(dst_index=0; dst_index < dst_size; dst_index++){ +            dst[dst_index] = src[index2>>32]; +            index2 += incr; +        } +        frac += dst_index * dst_incr_frac; +        index += dst_index * dst_incr; +        index += frac / c->src_incr; +        frac %= c->src_incr; +  }else{ +    for(dst_index=0; dst_index < dst_size; dst_index++){ +        FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask); +        int sample_index= index >> c->phase_shift; +        FELEM2 val=0; + +        if(sample_index < 0){ +            for(i=0; i<c->filter_length; i++) +                val += src[FFABS(sample_index + i) % src_size] * filter[i]; +        }else if(sample_index + c->filter_length > src_size){ +            break; +        }else if(c->linear){ +            FELEM2 v2=0; +            for(i=0; i<c->filter_length; i++){ +                val += src[sample_index + i] * (FELEM2)filter[i]; +                v2  += src[sample_index + i] * (FELEM2)filter[i + c->filter_length]; +            } +            val+=(v2-val)*(FELEML)frac / c->src_incr; +        }else{ +            for(i=0; i<c->filter_length; i++){ +                val += src[sample_index + i] * (FELEM2)filter[i]; +            } +        } + +#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE +        dst[dst_index] = av_clip_int16(lrintf(val)); +#else +        val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT; +        dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val; +#endif + +        frac += dst_incr_frac; +        index += dst_incr; +        if(frac >= c->src_incr){ +            frac -= c->src_incr; +            index++; +        } + +        if(dst_index + 1 == compensation_distance){ +            compensation_distance= 0; +            dst_incr_frac= c->ideal_dst_incr % c->src_incr; +            dst_incr=      c->ideal_dst_incr / c->src_incr; +        } +    } +  } +    *consumed= FFMAX(index, 0) >> c->phase_shift; +    if(index>=0) index &= c->phase_mask; + +    if(compensation_distance){ +        compensation_distance -= dst_index; +        assert(compensation_distance > 0); +    } +    if(update_ctx){ +        c->frac= frac; +        c->index= index; +        c->dst_incr= dst_incr_frac + c->src_incr*dst_incr; +        c->compensation_distance= compensation_distance; +    } +#if 0 +    if(update_ctx && !c->compensation_distance){ +#undef rand +        av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2); +av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance); +    } +#endif + +    return dst_index; +} diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c index 00567ab3..d9740777 100644 --- a/src/pulsecore/flist.c +++ b/src/pulsecore/flist.c @@ -25,12 +25,14 @@  #include <config.h>  #endif -#include <assert.h> +#include <pulse/xmalloc.h>  #include <pulsecore/atomic.h>  #include <pulsecore/log.h>  #include <pulsecore/thread.h> -#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "flist.h" @@ -90,21 +92,18 @@ enum {  };  struct cell { -    pa_atomic_int_t state; +    pa_atomic_t state;      void *data;  };  struct pa_flist { -    struct cell *cells;      unsigned size; -    pa_atomic_int_t length; -    pa_atomic_int_t read_idx; -    pa_atomic_int_t write_idx; +    pa_atomic_t length; +    pa_atomic_t read_idx; +    pa_atomic_t write_idx;  }; -static int is_power_of_two(unsigned size) { -    return !(size & (size - 1)); -} +#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))  pa_flist *pa_flist_new(unsigned size) {      pa_flist *l; @@ -112,12 +111,11 @@ pa_flist *pa_flist_new(unsigned size) {      if (!size)          size = FLIST_SIZE; -    assert(is_power_of_two(size)); +    pa_assert(pa_is_power_of_two(size)); -    l = pa_xnew(pa_flist, 1); +    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size));      l->size = size; -    l->cells = pa_xnew0(struct cell, size);      pa_atomic_store(&l->read_idx, 0);      pa_atomic_store(&l->write_idx, 0); @@ -131,32 +129,37 @@ static int reduce(pa_flist *l, int value) {  }  void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) { -    assert(l); +    pa_assert(l);      if (free_cb) { +        struct cell *cells;          int len, idx; +        cells = PA_FLIST_CELLS(l); +          idx = reduce(l, pa_atomic_load(&l->read_idx));          len = pa_atomic_load(&l->length);          for (; len > 0; len--) { -            if (pa_atomic_load(&l->cells[idx].state) == STATE_USED) -                free_cb(l->cells[idx].data); +            if (pa_atomic_load(&cells[idx].state) == STATE_USED) +                free_cb(cells[idx].data);              idx = reduce(l, idx + 1);          }      } -    pa_xfree(l->cells);      pa_xfree(l);  }  int pa_flist_push(pa_flist*l, void *p) {      int idx, len, n; +    struct cell *cells; + +    pa_assert(l); +    pa_assert(p); -    assert(l); -    assert(p); +    cells = PA_FLIST_CELLS(l);      n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;      _Y; @@ -165,13 +168,13 @@ int pa_flist_push(pa_flist*l, void *p) {      for (; n > 0 ; n--) {          _Y; -        if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_UNUSED, STATE_BUSY)) { +        if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) {              _Y;              pa_atomic_inc(&l->write_idx);              _Y; -            l->cells[idx].data = p; +            cells[idx].data = p;              _Y; -            pa_atomic_store(&l->cells[idx].state, STATE_USED); +            pa_atomic_store(&cells[idx].state, STATE_USED);              _Y;              pa_atomic_inc(&l->length);              return 0; @@ -183,7 +186,7 @@ int pa_flist_push(pa_flist*l, void *p) {  #ifdef PROFILE      if (len > N_EXTRA_SCAN) -        pa_log("WARNING: Didn't  find free cell after %u iterations.", len); +        pa_log_warn("Didn't  find free cell after %u iterations.", len);  #endif      return -1; @@ -191,8 +194,11 @@ int pa_flist_push(pa_flist*l, void *p) {  void* pa_flist_pop(pa_flist*l) {      int idx, len, n; +    struct cell *cells; + +    pa_assert(l); -    assert(l); +    cells = PA_FLIST_CELLS(l);      n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;      _Y; @@ -201,14 +207,14 @@ void* pa_flist_pop(pa_flist*l) {      for (; n > 0 ; n--) {          _Y; -        if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_USED, STATE_BUSY)) { +        if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) {              void *p;              _Y;              pa_atomic_inc(&l->read_idx);              _Y; -            p = l->cells[idx].data; +            p = cells[idx].data;              _Y; -            pa_atomic_store(&l->cells[idx].state, STATE_UNUSED); +            pa_atomic_store(&cells[idx].state, STATE_UNUSED);              _Y;              pa_atomic_dec(&l->length); @@ -221,7 +227,7 @@ void* pa_flist_pop(pa_flist*l) {  #ifdef PROFILE      if (len > N_EXTRA_SCAN) -        pa_log("WARNING: Didn't find used cell after %u iterations.", len); +        pa_log_warn("Didn't find used cell after %u iterations.", len);  #endif      return NULL; diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h index bf702bf3..daf0fec4 100644 --- a/src/pulsecore/flist.h +++ b/src/pulsecore/flist.h @@ -26,6 +26,9 @@  #include <pulse/def.h> +#include <pulsecore/once.h> +#include <pulsecore/gccmacro.h> +  /* A multiple-reader multipler-write lock-free free list implementation */  typedef struct pa_flist pa_flist; @@ -38,4 +41,28 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb);  int pa_flist_push(pa_flist*l, void *p);  void* pa_flist_pop(pa_flist*l); +/* Please not that the destructor stuff is not really necesary, we do + * this just to make valgrind output more useful. */ + +#define PA_STATIC_FLIST_DECLARE(name, size, free_cb)                    \ +    static struct {                                                     \ +        pa_flist *flist;                                                \ +        pa_once once;                                                   \ +    } name##_flist = { NULL, PA_ONCE_INIT };                            \ +    static void name##_flist_init(void) {                               \ +        name##_flist.flist = pa_flist_new(size);                        \ +    }                                                                   \ +    static inline pa_flist* name##_flist_get(void) {                    \ +        pa_run_once(&name##_flist.once, name##_flist_init);             \ +        return name##_flist.flist;                                      \ +    }                                                                   \ +    static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR;        \ +    static void name##_flist_destructor(void) {                         \ +        if (name##_flist.flist)                                         \ +            pa_flist_free(name##_flist.flist, (free_cb));               \ +    }                                                                   \ +    struct __stupid_useless_struct_to_allow_trailing_semicolon + +#define PA_STATIC_FLIST_GET(name) (name##_flist_get()) +  #endif diff --git a/src/pulsecore/g711.c b/src/pulsecore/g711.c index 8c2bbf00..aa2d703a 100644 --- a/src/pulsecore/g711.c +++ b/src/pulsecore/g711.c @@ -43,30 +43,30 @@  #include "g711.h" -#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */ -#define	QUANT_MASK	(0xf)		/* Quantization field mask. */ -#define	NSEGS		(8)		/* Number of A-law segments. */ -#define	SEG_SHIFT	(4)		/* Left shift for segment number. */ -#define	SEG_MASK	(0x70)		/* Segment field mask. */ +#define        SIGN_BIT        (0x80)                /* Sign bit for a A-law byte. */ +#define        QUANT_MASK        (0xf)                /* Quantization field mask. */ +#define        NSEGS                (8)                /* Number of A-law segments. */ +#define        SEG_SHIFT        (4)                /* Left shift for segment number. */ +#define        SEG_MASK        (0x70)                /* Segment field mask. */  #if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)  static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, -			      0x1FF, 0x3FF, 0x7FF, 0xFFF}; +                              0x1FF, 0x3FF, 0x7FF, 0xFFF};  static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, -			      0x3FF, 0x7FF, 0xFFF, 0x1FFF}; +                              0x3FF, 0x7FF, 0xFFF, 0x1FFF};  static int16_t search( -	int16_t	val, -	int16_t *table, -	int size) +        int16_t        val, +        int16_t *table, +        int size)  { -	int i; +        int i; -	for (i = 0; i < size; i++) { -		if (val <= *table++) -			return (i); -	} -	return (size); +        for (i = 0; i < size; i++) { +                if (val <= *table++) +                        return (i); +        } +        return (size);  }  #endif /* !FAST_*_CONVERSION */ @@ -77,55 +77,55 @@ static int16_t search(   * the data shifted such that it only contains information in the lower   * 13-bits.   * - *		Linear Input Code	Compressed Code - *	------------------------	--------------- - *	0000000wxyza			000wxyz - *	0000001wxyza			001wxyz - *	000001wxyzab			010wxyz - *	00001wxyzabc			011wxyz - *	0001wxyzabcd			100wxyz - *	001wxyzabcde			101wxyz - *	01wxyzabcdef			110wxyz - *	1wxyzabcdefg			111wxyz + *                Linear Input Code        Compressed Code + *        ------------------------        --------------- + *        0000000wxyza                        000wxyz + *        0000001wxyza                        001wxyz + *        000001wxyzab                        010wxyz + *        00001wxyzabc                        011wxyz + *        0001wxyzabcd                        100wxyz + *        001wxyzabcde                        101wxyz + *        01wxyzabcdef                        110wxyz + *        1wxyzabcdefg                        111wxyz   *   * For further information see John C. Bellamy's Digital Telephony, 1982,   * John Wiley & Sons, pps 98-111 and 472-476.   */  unsigned char st_13linear2alaw( -	int16_t		pcm_val)	/* 2's complement (13-bit range) */ +        int16_t                pcm_val)        /* 2's complement (13-bit range) */  { -	int16_t		mask; -	short		seg; -	unsigned char	aval; +        int16_t                mask; +        short                seg; +        unsigned char        aval; -	/* Have calling software do it since its already doing a shift -	 * from 32-bits down to 16-bits. -	 */ -	/* pcm_val = pcm_val >> 3; */ +        /* Have calling software do it since its already doing a shift +         * from 32-bits down to 16-bits. +         */ +        /* pcm_val = pcm_val >> 3; */ -	/* A-law using even bit inversion */ -	if (pcm_val >= 0) { -		mask = 0xD5;		/* sign (7th) bit = 1 */ -	} else { -		mask = 0x55;		/* sign bit = 0 */ -		pcm_val = -pcm_val - 1; -	} +        /* A-law using even bit inversion */ +        if (pcm_val >= 0) { +                mask = 0xD5;                /* sign (7th) bit = 1 */ +        } else { +                mask = 0x55;                /* sign bit = 0 */ +                pcm_val = -pcm_val - 1; +        } -	/* Convert the scaled magnitude to segment number. */ -	seg = search(pcm_val, seg_aend, 8); +        /* Convert the scaled magnitude to segment number. */ +        seg = search(pcm_val, seg_aend, 8); -	/* Combine the sign, segment, and quantization bits. */ +        /* Combine the sign, segment, and quantization bits. */ -	if (seg >= 8)		/* out of range, return maximum value. */ -		return (unsigned char) (0x7F ^ mask); -	else { -		aval = (unsigned char) seg << SEG_SHIFT; -		if (seg < 2) -			aval |= (pcm_val >> 1) & QUANT_MASK; -		else -			aval |= (pcm_val >> seg) & QUANT_MASK; -		return (aval ^ mask); -	} +        if (seg >= 8)                /* out of range, return maximum value. */ +                return (unsigned char) (0x7F ^ mask); +        else { +                aval = (unsigned char) seg << SEG_SHIFT; +                if (seg < 2) +                        aval |= (pcm_val >> 1) & QUANT_MASK; +                else +                        aval |= (pcm_val >> seg) & QUANT_MASK; +                return (aval ^ mask); +        }  }  /* @@ -133,31 +133,31 @@ unsigned char st_13linear2alaw(   *   */  int16_t st_alaw2linear16( -	unsigned char	a_val) +        unsigned char        a_val)  { -	int16_t t; -	int16_t seg; +        int16_t t; +        int16_t seg; -	a_val ^= 0x55; +        a_val ^= 0x55; -	t = (a_val & QUANT_MASK) << 4; -	seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; -	switch (seg) { -	case 0: -		t += 8; -		break; -	case 1: -		t += 0x108; -		break; -	default: -		t += 0x108; -		t <<= seg - 1; -	} -	return ((a_val & SIGN_BIT) ? t : -t); +        t = (a_val & QUANT_MASK) << 4; +        seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; +        switch (seg) { +        case 0: +                t += 8; +                break; +        case 1: +                t += 0x108; +                break; +        default: +                t += 0x108; +                t <<= seg - 1; +        } +        return ((a_val & SIGN_BIT) ? t : -t);  }  #endif /* !FAST_ALAW_CONVERSION */ -#define	BIAS		(0x84)		/* Bias for linear code. */ +#define        BIAS                (0x84)                /* Bias for linear code. */  #define CLIP            8159  #ifndef FAST_ULAW_CONVERSION @@ -171,16 +171,16 @@ int16_t st_alaw2linear16(   * is biased by adding 33 which shifts the encoding range from (0 - 8158) to   * (33 - 8191). The result can be seen in the following encoding table:   * - *	Biased Linear Input Code	Compressed Code - *	------------------------	--------------- - *	00000001wxyza			000wxyz - *	0000001wxyzab			001wxyz - *	000001wxyzabc			010wxyz - *	00001wxyzabcd			011wxyz - *	0001wxyzabcde			100wxyz - *	001wxyzabcdef			101wxyz - *	01wxyzabcdefg			110wxyz - *	1wxyzabcdefgh			111wxyz + *        Biased Linear Input Code        Compressed Code + *        ------------------------        --------------- + *        00000001wxyza                        000wxyz + *        0000001wxyzab                        001wxyz + *        000001wxyzabc                        010wxyz + *        00001wxyzabcd                        011wxyz + *        0001wxyzabcde                        100wxyz + *        001wxyzabcdef                        101wxyz + *        01wxyzabcdefg                        110wxyz + *        1wxyzabcdefgh                        111wxyz   *   * Each biased linear code has a leading 1 which identifies the segment   * number. The value of the segment number is equal to 7 minus the number @@ -194,41 +194,41 @@ int16_t st_alaw2linear16(   * John Wiley & Sons, pps 98-111 and 472-476.   */  unsigned char st_14linear2ulaw( -	int16_t		pcm_val)	/* 2's complement (14-bit range) */ +        int16_t                pcm_val)        /* 2's complement (14-bit range) */  { -	int16_t		mask; -	int16_t		seg; -	unsigned char	uval; +        int16_t                mask; +        int16_t                seg; +        unsigned char        uval; -	/* Have calling software do it since its already doing a shift -	 * from 32-bits down to 16-bits. -	 */ -	/* pcm_val = pcm_val >> 2; */ +        /* Have calling software do it since its already doing a shift +         * from 32-bits down to 16-bits. +         */ +        /* pcm_val = pcm_val >> 2; */ -	/* u-law inverts all bits */ -	/* Get the sign and the magnitude of the value. */ -	if (pcm_val < 0) { -		pcm_val = -pcm_val; -		mask = 0x7F; -	} else { -		mask = 0xFF; -	} -        if ( pcm_val > CLIP ) pcm_val = CLIP;		/* clip the magnitude */ -	pcm_val += (BIAS >> 2); +        /* u-law inverts all bits */ +        /* Get the sign and the magnitude of the value. */ +        if (pcm_val < 0) { +                pcm_val = -pcm_val; +                mask = 0x7F; +        } else { +                mask = 0xFF; +        } +        if ( pcm_val > CLIP ) pcm_val = CLIP;                /* clip the magnitude */ +        pcm_val += (BIAS >> 2); -	/* Convert the scaled magnitude to segment number. */ -	seg = search(pcm_val, seg_uend, 8); +        /* Convert the scaled magnitude to segment number. */ +        seg = search(pcm_val, seg_uend, 8); -	/* -	 * Combine the sign, segment, quantization bits; -	 * and complement the code word. -	 */ -	if (seg >= 8)		/* out of range, return maximum value. */ -		return (unsigned char) (0x7F ^ mask); -	else { -		uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); -		return (uval ^ mask); -	} +        /* +         * Combine the sign, segment, quantization bits; +         * and complement the code word. +         */ +        if (seg >= 8)                /* out of range, return maximum value. */ +                return (unsigned char) (0x7F ^ mask); +        else { +                uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); +                return (uval ^ mask); +        }  } @@ -242,21 +242,21 @@ unsigned char st_14linear2ulaw(   * original code word. This is in keeping with ISDN conventions.   */  int16_t st_ulaw2linear16( -	unsigned char	u_val) +        unsigned char        u_val)  { -	int16_t		t; +        int16_t                t; -	/* Complement to obtain normal u-law value. */ -	u_val = ~u_val; +        /* Complement to obtain normal u-law value. */ +        u_val = ~u_val; -	/* -	 * Extract and bias the quantization bits. Then -	 * shift up by the segment number and subtract out the bias. -	 */ -	t = ((u_val & QUANT_MASK) << 3) + BIAS; -	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; +        /* +         * Extract and bias the quantization bits. Then +         * shift up by the segment number and subtract out the bias. +         */ +        t = ((u_val & QUANT_MASK) << 3) + BIAS; +        t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; -	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +        return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));  }  #endif /* !FAST_ULAW_CONVERSION */ @@ -2413,52 +2413,52 @@ int main()      printf("int16_t _st_alaw2linear16[256] = {\n  ");      for (x = 0; x < 256; x++)      { -	printf("%8d,", st_alaw2linear16(x)); -	y++; -	if (y == 7) -	{ -	    y = 0; -	    printf("\n  "); -	} +        printf("%8d,", st_alaw2linear16(x)); +        y++; +        if (y == 7) +        { +            y = 0; +            printf("\n  "); +        }      }      printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n  ");      y = 0;      for (x = 0; x < 0x2000; x++)      { -	printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x)); -	y++; -	if (y == 12) -	{ -	    y = 0; -	    printf("\n  "); -	} +        printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x)); +        y++; +        if (y == 12) +        { +            y = 0; +            printf("\n  "); +        }      }      printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n  ");      y = 0;      for (x = 0; x < 256; x++)      { -	printf("%8d,", st_ulaw2linear16(x)); -	y++; -	if (y == 7) -	{ -	    y = 0; -	    printf("\n  "); -	} +        printf("%8d,", st_ulaw2linear16(x)); +        y++; +        if (y == 7) +        { +            y = 0; +            printf("\n  "); +        }      }      printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n  ");      y = 0;      for (x = 0; x < 0x4000; x++)      { -	printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x)); -	y++; -	if (y == 12) -	{ -	    y = 0; -	    printf("\n  "); -	} +        printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x)); +        y++; +        if (y == 12) +        { +            y = 0; +            printf("\n  "); +        }      }      printf("\n};\n"); @@ -2468,64 +2468,64 @@ int main()  /* The following is not used by SoX but kept for reference */  #if 0  /* copy from CCITT G.711 specifications */ -unsigned char _u2a[128] = {			/* u- to A-law conversions */ -	1,	1,	2,	2,	3,	3,	4,	4, -	5,	5,	6,	6,	7,	7,	8,	8, -	9,	10,	11,	12,	13,	14,	15,	16, -	17,	18,	19,	20,	21,	22,	23,	24, -	25,	27,	29,	31,	33,	34,	35,	36, -	37,	38,	39,	40,	41,	42,	43,	44, -	46,	48,	49,	50,	51,	52,	53,	54, -	55,	56,	57,	58,	59,	60,	61,	62, -	64,	65,	66,	67,	68,	69,	70,	71, -	72,	73,	74,	75,	76,	77,	78,	79, +unsigned char _u2a[128] = {                        /* u- to A-law conversions */ +        1,        1,        2,        2,        3,        3,        4,        4, +        5,        5,        6,        6,        7,        7,        8,        8, +        9,        10,        11,        12,        13,        14,        15,        16, +        17,        18,        19,        20,        21,        22,        23,        24, +        25,        27,        29,        31,        33,        34,        35,        36, +        37,        38,        39,        40,        41,        42,        43,        44, +        46,        48,        49,        50,        51,        52,        53,        54, +        55,        56,        57,        58,        59,        60,        61,        62, +        64,        65,        66,        67,        68,        69,        70,        71, +        72,        73,        74,        75,        76,        77,        78,        79,  /* corrected: -	81,	82,	83,	84,	85,	86,	87,	88, +        81,        82,        83,        84,        85,        86,        87,        88,     should be: */ -	80,	82,	83,	84,	85,	86,	87,	88, -	89,	90,	91,	92,	93,	94,	95,	96, -	97,	98,	99,	100,	101,	102,	103,	104, -	105,	106,	107,	108,	109,	110,	111,	112, -	113,	114,	115,	116,	117,	118,	119,	120, -	121,	122,	123,	124,	125,	126,	127,	128}; +        80,        82,        83,        84,        85,        86,        87,        88, +        89,        90,        91,        92,        93,        94,        95,        96, +        97,        98,        99,        100,        101,        102,        103,        104, +        105,        106,        107,        108,        109,        110,        111,        112, +        113,        114,        115,        116,        117,        118,        119,        120, +        121,        122,        123,        124,        125,        126,        127,        128}; -unsigned char _a2u[128] = {			/* A- to u-law conversions */ -	1,	3,	5,	7,	9,	11,	13,	15, -	16,	17,	18,	19,	20,	21,	22,	23, -	24,	25,	26,	27,	28,	29,	30,	31, -	32,	32,	33,	33,	34,	34,	35,	35, -	36,	37,	38,	39,	40,	41,	42,	43, -	44,	45,	46,	47,	48,	48,	49,	49, -	50,	51,	52,	53,	54,	55,	56,	57, -	58,	59,	60,	61,	62,	63,	64,	64, -	65,	66,	67,	68,	69,	70,	71,	72, +unsigned char _a2u[128] = {                        /* A- to u-law conversions */ +        1,        3,        5,        7,        9,        11,        13,        15, +        16,        17,        18,        19,        20,        21,        22,        23, +        24,        25,        26,        27,        28,        29,        30,        31, +        32,        32,        33,        33,        34,        34,        35,        35, +        36,        37,        38,        39,        40,        41,        42,        43, +        44,        45,        46,        47,        48,        48,        49,        49, +        50,        51,        52,        53,        54,        55,        56,        57, +        58,        59,        60,        61,        62,        63,        64,        64, +        65,        66,        67,        68,        69,        70,        71,        72,  /* corrected: -	73,	74,	75,	76,	77,	78,	79,	79, +        73,        74,        75,        76,        77,        78,        79,        79,     should be: */ -	73,	74,	75,	76,	77,	78,	79,	80, +        73,        74,        75,        76,        77,        78,        79,        80, -	80,	81,	82,	83,	84,	85,	86,	87, -	88,	89,	90,	91,	92,	93,	94,	95, -	96,	97,	98,	99,	100,	101,	102,	103, -	104,	105,	106,	107,	108,	109,	110,	111, -	112,	113,	114,	115,	116,	117,	118,	119, -	120,	121,	122,	123,	124,	125,	126,	127}; +        80,        81,        82,        83,        84,        85,        86,        87, +        88,        89,        90,        91,        92,        93,        94,        95, +        96,        97,        98,        99,        100,        101,        102,        103, +        104,        105,        106,        107,        108,        109,        110,        111, +        112,        113,        114,        115,        116,        117,        118,        119, +        120,        121,        122,        123,        124,        125,        126,        127};  /* A-law to u-law conversion */  unsigned char st_alaw2ulaw( -	unsigned char	aval) +        unsigned char        aval)  { -	aval &= 0xff; -	return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : -	    (0x7F ^ _a2u[aval ^ 0x55])); +        aval &= 0xff; +        return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : +            (0x7F ^ _a2u[aval ^ 0x55]));  }  /* u-law to A-law conversion */  unsigned char st_ulaw2alaw( -	unsigned char	uval) +        unsigned char        uval)  { -	uval &= 0xff; -	return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : -	    (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +        uval &= 0xff; +        return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : +            (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));  }  #endif diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h index 57d28006..e9f0d093 100644 --- a/src/pulsecore/gccmacro.h +++ b/src/pulsecore/gccmacro.h @@ -52,4 +52,29 @@  #define PA_GCC_UNUSED  #endif +#ifdef __GNUC__ +#define PA_GCC_DESTRUCTOR __attribute__ ((destructor)) +#else +/** Call this function when process terminates */ +#define PA_GCC_DESTRUCTOR +#endif + +#ifndef PA_GCC_PURE +#ifdef __GNUCC__ +#define PA_GCC_PURE __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list and global state **/ +#define PA_GCC_PURE +#endif +#endif + +#ifndef PA_GCC_CONST +#ifdef __GNUCC__ +#define PA_GCC_CONST __attribute__ ((const)) +#else +/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/ +#define PA_GCC_CONST +#endif +#endif +  #endif diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c index 818e12bf..f5589664 100644 --- a/src/pulsecore/hashmap.c +++ b/src/pulsecore/hashmap.c @@ -26,13 +26,14 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <pulse/xmalloc.h>  #include <pulsecore/idxset.h>  #include <pulsecore/log.h> +#include <pulsecore/flist.h> +#include <pulsecore/macro.h>  #include "hashmap.h" @@ -55,6 +56,8 @@ struct pa_hashmap {      pa_compare_func_t compare_func;  }; +PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); +  pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {      pa_hashmap *h; @@ -69,8 +72,8 @@ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_f  }  static void remove(pa_hashmap *h, struct hashmap_entry *e) { -    assert(h); -    assert(e); +    pa_assert(h); +    pa_assert(e);      if (e->next)          e->next->previous = e->previous; @@ -84,16 +87,18 @@ static void remove(pa_hashmap *h, struct hashmap_entry *e) {      if (e->bucket_previous)          e->bucket_previous->bucket_next = e->bucket_next;      else { -        assert(e->hash < h->size); +        pa_assert(e->hash < h->size);          h->data[e->hash] = e->bucket_next;      } -    pa_xfree(e); +    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) +        pa_xfree(e); +      h->n_entries--;  }  void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { -    assert(h); +    pa_assert(h);      while (h->first_entry) {          if (free_func) @@ -107,8 +112,8 @@ void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), v  static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {      struct hashmap_entry *e; -    assert(h); -    assert(hash < h->size); +    pa_assert(h); +    pa_assert(hash < h->size);      for (e = h->data[hash]; e; e = e->bucket_next)          if (h->compare_func(e->key, key) == 0) @@ -120,14 +125,16 @@ static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key)  int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {      struct hashmap_entry *e;      unsigned hash; -    assert(h); +    pa_assert(h);      hash = h->hash_func(key) % h->size;      if ((e = get(h, hash, key)))          return -1; -    e = pa_xnew(struct hashmap_entry, 1); +    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) +        e = pa_xnew(struct hashmap_entry, 1); +      e->hash = hash;      e->key = key;      e->value = value; @@ -152,7 +159,7 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key) {      unsigned hash;      struct hashmap_entry *e; -    assert(h); +    pa_assert(h);      hash = h->hash_func(key) % h->size; @@ -167,7 +174,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {      unsigned hash;      void *data; -    assert(h); +    pa_assert(h);      hash = h->hash_func(key) % h->size; @@ -184,8 +191,8 @@ unsigned pa_hashmap_size(pa_hashmap *h) {  }  void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { -    assert(h); -    assert(state); +    pa_assert(h); +    pa_assert(state);      if (!*state)          *state = h->first_entry; @@ -207,7 +214,7 @@ void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {  void* pa_hashmap_steal_first(pa_hashmap *h) {      void *data; -    assert(h); +    pa_assert(h);      if (!h->first_entry)          return NULL; @@ -218,7 +225,7 @@ void* pa_hashmap_steal_first(pa_hashmap *h) {  }  void *pa_hashmap_get_first(pa_hashmap *h) { -    assert(h); +    pa_assert(h);      if (!h->first_entry)          return NULL; diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index 3ca2a479..98df4502 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -32,11 +32,13 @@  typedef struct pa_hashmap pa_hashmap; +typedef void (*pa_free2_cb_t)(void *p, void *userdata); +  /* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */  pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);  /* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */ -void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); +void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata);  /* Returns non-zero when the entry already exists */  int pa_hashmap_put(pa_hashmap *h, const void *key, void *value); diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c index 4f884187..3a6874c4 100644 --- a/src/pulsecore/hook-list.c +++ b/src/pulsecore/hook-list.c @@ -21,10 +21,16 @@    USA.  ***/ -#include <pulsecore/hook-list.h> +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulsecore/macro.h> + +#include "hook-list.h"  void pa_hook_init(pa_hook *hook, void *data) { -    assert(hook); +    pa_assert(hook);      PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);      hook->last = NULL; @@ -33,8 +39,8 @@ void pa_hook_init(pa_hook *hook, void *data) {  }  static void slot_free(pa_hook *hook, pa_hook_slot *slot) { -    assert(hook); -    assert(slot); +    pa_assert(hook); +    pa_assert(slot);      if (hook->last == slot)          hook->last = slot->prev; @@ -45,8 +51,8 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {  }  void pa_hook_free(pa_hook *hook) { -    assert(hook); -    assert(!hook->firing); +    pa_assert(hook); +    pa_assert(!hook->firing);      while (hook->slots)          slot_free(hook, hook->slots); @@ -57,7 +63,7 @@ void pa_hook_free(pa_hook *hook) {  pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {      pa_hook_slot *slot; -    assert(cb); +    pa_assert(cb);      slot = pa_xnew(pa_hook_slot, 1);      slot->hook = hook; @@ -72,8 +78,8 @@ pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {  }  void pa_hook_slot_free(pa_hook_slot *slot) { -    assert(slot); -    assert(!slot->dead); +    pa_assert(slot); +    pa_assert(!slot->dead);      if (slot->hook->firing > 0) {          slot->dead = 1; @@ -86,7 +92,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {      pa_hook_slot *slot, *next;      pa_hook_result_t result = PA_HOOK_OK; -    assert(hook); +    pa_assert(hook);      hook->firing ++; diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c index 70ef7ba7..8a88471f 100644 --- a/src/pulsecore/idxset.c +++ b/src/pulsecore/idxset.c @@ -27,32 +27,35 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/flist.h>  #include "idxset.h" -typedef struct idxset_entry { +struct idxset_entry {      void *data;      uint32_t index;      unsigned hash_value;      struct idxset_entry *hash_prev, *hash_next;      struct idxset_entry* iterate_prev, *iterate_next; -} idxset_entry; +};  struct pa_idxset {      pa_hash_func_t hash_func;      pa_compare_func_t compare_func;      unsigned hash_table_size, n_entries; -    idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; +    struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;      uint32_t index, start_index, array_size;  }; +PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); +  unsigned pa_idxset_string_hash_func(const void *p) {      unsigned hash = 0;      const char *c; @@ -82,7 +85,7 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun      s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;      s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;      s->hash_table_size = 127; -    s->hash_table = pa_xnew0(idxset_entry*, s->hash_table_size); +    s->hash_table = pa_xnew0(struct idxset_entry*, s->hash_table_size);      s->array = NULL;      s->array_size = 0;      s->index = 0; @@ -95,15 +98,17 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun  }  void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { -    assert(s); +    pa_assert(s);      while (s->iterate_list_head) { -        idxset_entry *e = s->iterate_list_head; +        struct idxset_entry *e = s->iterate_list_head;          s->iterate_list_head = s->iterate_list_head->iterate_next;          if (free_func)              free_func(e->data, userdata); -        pa_xfree(e); + +        if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) +            pa_xfree(e);      }      pa_xfree(s->hash_table); @@ -111,10 +116,10 @@ void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), v      pa_xfree(s);  } -static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) { -    assert(p); +static struct idxset_entry* hash_scan(pa_idxset *s, struct idxset_entry* e, const void *p) { +    pa_assert(p); -    assert(s->compare_func); +    pa_assert(s->compare_func);      for (; e; e = e->hash_next)          if (s->compare_func(e->data, p) == 0)              return e; @@ -124,8 +129,10 @@ static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {  static void extend_array(pa_idxset *s, uint32_t idx) {      uint32_t i, j, l; -    idxset_entry** n; -    assert(idx >= s->start_index); +    struct idxset_entry** n; + +    pa_assert(s); +    pa_assert(idx >= s->start_index);      if (idx < s->start_index + s->array_size)          return; @@ -135,7 +142,7 @@ static void extend_array(pa_idxset *s, uint32_t idx) {              break;      l = idx - s->start_index - i + 100; -    n = pa_xnew0(idxset_entry*, l); +    n = pa_xnew0(struct idxset_entry*, l);      for (j = 0; j < s->array_size-i; j++)          n[j] = s->array[i+j]; @@ -147,7 +154,9 @@ static void extend_array(pa_idxset *s, uint32_t idx) {      s->start_index += i;  } -static idxset_entry** array_index(pa_idxset*s, uint32_t idx) { +static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) { +    pa_assert(s); +      if (idx >= s->start_index + s->array_size)          return NULL; @@ -159,15 +168,15 @@ static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {  int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {      unsigned h; -    idxset_entry *e, **a; +    struct idxset_entry *e, **a; -    assert(s); -    assert(p); +    pa_assert(s); +    pa_assert(p); -    assert(s->hash_func); +    pa_assert(s->hash_func);      h = s->hash_func(p) % s->hash_table_size; -    assert(s->hash_table); +    pa_assert(s->hash_table);      if ((e = hash_scan(s, s->hash_table[h], p))) {          if (idx)              *idx = e->index; @@ -175,7 +184,8 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {          return -1;      } -    e = pa_xmalloc(sizeof(idxset_entry)); +    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) +        e = pa_xnew(struct idxset_entry, 1);      e->data = p;      e->index = s->index++;      e->hash_value = h; @@ -190,23 +200,23 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {      /* Insert into array */      extend_array(s, e->index);      a = array_index(s, e->index); -    assert(a && !*a); +    pa_assert(a && !*a);      *a = e;      /* Insert into linked list */      e->iterate_next = NULL;      e->iterate_prev = s->iterate_list_tail;      if (s->iterate_list_tail) { -        assert(s->iterate_list_head); +        pa_assert(s->iterate_list_head);          s->iterate_list_tail->iterate_next = e;      } else { -        assert(!s->iterate_list_head); +        pa_assert(!s->iterate_list_head);          s->iterate_list_head = e;      }      s->iterate_list_tail = e;      s->n_entries++; -    assert(s->n_entries >= 1); +    pa_assert(s->n_entries >= 1);      if (idx)          *idx = e->index; @@ -215,8 +225,8 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {  }  void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { -    idxset_entry **a; -    assert(s); +    struct idxset_entry **a; +    pa_assert(s);      if (!(a = array_index(s, idx)))          return NULL; @@ -229,13 +239,15 @@ void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {  void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {      unsigned h; -    idxset_entry *e; -    assert(s && p); +    struct idxset_entry *e; -    assert(s->hash_func); +    pa_assert(s); +    pa_assert(p); + +    pa_assert(s->hash_func);      h = s->hash_func(p) % s->hash_table_size; -    assert(s->hash_table); +    pa_assert(s->hash_table);      if (!(e = hash_scan(s, s->hash_table[h], p)))          return NULL; @@ -245,13 +257,15 @@ void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {      return e->data;  } -static void remove_entry(pa_idxset *s, idxset_entry *e) { -    idxset_entry **a; -    assert(s && e); +static void remove_entry(pa_idxset *s, struct idxset_entry *e) { +    struct idxset_entry **a; + +    pa_assert(s); +    pa_assert(e);      /* Remove from array */      a = array_index(s, e->index); -    assert(a && *a && *a == e); +    pa_assert(a && *a && *a == e);      *a = NULL;      /* Remove from linked list */ @@ -274,17 +288,18 @@ static void remove_entry(pa_idxset *s, idxset_entry *e) {      else          s->hash_table[e->hash_value] = e->hash_next; -    pa_xfree(e); +    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) +        pa_xfree(e); -    assert(s->n_entries >= 1); +    pa_assert(s->n_entries >= 1);      s->n_entries--;  }  void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) { -    idxset_entry **a; +    struct idxset_entry **a;      void *data; -    assert(s); +    pa_assert(s);      if (!(a = array_index(s, idx)))          return NULL; @@ -299,14 +314,16 @@ void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {  }  void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) { -    idxset_entry *e; +    struct idxset_entry *e;      unsigned h;      void *r; -    assert(s->hash_func); +    pa_assert(s); + +    pa_assert(s->hash_func);      h = s->hash_func(data) % s->hash_table_size; -    assert(s->hash_table); +    pa_assert(s->hash_table);      if (!(e = hash_scan(s, s->hash_table[h], data)))          return NULL; @@ -320,8 +337,10 @@ void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {  }  void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) { -    idxset_entry **a, *e = NULL; -    assert(s && idx); +    struct idxset_entry **a, *e = NULL; + +    pa_assert(s); +    pa_assert(idx);      if ((a = array_index(s, *idx)) && *a)          e = (*a)->iterate_next; @@ -337,7 +356,7 @@ void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {  }  void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { -    assert(s); +    pa_assert(s);      if (!s->iterate_list_head)          return NULL; @@ -348,9 +367,10 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {  }  void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { -    idxset_entry **a, *e = NULL; -    assert(s); -    assert(idx); +    struct idxset_entry **a, *e = NULL; + +    pa_assert(s); +    pa_assert(idx);      if ((a = array_index(s, *idx)) && *a)          e = (*a)->iterate_next; @@ -365,13 +385,15 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {  }  int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) { -    idxset_entry *e; -    assert(s && func); +    struct idxset_entry *e; + +    pa_assert(s); +    pa_assert(func);      e = s->iterate_list_head;      while (e) {          int del = 0, r; -        idxset_entry *n = e->iterate_next; +        struct idxset_entry *n = e->iterate_next;          r = func(e->data, e->index, &del, userdata); @@ -388,12 +410,14 @@ int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del,  }  unsigned pa_idxset_size(pa_idxset*s) { -    assert(s); +    pa_assert(s); +      return s->n_entries;  }  int pa_idxset_isempty(pa_idxset *s) { -    assert(s); +    pa_assert(s); +      return s->n_entries == 0;  } diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h index 17a70f4f..5b55cec2 100644 --- a/src/pulsecore/idxset.h +++ b/src/pulsecore/idxset.h @@ -44,11 +44,6 @@ int pa_idxset_trivial_compare_func(const void *a, const void *b);  unsigned pa_idxset_string_hash_func(const void *p);  int pa_idxset_string_compare_func(const void *a, const void *b); -#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) -#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) -#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p)) -#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR(u) -  typedef unsigned (*pa_hash_func_t)(const void *p);  typedef int (*pa_compare_func_t)(const void *a, const void *b); diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c index 302369f7..041bc09b 100644 --- a/src/pulsecore/inet_ntop.c +++ b/src/pulsecore/inet_ntop.c @@ -47,7 +47,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {      switch (af) {      case AF_INET: -        snprintf(dst, cnt, "%d.%d.%d.%d", +        pa_snprintf(dst, cnt, "%d.%d.%d.%d",  #ifdef WORDS_BIGENDIAN              (int)(in->s_addr >> 24) & 0xff,              (int)(in->s_addr >> 16) & 0xff, @@ -61,7 +61,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {  #endif          break;      case AF_INET6: -        snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x", +        pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",              in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],              in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],              in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5], diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c index 6f58ae75..01f17ab3 100644 --- a/src/pulsecore/iochannel.c +++ b/src/pulsecore/iochannel.c @@ -27,7 +27,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <fcntl.h>  #include <unistd.h>  #include <errno.h> @@ -47,6 +46,7 @@  #include <pulsecore/core-util.h>  #include <pulsecore/socket-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "iochannel.h" @@ -58,21 +58,21 @@ struct pa_iochannel {      pa_iochannel_cb_t callback;      void*userdata; -    int readable; -    int writable; -    int hungup; +    pa_bool_t readable; +    pa_bool_t writable; +    pa_bool_t hungup; -    int no_close; +    pa_bool_t no_close;      pa_io_event* input_event, *output_event;  };  static void enable_mainloop_sources(pa_iochannel *io) { -    assert(io); +    pa_assert(io);      if (io->input_event == io->output_event && io->input_event) {          pa_io_event_flags_t f = PA_IO_EVENT_NULL; -        assert(io->input_event); +        pa_assert(io->input_event);          if (!io->readable)              f |= PA_IO_EVENT_INPUT; @@ -90,28 +90,28 @@ static void enable_mainloop_sources(pa_iochannel *io) {  static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {      pa_iochannel *io = userdata; -    int changed = 0; +    pa_bool_t changed = FALSE; -    assert(m); -    assert(e); -    assert(fd >= 0); -    assert(userdata); +    pa_assert(m); +    pa_assert(e); +    pa_assert(fd >= 0); +    pa_assert(userdata);      if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) { -        io->hungup = 1; -        changed = 1; +        io->hungup = TRUE; +        changed = TRUE;      }      if ((f & PA_IO_EVENT_INPUT) && !io->readable) { -        io->readable = 1; -        changed = 1; -        assert(e == io->input_event); +        io->readable = TRUE; +        changed = TRUE; +        pa_assert(e == io->input_event);      }      if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) { -        io->writable = 1; -        changed = 1; -        assert(e == io->output_event); +        io->writable = TRUE; +        changed = TRUE; +        pa_assert(e == io->output_event);      }      if (changed) { @@ -125,8 +125,8 @@ static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_fla  pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {      pa_iochannel *io; -    assert(m); -    assert(ifd >= 0 || ofd >= 0); +    pa_assert(m); +    pa_assert(ifd >= 0 || ofd >= 0);      io = pa_xnew(pa_iochannel, 1);      io->ifd = ifd; @@ -136,26 +136,26 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {      io->userdata = NULL;      io->callback = NULL; -    io->readable = 0; -    io->writable = 0; -    io->hungup = 0; -    io->no_close = 0; +    io->readable = FALSE; +    io->writable = FALSE; +    io->hungup = FALSE; +    io->no_close = FALSE;      io->input_event = io->output_event = NULL;      if (ifd == ofd) { -        assert(ifd >= 0); -        pa_make_nonblock_fd(io->ifd); +        pa_assert(ifd >= 0); +        pa_make_fd_nonblock(io->ifd);          io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);      } else {          if (ifd >= 0) { -            pa_make_nonblock_fd(io->ifd); +            pa_make_fd_nonblock(io->ifd);              io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);          }          if (ofd >= 0) { -            pa_make_nonblock_fd(io->ofd); +            pa_make_fd_nonblock(io->ofd);              io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);          }      } @@ -164,7 +164,7 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {  }  void pa_iochannel_free(pa_iochannel*io) { -    assert(io); +    pa_assert(io);      if (io->input_event)          io->mainloop->io_free(io->input_event); @@ -182,20 +182,20 @@ void pa_iochannel_free(pa_iochannel*io) {      pa_xfree(io);  } -int pa_iochannel_is_readable(pa_iochannel*io) { -    assert(io); +pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) { +    pa_assert(io);      return io->readable || io->hungup;  } -int pa_iochannel_is_writable(pa_iochannel*io) { -    assert(io); +pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) { +    pa_assert(io);      return io->writable && !io->hungup;  } -int pa_iochannel_is_hungup(pa_iochannel*io) { -    assert(io); +pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) { +    pa_assert(io);      return io->hungup;  } @@ -203,14 +203,13 @@ int pa_iochannel_is_hungup(pa_iochannel*io) {  ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {      ssize_t r; -    assert(io); -    assert(data); -    assert(l); -    assert(io->ofd >= 0); +    pa_assert(io); +    pa_assert(data); +    pa_assert(l); +    pa_assert(io->ofd >= 0); -    r = pa_write(io->ofd, data, l, &io->ofd_type); -    if (r >= 0) { -        io->writable = 0; +    if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) { +        io->writable = FALSE;          enable_mainloop_sources(io);      } @@ -220,13 +219,12 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {  ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {      ssize_t r; -    assert(io); -    assert(data); -    assert(io->ifd >= 0); +    pa_assert(io); +    pa_assert(data); +    pa_assert(io->ifd >= 0); -    r = pa_read(io->ifd, data, l, &io->ifd_type); -    if (r >= 0) { -        io->readable = 0; +    if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) { +        io->readable = FALSE;          enable_mainloop_sources(io);      } @@ -235,13 +233,13 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {  #ifdef HAVE_CREDS -int pa_iochannel_creds_supported(pa_iochannel *io) { +pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {      struct sockaddr_un sa;      socklen_t l; -    assert(io); -    assert(io->ifd >= 0); -    assert(io->ofd == io->ifd); +    pa_assert(io); +    pa_assert(io->ifd >= 0); +    pa_assert(io->ofd == io->ifd);      l = sizeof(sa); @@ -254,8 +252,8 @@ int pa_iochannel_creds_supported(pa_iochannel *io) {  int pa_iochannel_creds_enable(pa_iochannel *io) {      int t = 1; -    assert(io); -    assert(io->ifd >= 0); +    pa_assert(io); +    pa_assert(io->ifd >= 0);      if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {          pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno)); @@ -273,10 +271,10 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l      struct ucred *u;      struct cmsghdr *cmsg; -    assert(io); -    assert(data); -    assert(l); -    assert(io->ofd >= 0); +    pa_assert(io); +    pa_assert(data); +    pa_assert(l); +    pa_assert(io->ofd >= 0);      memset(&iov, 0, sizeof(iov));      iov.iov_base = (void*) data; @@ -309,25 +307,25 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l      mh.msg_flags = 0;      if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) { -        io->writable = 0; +        io->writable = FALSE;          enable_mainloop_sources(io);      }      return r;  } -ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, int *creds_valid) { +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {      ssize_t r;      struct msghdr mh;      struct iovec iov;      uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))]; -    assert(io); -    assert(data); -    assert(l); -    assert(io->ifd >= 0); -    assert(creds); -    assert(creds_valid); +    pa_assert(io); +    pa_assert(data); +    pa_assert(l); +    pa_assert(io->ifd >= 0); +    pa_assert(creds); +    pa_assert(creds_valid);      memset(&iov, 0, sizeof(iov));      iov.iov_base = data; @@ -353,17 +351,17 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr              if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {                  struct ucred u; -                assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))); +                pa_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));                  memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));                  creds->gid = u.gid;                  creds->uid = u.uid; -                *creds_valid = 1; +                *creds_valid = TRUE;                  break;              }          } -        io->readable = 0; +        io->readable = FALSE;          enable_mainloop_sources(io);      } @@ -373,46 +371,52 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr  #endif /* HAVE_CREDS */  void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) { -    assert(io); +    pa_assert(io);      io->callback = _callback;      io->userdata = userdata;  } -void pa_iochannel_set_noclose(pa_iochannel*io, int b) { -    assert(io); +void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) { +    pa_assert(io); -    io->no_close = b; +    io->no_close = !!b;  }  void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) { -    assert(io); -    assert(s); -    assert(l); +    pa_assert(io); +    pa_assert(s); +    pa_assert(l);      pa_socket_peer_to_string(io->ifd, s, l);  }  int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) { -    assert(io); +    pa_assert(io);      return pa_socket_set_rcvbuf(io->ifd, l);  }  int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) { -    assert(io); +    pa_assert(io);      return pa_socket_set_sndbuf(io->ofd, l);  }  pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) { -    assert(io); +    pa_assert(io);      return io->mainloop;  }  int pa_iochannel_get_recv_fd(pa_iochannel *io) { -    assert(io); +    pa_assert(io);      return io->ifd;  } + +int pa_iochannel_get_send_fd(pa_iochannel *io) { +    pa_assert(io); + +    return io->ofd; +} diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h index c22fefd3..c9794d99 100644 --- a/src/pulsecore/iochannel.h +++ b/src/pulsecore/iochannel.h @@ -25,10 +25,15 @@    USA.  ***/ +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif +  #include <sys/types.h>  #include <pulse/mainloop-api.h>  #include <pulsecore/creds.h> +#include <pulsecore/macro.h>  /* A wrapper around UNIX file descriptors for attaching them to the a     main event loop. Everytime new data may be read or be written to @@ -54,20 +59,20 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);  ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);  #ifdef HAVE_CREDS -int pa_iochannel_creds_supported(pa_iochannel *io); +pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io);  int pa_iochannel_creds_enable(pa_iochannel *io);  ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred); -ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, int *creds_valid); +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, pa_bool_t *creds_valid);  #endif -int pa_iochannel_is_readable(pa_iochannel*io); -int pa_iochannel_is_writable(pa_iochannel*io); -int pa_iochannel_is_hungup(pa_iochannel*io); +pa_bool_t pa_iochannel_is_readable(pa_iochannel*io); +pa_bool_t pa_iochannel_is_writable(pa_iochannel*io); +pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io);  /* Don't close the file descirptors when the io channel is freed. By   * default the file descriptors are closed. */ -void pa_iochannel_set_noclose(pa_iochannel*io, int b); +void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b);  /* Set the callback function that is called whenever data becomes available for read or write */  typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata); @@ -83,5 +88,6 @@ int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);  pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);  int pa_iochannel_get_recv_fd(pa_iochannel *io); +int pa_iochannel_get_send_fd(pa_iochannel *io);  #endif diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 07b60bee..5fd2189b 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -27,14 +27,16 @@  #include <errno.h>  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h>  #include <pulse/xmalloc.h> +#include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/refcnt.h>  #include "ioline.h" @@ -42,10 +44,11 @@  #define READ_SIZE (1024)  struct pa_ioline { +    PA_REFCNT_DECLARE; +      pa_iochannel *io;      pa_defer_event *defer_event;      pa_mainloop_api *mainloop; -    int ref;      int dead;      char *wbuf; @@ -65,9 +68,10 @@ static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);  pa_ioline* pa_ioline_new(pa_iochannel *io) {      pa_ioline *l; -    assert(io); +    pa_assert(io);      l = pa_xnew(pa_ioline, 1); +    PA_REFCNT_INIT(l);      l->io = io;      l->dead = 0; @@ -79,7 +83,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {      l->callback = NULL;      l->userdata = NULL; -    l->ref = 1;      l->mainloop = pa_iochannel_get_mainloop_api(io); @@ -94,7 +97,7 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {  }  static void ioline_free(pa_ioline *l) { -    assert(l); +    pa_assert(l);      if (l->io)          pa_iochannel_free(l->io); @@ -108,24 +111,24 @@ static void ioline_free(pa_ioline *l) {  }  void pa_ioline_unref(pa_ioline *l) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); -    if ((--l->ref) <= 0) +    if (PA_REFCNT_DEC(l) <= 0)          ioline_free(l);  }  pa_ioline* pa_ioline_ref(pa_ioline *l) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); -    l->ref++; +    PA_REFCNT_INC(l);      return l;  }  void pa_ioline_close(pa_ioline *l) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      l->dead = 1; @@ -146,9 +149,9 @@ void pa_ioline_close(pa_ioline *l) {  void pa_ioline_puts(pa_ioline *l, const char *c) {      size_t len; -    assert(l); -    assert(l->ref >= 1); -    assert(c); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); +    pa_assert(c);      if (l->dead)          return; @@ -158,7 +161,7 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {          len = BUFFER_LIMIT - l->wbuf_valid_length;      if (len) { -        assert(l->wbuf_length >= l->wbuf_valid_length); +        pa_assert(l->wbuf_length >= l->wbuf_valid_length);          /* In case the allocated buffer is too small, enlarge it. */          if (l->wbuf_valid_length + len > l->wbuf_length) { @@ -178,7 +181,7 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {              l->wbuf_index = 0;          } -        assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length); +        pa_assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);          /* Append the new string */          memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len); @@ -189,17 +192,17 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {  }  void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      l->callback = callback;      l->userdata = userdata;  }  static void failure(pa_ioline *l, int process_leftover) { -    assert(l); -    assert(l->ref >= 1); -    assert(!l->dead); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); +    pa_assert(!l->dead);      if (process_leftover && l->rbuf_valid_length > 0) {          /* Pass the last missing bit to the client */ @@ -220,7 +223,9 @@ static void failure(pa_ioline *l, int process_leftover) {  }  static void scan_for_lines(pa_ioline *l, size_t skip) { -    assert(l && l->ref >= 1 && skip < l->rbuf_valid_length); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); +    pa_assert(skip < l->rbuf_valid_length);      while (!l->dead && l->rbuf_valid_length > skip) {          char *e, *p; @@ -255,7 +260,8 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {  static int do_write(pa_ioline *l);  static int do_read(pa_ioline *l) { -    assert(l && l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      while (!l->dead && pa_iochannel_is_readable(l->io)) {          ssize_t r; @@ -289,11 +295,11 @@ static int do_read(pa_ioline *l) {          len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; -        assert(len >= READ_SIZE); +        pa_assert(len >= READ_SIZE);          /* Read some data */          if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) { -            if (r < 0) { +            if (r < 0 && errno != ECONNRESET) {                  pa_log("read(): %s", pa_cstrerror(errno));                  failure(l, 0);              } else @@ -314,13 +320,19 @@ static int do_read(pa_ioline *l) {  /* Try to flush the buffer */  static int do_write(pa_ioline *l) {      ssize_t r; -    assert(l && l->ref >= 1); + +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) { -        if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) { -            pa_log("write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); +        if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) { + +            if (r < 0 && errno != EPIPE) +                pa_log("write(): %s", pa_cstrerror(errno)); +              failure(l, 0); +              return -1;          } @@ -337,8 +349,8 @@ static int do_write(pa_ioline *l) {  /* Try to flush read/write data */  static void do_work(pa_ioline *l) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      pa_ioline_ref(l); @@ -358,21 +370,28 @@ static void do_work(pa_ioline *l) {  static void io_callback(pa_iochannel*io, void *userdata) {      pa_ioline *l = userdata; -    assert(io && l && l->ref >= 1); + +    pa_assert(io); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      do_work(l);  }  static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {      pa_ioline *l = userdata; -    assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e); + +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1); +    pa_assert(l->mainloop == m); +    pa_assert(l->defer_event == e);      do_work(l);  }  void pa_ioline_defer_close(pa_ioline *l) { -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      l->defer_close = 1; @@ -384,8 +403,8 @@ void pa_ioline_printf(pa_ioline *l, const char *format, ...) {      char *t;      va_list ap; -    assert(l); -    assert(l->ref >= 1); +    pa_assert(l); +    pa_assert(PA_REFCNT_VALUE(l) >= 1);      va_start(ap, format);      t = pa_vsprintf_malloc(format, ap); diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c index a240d2a0..9b22e8f5 100644 --- a/src/pulsecore/ipacl.c +++ b/src/pulsecore/ipacl.c @@ -46,13 +46,13 @@  #include <arpa/inet.h>  #endif -#include "winsock.h" -  #include <pulse/xmalloc.h>  #include <pulsecore/core-util.h>  #include <pulsecore/llist.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/winsock.h>  #ifndef HAVE_INET_PTON  #include "inet_pton.h" @@ -77,7 +77,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {      char *a;      pa_ip_acl *acl; -    assert(s); +    pa_assert(s);      acl = pa_xnew(pa_ip_acl, 1);      PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries); @@ -91,7 +91,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {              *slash = 0;              slash++;              if (pa_atou(slash, &bits) < 0) { -                pa_log("failed to parse number of bits: %s", slash); +                pa_log_warn("Failed to parse number of bits: %s", slash);                  goto fail;              }          } else @@ -102,21 +102,21 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {              e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;              if (e.bits > 32) { -                pa_log("number of bits out of range: %i", e.bits); +                pa_log_warn("Number of bits out of range: %i", e.bits);                  goto fail;              }              e.family = AF_INET;              if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0) -                pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits); +                pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);          } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {              e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;              if (e.bits > 128) { -                pa_log("number of bits out of range: %i", e.bits); +                pa_log_warn("Number of bits out of range: %i", e.bits);                  goto fail;              }              e.family = AF_INET6; @@ -138,11 +138,11 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {                  }                  if (t) -                    pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits); +                    pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);              }          } else { -            pa_log("failed to parse address: %s", a); +            pa_log_warn("Failed to parse address: %s", a);              goto fail;          } @@ -162,7 +162,7 @@ fail:  }  void pa_ip_acl_free(pa_ip_acl *acl) { -    assert(acl); +    pa_assert(acl);      while (acl->entries) {          struct acl_entry *e = acl->entries; @@ -178,8 +178,8 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {      struct acl_entry *e;      socklen_t  salen; -    assert(acl); -    assert(fd >= 0); +    pa_assert(acl); +    pa_assert(fd >= 0);      salen = sizeof(sa);      if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0) diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h index 8fc8e22b..e62f15b4 100644 --- a/src/pulsecore/llist.h +++ b/src/pulsecore/llist.h @@ -24,77 +24,86 @@    USA.  ***/ -#include <assert.h> +#include <pulsecore/macro.h>  /* Some macros for maintaining doubly linked lists */  /* The head of the linked list. Use this in the structure that shall   * contain the head of the linked list */ -#define PA_LLIST_HEAD(t,name) t *name +#define PA_LLIST_HEAD(t,name)                                           \ +    t *name  /* The pointers in the linked list's items. Use this in the item structure */ -#define PA_LLIST_FIELDS(t) t *next, *prev +#define PA_LLIST_FIELDS(t)                                              \ +    t *next, *prev  /* Initialize the list's head */ -#define PA_LLIST_HEAD_INIT(t,item) do { (item) = (t*) NULL; } while(0) +#define PA_LLIST_HEAD_INIT(t,item)                                      \ +    do {                                                                \ +        (item) = (t*) NULL; }                                           \ +    while(0)  /* Initialize a list item */ -#define PA_LLIST_INIT(t,item) do { \ -                               t *_item = (item); \ -                               assert(_item); \ -                               _item->prev = _item->next = NULL; \ -                               } while(0) +#define PA_LLIST_INIT(t,item)                                           \ +    do {                                                                \ +        t *_item = (item);                                              \ +        pa_assert(_item);                                               \ +        _item->prev = _item->next = NULL;                               \ +    } while(0)  /* Prepend an item to the list */ -#define PA_LLIST_PREPEND(t,head,item) do { \ -                                        t **_head = &(head), *_item = (item); \ -                                        assert(_item); \ -                                        if ((_item->next = *_head)) \ -                                           _item->next->prev = _item; \ -                                        _item->prev = NULL; \ -                                        *_head = _item; \ -                                        } while (0) +#define PA_LLIST_PREPEND(t,head,item)                                   \ +    do {                                                                \ +        t **_head = &(head), *_item = (item);                           \ +        pa_assert(_item);                                               \ +        if ((_item->next = *_head))                                     \ +            _item->next->prev = _item;                                  \ +        _item->prev = NULL;                                             \ +        *_head = _item;                                                 \ +    } while (0)  /* Remove an item from the list */ -#define PA_LLIST_REMOVE(t,head,item) do { \ -                                    t **_head = &(head), *_item = (item); \ -                                    assert(_item); \ -                                    if (_item->next) \ -                                       _item->next->prev = _item->prev; \ -                                    if (_item->prev) \ -                                       _item->prev->next = _item->next; \ -                                    else {\ -                                       assert(*_head == _item); \ -                                       *_head = _item->next; \ -                                    } \ -                                    _item->next = _item->prev = NULL; \ -                                    } while(0) - -#define PA_LLIST_FIND_HEAD(t,item,head) \ -do { \ -    t **_head = (head), *_item = (item); \ -    *_head = _item; \ -    assert(_head); \ -    while ((*_head)->prev) \ -        *_head = (*_head)->prev; \ -} while (0) - -#define PA_LLIST_INSERT_AFTER(t,head,a,b) \ -do { \ -    t **_head = &(head), *_a = (a), *_b = (b); \ -    assert(_b); \ -    if (!_a) { \ -        if ((_b->next = *_head)) \ -            _b->next->prev = _b; \ -        _b->prev = NULL; \ -        *_head = _b; \ -    } else { \ -        if ((_b->next = _a->next)) \ -            _b->next->prev = _b; \ -        _b->prev = _a; \ -        _a->next = _b; \ -    } \ -} while (0) - +#define PA_LLIST_REMOVE(t,head,item)                                    \ +    do {                                                                \ +        t **_head = &(head), *_item = (item);                           \ +        pa_assert(_item);                                               \ +        if (_item->next)                                                \ +            _item->next->prev = _item->prev;                            \ +        if (_item->prev)                                                \ +            _item->prev->next = _item->next;                            \ +        else {                                                          \ +            pa_assert(*_head == _item);                                 \ +            *_head = _item->next;                                       \ +        }                                                               \ +        _item->next = _item->prev = NULL;                               \ +    } while(0) + +/* Find the head of the list */ +#define PA_LLIST_FIND_HEAD(t,item,head)                                 \ +    do {                                                                \ +        t **_head = (head), *_item = (item);                            \ +        *_head = _item;                                                 \ +        pa_assert(_head);                                               \ +        while ((*_head)->prev)                                          \ +            *_head = (*_head)->prev;                                    \ +    } while (0) + +/* Insert an item after another one (a = where, b = what) */ +#define PA_LLIST_INSERT_AFTER(t,head,a,b)                               \ +    do {                                                                \ +        t **_head = &(head), *_a = (a), *_b = (b);                      \ +        pa_assert(_b);                                                  \ +        if (!_a) {                                                      \ +            if ((_b->next = *_head))                                    \ +                _b->next->prev = _b;                                    \ +            _b->prev = NULL;                                            \ +            *_head = _b;                                                \ +        } else {                                                        \ +            if ((_b->next = _a->next))                                  \ +                _b->next->prev = _b;                                    \ +            _b->prev = _a;                                              \ +            _a->next = _b;                                              \ +        }                                                               \ +    } while (0)  #endif diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index 0033adb9..c824e84d 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdarg.h>  #include <stdio.h>  #include <unistd.h> @@ -40,6 +39,7 @@  #include <pulse/xmalloc.h>  #include <pulse/util.h> +#include <pulsecore/macro.h>  #include <pulsecore/core-util.h>  #include "log.h" @@ -71,24 +71,30 @@ static const char level_to_char[] = {  };  void pa_log_set_ident(const char *p) { -    if (log_ident) -        pa_xfree(log_ident); -    if (log_ident_local) -        pa_xfree(log_ident_local); +    pa_xfree(log_ident); +    pa_xfree(log_ident_local);      log_ident = pa_xstrdup(p); -    log_ident_local = pa_utf8_to_locale(log_ident); -    if (!log_ident_local) +    if (!(log_ident_local = pa_utf8_to_locale(log_ident)))          log_ident_local = pa_xstrdup(log_ident);  } +/* To make valgrind shut up. */ +static void ident_destructor(void) PA_GCC_DESTRUCTOR; +static void ident_destructor(void) { +    pa_xfree(log_ident); +    pa_xfree(log_ident_local); +} +  void pa_log_set_maximal_level(pa_log_level_t l) { -    assert(l < PA_LOG_LEVEL_MAX); +    pa_assert(l < PA_LOG_LEVEL_MAX); +      maximal_level = l;  }  void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) { -    assert(t == PA_LOG_USER || !func); +    pa_assert(t == PA_LOG_USER || !func); +      log_target = t;      user_log_func = func;  } @@ -104,8 +110,8 @@ void pa_log_levelv_meta(      const char *e;      char *text, *t, *n, *location; -    assert(level < PA_LOG_LEVEL_MAX); -    assert(format); +    pa_assert(level < PA_LOG_LEVEL_MAX); +    pa_assert(format);      if ((e = getenv(ENV_LOGLEVEL)))          maximal_level = atoi(e); @@ -221,6 +227,7 @@ void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {  void pa_log_level(pa_log_level_t level, const char *format, ...) {      va_list ap; +      va_start(ap, format);      pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);      va_end(ap); diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c new file mode 100644 index 00000000..711396d8 --- /dev/null +++ b/src/pulsecore/ltdl-helper.c @@ -0,0 +1,64 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <ctype.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> + +#include "ltdl-helper.h" + +pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *symbol) { +    char *sn, *c; +    pa_void_func_t f; + +    pa_assert(handle); +    pa_assert(module); +    pa_assert(symbol); + +    if ((f = ((pa_void_func_t) (long) lt_dlsym(handle, symbol)))) +        return f; + +    /* As the .la files might have been cleansed from the system, we should +     * try with the ltdl prefix as well. */ + +    sn = pa_sprintf_malloc("%s_LTX_%s", module, symbol); + +    for (c = sn; *c; c++) +        if (!isalnum(*c)) +            *c = '_'; + +    f = (pa_void_func_t) (long) lt_dlsym(handle, sn); +    pa_xfree(sn); + +    return f; +} diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h new file mode 100644 index 00000000..5c7388a1 --- /dev/null +++ b/src/pulsecore/ltdl-helper.h @@ -0,0 +1,34 @@ +#ifndef foopulsecoreltdlhelperhfoo +#define foopulsecoreltdlhelperhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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. +***/ + +#include <ltdl.h> + +typedef void (*pa_void_func_t)(void); + +pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol); + +#endif + diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h new file mode 100644 index 00000000..c6bba437 --- /dev/null +++ b/src/pulsecore/macro.h @@ -0,0 +1,149 @@ +#ifndef foopulsemacrohfoo +#define foopulsemacrohfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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. +***/ + +#include <sys/types.h> +#include <unistd.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> + +#include <pulsecore/log.h> + +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif + +#if defined(PAGE_SIZE) +#define PA_PAGE_SIZE ((size_t) PAGE_SIZE) +#elif defined(PAGESIZE) +#define PA_PAGE_SIZE ((size_t) PAGESIZE) +#elif defined(HAVE_SYSCONF) +#define PA_PAGE_SIZE ((size_t) (sysconf(_SC_PAGE_SIZE))) +#else +/* Let's hope it's like x86. */ +#define PA_PAGE_SIZE ((size_t) 4096) +#endif + +static inline size_t pa_align(size_t l) { +    return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)); +} +#define PA_ALIGN(x) (pa_align(x)) + +static inline void* pa_page_align_ptr(const void *p) { +    return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1)); +} +#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x)) + +static inline size_t pa_page_align(size_t l) { +    return l & ~(PA_PAGE_SIZE-1); +} +#define PA_PAGE_ALIGN(x) (pa_page_align(x)) + +#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef CLAMP +#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#endif + +/* This type is not intended to be used in exported APIs! Use classic "int" there! */ +#ifdef HAVE_STD_BOOL +typedef _Bool pa_bool_t; +#else +typedef int pa_bool_t; +#endif + +#ifndef FALSE +#define FALSE ((pa_bool_t) 0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#ifdef __GNUC__ +#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define PA_PRETTY_FUNCTION "" +#endif + +#define pa_return_if_fail(expr) \ +    do { \ +        if (!(expr)) { \ +            pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ +            return; \ +        } \ +    } while(0) + +#define pa_return_val_if_fail(expr, val) \ +    do { \ +        if (!(expr)) { \ +            pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ +            return (val); \ +        } \ +    } while(0) + +#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL) + +#define pa_assert assert + +#define pa_assert_not_reached() pa_assert(!"Should not be reached.") + +/* An assert which guarantees side effects of x */ +#ifdef NDEBUG +#define pa_assert_se(x) x +#else +#define pa_assert_se(x) pa_assert(x) +#endif + +#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) +#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) + +#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p)) +#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u) + +#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p)) +#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u) + +#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p)) +#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u) + +#ifdef OS_IS_WIN32 +#define PA_PATH_SEP "\\" +#define PA_PATH_SEP_CHAR '\\' +#else +#define PA_PATH_SEP "/" +#define PA_PATH_SEP_CHAR '/' +#endif + +#endif diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c index dd1d71f3..8ca7c962 100644 --- a/src/pulsecore/mcalign.c +++ b/src/pulsecore/mcalign.c @@ -27,10 +27,10 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include "mcalign.h" @@ -41,7 +41,7 @@ struct pa_mcalign {  pa_mcalign *pa_mcalign_new(size_t base) {      pa_mcalign *m; -    assert(base); +    pa_assert(base);      m = pa_xnew(pa_mcalign, 1); @@ -53,7 +53,7 @@ pa_mcalign *pa_mcalign_new(size_t base) {  }  void pa_mcalign_free(pa_mcalign *m) { -    assert(m); +    pa_assert(m);      if (m->leftover.memblock)          pa_memblock_unref(m->leftover.memblock); @@ -65,13 +65,13 @@ void pa_mcalign_free(pa_mcalign *m) {  }  void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { -    assert(m); -    assert(c); +    pa_assert(m); +    pa_assert(c); -    assert(c->memblock); -    assert(c->length > 0); +    pa_assert(c->memblock); +    pa_assert(c->length > 0); -    assert(!m->current.memblock); +    pa_assert(!m->current.memblock);      /* Append to the leftover memory block */      if (m->leftover.memblock) { @@ -91,9 +91,10 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {          } else {              size_t l; +            void *lo_data, *m_data;              /* We have to copy */ -            assert(m->leftover.length < m->base); +            pa_assert(m->leftover.length < m->base);              l = m->base - m->leftover.length;              if (l > c->length) @@ -102,10 +103,15 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {              /* Can we use the current block? */              pa_memchunk_make_writable(&m->leftover, m->base); -            memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); +            lo_data = pa_memblock_acquire(m->leftover.memblock); +            m_data = pa_memblock_acquire(c->memblock); +            memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l); +            pa_memblock_release(m->leftover.memblock); +            pa_memblock_release(c->memblock);              m->leftover.length += l; -            assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); +            pa_assert(m->leftover.length <= m->base); +            pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));              if (c->length > l) {                  /* Save the remainder of the memory block */ @@ -128,12 +134,13 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {  }  int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { -    assert(m); -    assert(c); +    pa_assert(m); +    pa_assert(c);      /* First test if there's a leftover memory block available */      if (m->leftover.memblock) { -        assert(m->leftover.length > 0 && m->leftover.length <= m->base); +        pa_assert(m->leftover.length > 0); +        pa_assert(m->leftover.length <= m->base);          /* The leftover memory block is not yet complete */          if (m->leftover.length < m->base) @@ -155,13 +162,13 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {      /* Now let's see if there is other data available */      if (m->current.memblock) {          size_t l; -        assert(m->current.length >= m->base); +        pa_assert(m->current.length >= m->base);          /* The length of the returned memory block */          l = m->current.length;          l /= m->base;          l *= m->base; -        assert(l > 0); +        pa_assert(l > 0);          /* Prepare the returned block */          *c = m->current; @@ -169,7 +176,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {          c->length = l;          /* Drop that from the current memory block */ -        assert(l <= m->current.length); +        pa_assert(l <= m->current.length);          m->current.index += l;          m->current.length -= l; @@ -178,7 +185,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {              pa_memblock_unref(m->current.memblock);          else {              /* Move the raimainder to leftover */ -            assert(m->current.length < m->base && !m->leftover.memblock); +            pa_assert(m->current.length < m->base && !m->leftover.memblock);              m->leftover = m->current;          } @@ -194,10 +201,10 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {  }  size_t pa_mcalign_csize(pa_mcalign *m, size_t l) { -    assert(m); -    assert(l > 0); +    pa_assert(m); +    pa_assert(l > 0); -    assert(!m->current.memblock); +    pa_assert(!m->current.memblock);      if (m->leftover.memblock)          l += m->leftover.length; diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 6f09a906..99b5a13f 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -14,7 +14,7 @@    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. +  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 @@ -28,15 +28,21 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <unistd.h> +#include <signal.h> +#include <errno.h>  #include <pulse/xmalloc.h> +#include <pulse/def.h>  #include <pulsecore/shm.h>  #include <pulsecore/log.h>  #include <pulsecore/hashmap.h> +#include <pulsecore/semaphore.h> +#include <pulsecore/macro.h> +#include <pulsecore/flist.h> +#include <pulsecore/core-util.h>  #include "memblock.h" @@ -48,6 +54,32 @@  #define PA_MEMIMPORT_SLOTS_MAX 128  #define PA_MEMIMPORT_SEGMENTS_MAX 16 +struct pa_memblock { +    PA_REFCNT_DECLARE; /* the reference counter */ +    pa_mempool *pool; + +    pa_memblock_type_t type; +    int read_only; /* boolean */ + +    pa_atomic_ptr_t data; +    size_t length; + +    pa_atomic_t n_acquired; +    pa_atomic_t please_signal; + +    union { +        struct { +            /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ +            pa_free_cb_t free_cb; +        } user; + +        struct  { +            uint32_t id; +            pa_memimport_segment *segment; +        } imported; +    } per_type; +}; +  struct pa_memimport_segment {      pa_memimport *import;      pa_shm memory; @@ -55,6 +87,8 @@ struct pa_memimport_segment {  };  struct pa_memimport { +    pa_mutex *mutex; +      pa_mempool *pool;      pa_hashmap *segments;      pa_hashmap *blocks; @@ -73,9 +107,11 @@ struct memexport_slot {  };  struct pa_memexport { +    pa_mutex *mutex;      pa_mempool *pool;      struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX]; +      PA_LLIST_HEAD(struct memexport_slot, free_slots);      PA_LLIST_HEAD(struct memexport_slot, used_slots);      unsigned n_init; @@ -95,24 +131,32 @@ struct mempool_slot {  };  struct pa_mempool { +    pa_semaphore *semaphore; +    pa_mutex *mutex; +      pa_shm memory;      size_t block_size; -    unsigned n_blocks, n_init; +    unsigned n_blocks; + +    pa_atomic_t n_init;      PA_LLIST_HEAD(pa_memimport, imports);      PA_LLIST_HEAD(pa_memexport, exports);      /* A list of free slots that may be reused */ -    PA_LLIST_HEAD(struct mempool_slot, free_slots); +    pa_flist *free_slots;      pa_mempool_stat stat;  };  static void segment_detach(pa_memimport_segment *seg); +PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree); + +/* No lock necessary */  static void stat_add(pa_memblock*b) { -    assert(b); -    assert(b->pool); +    pa_assert(b); +    pa_assert(b->pool);      pa_atomic_inc(&b->pool->stat.n_allocated);      pa_atomic_add(&b->pool->stat.allocated_size, b->length); @@ -129,19 +173,20 @@ static void stat_add(pa_memblock*b) {      pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);  } +/* No lock necessary */  static void stat_remove(pa_memblock *b) { -    assert(b); -    assert(b->pool); +    pa_assert(b); +    pa_assert(b->pool); -    assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0); -    assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length); +    pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0); +    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);      if (b->type == PA_MEMBLOCK_IMPORTED) { -        assert(pa_atomic_load(&b->pool->stat.n_imported) > 0); -        assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length); +        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); @@ -152,11 +197,12 @@ static void stat_remove(pa_memblock *b) {  static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length); +/* No lock necessary */  pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {      pa_memblock *b; -    assert(p); -    assert(length > 0); +    pa_assert(p); +    pa_assert(length > 0);      if (!(b = pa_memblock_new_pool(p, length)))          b = memblock_new_appended(p, length); @@ -164,56 +210,75 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {      return b;  } +/* No lock necessary */  static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {      pa_memblock *b; -    assert(p); -    assert(length > 0); +    pa_assert(p); +    pa_assert(length > 0); -    b = pa_xmalloc(sizeof(pa_memblock) + length); +    /* If -1 is passed as length we choose the size for the caller. */ + +    if (length == (size_t) -1) +        length = p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock)); + +    b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length); +    PA_REFCNT_INIT(b); +    b->pool = p;      b->type = PA_MEMBLOCK_APPENDED;      b->read_only = 0; -    PA_REFCNT_INIT(b); +    pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));      b->length = length; -    b->data = (uint8_t*) b + sizeof(pa_memblock); -    b->pool = p; +    pa_atomic_store(&b->n_acquired, 0); +    pa_atomic_store(&b->please_signal, 0);      stat_add(b);      return b;  } +/* No lock necessary */  static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {      struct mempool_slot *slot; -    assert(p); +    pa_assert(p); -    if (p->free_slots) { -        slot = p->free_slots; -        PA_LLIST_REMOVE(struct mempool_slot, p->free_slots, slot); -    } else if (p->n_init < p->n_blocks) -        slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * p->n_init++)); -    else { -        pa_log_debug("Pool full"); -        pa_atomic_inc(&p->stat.n_pool_full); -        return NULL; +    if (!(slot = pa_flist_pop(p->free_slots))) { +        int idx; + +        /* The free list was empty, we have to allocate a new entry */ + +        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)); + +        if (!slot) { +            pa_log_debug("Pool full"); +            pa_atomic_inc(&p->stat.n_pool_full); +            return NULL; +        }      }      return slot;  } +/* No lock necessary */  static void* mempool_slot_data(struct mempool_slot *slot) { -    assert(slot); +    pa_assert(slot); -    return (uint8_t*) slot + sizeof(struct mempool_slot); +    return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));  } +/* No lock necessary */  static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) { -    assert(p); -    assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr); -    assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size); +    pa_assert(p); + +    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;  } +/* No lock necessary */  static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {      unsigned idx; @@ -223,189 +288,321 @@ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {      return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));  } +/* No lock necessary */  pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {      pa_memblock *b = NULL;      struct mempool_slot *slot; -    assert(p); -    assert(length > 0); +    pa_assert(p); +    pa_assert(length > 0); + +    /* If -1 is passed as length we choose the size for the caller: we +     * take the largest size that fits in one of our slots. */ -    if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) { +    if (length == (size_t) -1) +        length = pa_mempool_block_size_max(p); + +    if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= PA_ALIGN(sizeof(pa_memblock)) + length) {          if (!(slot = mempool_allocate_slot(p)))              return NULL;          b = mempool_slot_data(slot);          b->type = PA_MEMBLOCK_POOL; -        b->data = (uint8_t*) b + sizeof(pa_memblock); +        pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); -    } else if (p->block_size - sizeof(struct mempool_slot) >= length) { +    } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {          if (!(slot = mempool_allocate_slot(p)))              return NULL; -        b = pa_xnew(pa_memblock, 1); +        if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) +            b = pa_xnew(pa_memblock, 1); +          b->type = PA_MEMBLOCK_POOL_EXTERNAL; -        b->data = mempool_slot_data(slot); +        pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); +      } else { -        pa_log_debug("Memory block too large for pool: %u > %u", length, p->block_size - sizeof(struct mempool_slot)); +        pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - PA_ALIGN(sizeof(struct mempool_slot))));          pa_atomic_inc(&p->stat.n_too_large_for_pool);          return NULL;      } -    b->length = length; -    b->read_only = 0;      PA_REFCNT_INIT(b);      b->pool = p; +    b->read_only = 0; +    b->length = length; +    pa_atomic_store(&b->n_acquired, 0); +    pa_atomic_store(&b->please_signal, 0);      stat_add(b);      return b;  } +/* No lock necessary */  pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {      pa_memblock *b; -    assert(p); -    assert(d); -    assert(length > 0); +    pa_assert(p); +    pa_assert(d); +    pa_assert(length != (size_t) -1); +    pa_assert(length > 0); -    b = pa_xnew(pa_memblock, 1); +    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) +        b = pa_xnew(pa_memblock, 1); +    PA_REFCNT_INIT(b); +    b->pool = p;      b->type = PA_MEMBLOCK_FIXED;      b->read_only = read_only; -    PA_REFCNT_INIT(b); +    pa_atomic_ptr_store(&b->data, d);      b->length = length; -    b->data = d; -    b->pool = p; +    pa_atomic_store(&b->n_acquired, 0); +    pa_atomic_store(&b->please_signal, 0);      stat_add(b);      return b;  } +/* No lock necessary */  pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {      pa_memblock *b; -    assert(p); -    assert(d); -    assert(length > 0); -    assert(free_cb); +    pa_assert(p); +    pa_assert(d); +    pa_assert(length > 0); +    pa_assert(length != (size_t) -1); +    pa_assert(free_cb); -    b = pa_xnew(pa_memblock, 1); +    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) +        b = pa_xnew(pa_memblock, 1); +    PA_REFCNT_INIT(b); +    b->pool = p;      b->type = PA_MEMBLOCK_USER;      b->read_only = read_only; -    PA_REFCNT_INIT(b); +    pa_atomic_ptr_store(&b->data, d);      b->length = length; -    b->data = d; +    pa_atomic_store(&b->n_acquired, 0); +    pa_atomic_store(&b->please_signal, 0); +      b->per_type.user.free_cb = free_cb; -    b->pool = p;      stat_add(b);      return b;  } +/* No lock necessary */ +int pa_memblock_is_read_only(pa_memblock *b) { +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    return b->read_only && PA_REFCNT_VALUE(b) == 1; +} + +/* No lock necessary */ +int pa_memblock_ref_is_one(pa_memblock *b) { +    int r; + +    pa_assert(b); + +    r = PA_REFCNT_VALUE(b); +    pa_assert(r > 0); + +    return r == 1; +} + +/* No lock necessary */ +void* pa_memblock_acquire(pa_memblock *b) { +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    pa_atomic_inc(&b->n_acquired); + +    return pa_atomic_ptr_load(&b->data); +} + +/* No lock necessary, in corner cases locks by its own */ +void pa_memblock_release(pa_memblock *b) { +    int r; +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    r = pa_atomic_dec(&b->n_acquired); +    pa_assert(r >= 1); + +    /* Signal a waiting thread that this memblock is no longer used */ +    if (r == 1 && pa_atomic_load(&b->please_signal)) +        pa_semaphore_post(b->pool->semaphore); +} + +size_t pa_memblock_get_length(pa_memblock *b) { +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    return b->length; +} + +pa_mempool* pa_memblock_get_pool(pa_memblock *b) { +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    return b->pool; +} + +/* No lock necessary */  pa_memblock* pa_memblock_ref(pa_memblock*b) { -    assert(b); -    assert(PA_REFCNT_VALUE(b) > 0); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0);      PA_REFCNT_INC(b);      return b;  } -void pa_memblock_unref(pa_memblock*b) { -    assert(b); -    assert(PA_REFCNT_VALUE(b) > 0); +static void memblock_free(pa_memblock *b) { +    pa_assert(b); -    if (PA_REFCNT_DEC(b) > 0) -        return; +    pa_assert(pa_atomic_load(&b->n_acquired) == 0);      stat_remove(b);      switch (b->type) {          case PA_MEMBLOCK_USER : -            assert(b->per_type.user.free_cb); -            b->per_type.user.free_cb(b->data); +            pa_assert(b->per_type.user.free_cb); +            b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));              /* Fall through */          case PA_MEMBLOCK_FIXED:          case PA_MEMBLOCK_APPENDED : -            pa_xfree(b); +            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) +                pa_xfree(b); +              break;          case PA_MEMBLOCK_IMPORTED : {              pa_memimport_segment *segment; +            pa_memimport *import; -            segment = b->per_type.imported.segment; -            assert(segment); -            assert(segment->import); +            /* FIXME! This should be implemented lock-free */ -            pa_hashmap_remove(segment->import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)); -            segment->import->release_cb(segment->import, b->per_type.imported.id, segment->import->userdata); +            segment = b->per_type.imported.segment; +            pa_assert(segment); +            import = segment->import; +            pa_assert(import); +            pa_mutex_lock(import->mutex); +            pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));              if (-- segment->n_blocks <= 0)                  segment_detach(segment); -            pa_xfree(b); +            pa_mutex_unlock(import->mutex); + +            import->release_cb(import, b->per_type.imported.id, import->userdata); + +            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) +                pa_xfree(b);              break;          }          case PA_MEMBLOCK_POOL_EXTERNAL:          case PA_MEMBLOCK_POOL: {              struct mempool_slot *slot; +            int call_free; -            slot = mempool_slot_by_ptr(b->pool, b->data); -            assert(slot); +            slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)); +            pa_assert(slot); -            PA_LLIST_PREPEND(struct mempool_slot, b->pool->free_slots, slot); +            call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL; -            if (b->type == PA_MEMBLOCK_POOL_EXTERNAL) -                pa_xfree(b); +            /* The free list dimensions should easily allow all slots +             * to fit in, hence try harder if pushing this slot into +             * the free list fails */ +            while (pa_flist_push(b->pool->free_slots, slot) < 0) +                ; + +            if (call_free) +                if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) +                    pa_xfree(b);              break;          }          case PA_MEMBLOCK_TYPE_MAX:          default: -            abort(); +            pa_assert_not_reached();      }  } +/* No lock necessary */ +void pa_memblock_unref(pa_memblock*b) { +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    if (PA_REFCNT_DEC(b) > 0) +        return; + +    memblock_free(b); +} + +/* Self locked */ +static void memblock_wait(pa_memblock *b) { +    pa_assert(b); + +    if (pa_atomic_load(&b->n_acquired) > 0) { +        /* We need to wait until all threads gave up access to the +         * memory block before we can go on. Unfortunately this means +         * that we have to lock and wait here. Sniff! */ + +        pa_atomic_inc(&b->please_signal); + +        while (pa_atomic_load(&b->n_acquired) > 0) +            pa_semaphore_wait(b->pool->semaphore); + +        pa_atomic_dec(&b->please_signal); +    } +} + +/* No lock necessary. This function is not multiple caller safe! */  static void memblock_make_local(pa_memblock *b) { -    assert(b); +    pa_assert(b);      pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); -    if (b->length <= b->pool->block_size - sizeof(struct mempool_slot)) { +    if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {          struct mempool_slot *slot;          if ((slot = mempool_allocate_slot(b->pool))) {              void *new_data;              /* We can move it into a local pool, perfect! */ +            new_data = mempool_slot_data(slot); +            memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length); +            pa_atomic_ptr_store(&b->data, new_data); +              b->type = PA_MEMBLOCK_POOL_EXTERNAL;              b->read_only = 0; -            new_data = mempool_slot_data(slot); -            memcpy(new_data, b->data, b->length); -            b->data = new_data;              goto finish;          }      }      /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */ -    b->type = PA_MEMBLOCK_USER;      b->per_type.user.free_cb = pa_xfree; +    pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length)); + +    b->type = PA_MEMBLOCK_USER;      b->read_only = 0; -    b->data = pa_xmemdup(b->data, b->length);  finish:      pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);      pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); +    memblock_wait(b);  } +/* No lock necessary. This function is not multiple caller safe*/  void pa_memblock_unref_fixed(pa_memblock *b) { -    assert(b); -    assert(PA_REFCNT_VALUE(b) > 0); -    assert(b->type == PA_MEMBLOCK_FIXED); +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); +    pa_assert(b->type == PA_MEMBLOCK_FIXED);      if (PA_REFCNT_VALUE(b) > 1)          memblock_make_local(b); @@ -413,20 +610,37 @@ void pa_memblock_unref_fixed(pa_memblock *b) {      pa_memblock_unref(b);  } +/* No lock necessary. */ +pa_memblock *pa_memblock_will_need(pa_memblock *b) { +    void *p; + +    pa_assert(b); +    pa_assert(PA_REFCNT_VALUE(b) > 0); + +    p = pa_memblock_acquire(b); +    pa_will_need(p, b->length); +    pa_memblock_release(b); + +    return b; +} + +/* Self-locked. This function is not multiple-caller safe */  static void memblock_replace_import(pa_memblock *b) {      pa_memimport_segment *seg; -    assert(b); -    assert(b->type == PA_MEMBLOCK_IMPORTED); +    pa_assert(b); +    pa_assert(b->type == PA_MEMBLOCK_IMPORTED); -    assert(pa_atomic_load(&b->pool->stat.n_imported) > 0); -    assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length); +    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);      seg = b->per_type.imported.segment; -    assert(seg); -    assert(seg->import); +    pa_assert(seg); +    pa_assert(seg->import); + +    pa_mutex_lock(seg->import->mutex);      pa_hashmap_remove(              seg->import->blocks, @@ -434,51 +648,49 @@ static void memblock_replace_import(pa_memblock *b) {      memblock_make_local(b); -    if (-- seg->n_blocks <= 0) +    if (-- seg->n_blocks <= 0) { +        pa_mutex_unlock(seg->import->mutex);          segment_detach(seg); +    } else +        pa_mutex_unlock(seg->import->mutex);  }  pa_mempool* pa_mempool_new(int shared) { -    size_t ps;      pa_mempool *p;      p = pa_xnew(pa_mempool, 1); -#ifdef HAVE_SYSCONF -    ps = (size_t) sysconf(_SC_PAGESIZE); -#elif defined(PAGE_SIZE) -	ps = (size_t) PAGE_SIZE; -#else -	ps = 4096; /* Let's hope it's like x86. */ -#endif - -    p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps; +    p->mutex = pa_mutex_new(TRUE, TRUE); +    p->semaphore = pa_semaphore_new(0); -    if (p->block_size < ps) -        p->block_size = ps; +    p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); +    if (p->block_size < PA_PAGE_SIZE) +        p->block_size = PA_PAGE_SIZE;      p->n_blocks = PA_MEMPOOL_SLOTS_MAX; -    assert(p->block_size > sizeof(struct mempool_slot)); +    pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));      if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {          pa_xfree(p);          return NULL;      } -    p->n_init = 0; +    memset(&p->stat, 0, sizeof(p->stat)); +    pa_atomic_store(&p->n_init, 0);      PA_LLIST_HEAD_INIT(pa_memimport, p->imports);      PA_LLIST_HEAD_INIT(pa_memexport, p->exports); -    PA_LLIST_HEAD_INIT(struct mempool_slot, p->free_slots); -    memset(&p->stat, 0, sizeof(p->stat)); +    p->free_slots = pa_flist_new(p->n_blocks*2);      return p;  }  void pa_mempool_free(pa_mempool *p) { -    assert(p); +    pa_assert(p); + +    pa_mutex_lock(p->mutex);      while (p->imports)          pa_memimport_free(p->imports); @@ -486,30 +698,65 @@ void pa_mempool_free(pa_mempool *p) {      while (p->exports)          pa_memexport_free(p->exports); -    if (pa_atomic_load(&p->stat.n_allocated) > 0) -        pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!"); +    pa_mutex_unlock(p->mutex); + +    pa_flist_free(p->free_slots, NULL); + +    if (pa_atomic_load(&p->stat.n_allocated) > 0) { +/*         raise(SIGTRAP);  */ +        pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated)); +    }      pa_shm_free(&p->memory); + +    pa_mutex_free(p->mutex); +    pa_semaphore_free(p->semaphore); +      pa_xfree(p);  } +/* No lock necessary */  const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) { -    assert(p); +    pa_assert(p);      return &p->stat;  } +/* No lock necessary */ +size_t pa_mempool_block_size_max(pa_mempool *p) { +    pa_assert(p); + +    return p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock)); +} + +/* No lock necessary */  void pa_mempool_vacuum(pa_mempool *p) {      struct mempool_slot *slot; +    pa_flist *list; + +    pa_assert(p); + +    list = pa_flist_new(p->n_blocks*2); -    assert(p); +    while ((slot = pa_flist_pop(p->free_slots))) +        while (pa_flist_push(list, slot) < 0) +            ; -    for (slot = p->free_slots; slot; slot = slot->next) -        pa_shm_punch(&p->memory, (uint8_t*) slot + sizeof(struct mempool_slot) - (uint8_t*) p->memory.ptr, p->block_size - sizeof(struct mempool_slot)); +    while ((slot = pa_flist_pop(list))) { +        pa_shm_punch(&p->memory, +                     (uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)), +                     p->block_size - PA_ALIGN(sizeof(struct mempool_slot))); + +        while (pa_flist_push(p->free_slots, slot)) +            ; +    } + +    pa_flist_free(list, NULL);  } +/* No lock necessary */  int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) { -    assert(p); +    pa_assert(p);      if (!p->memory.shared)          return -1; @@ -519,8 +766,9 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {      return 0;  } +/* No lock necessary */  int pa_mempool_is_shared(pa_mempool *p) { -    assert(p); +    pa_assert(p);      return !!p->memory.shared;  } @@ -529,22 +777,27 @@ int pa_mempool_is_shared(pa_mempool *p) {  pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {      pa_memimport *i; -    assert(p); -    assert(cb); +    pa_assert(p); +    pa_assert(cb);      i = pa_xnew(pa_memimport, 1); +    i->mutex = pa_mutex_new(TRUE, TRUE);      i->pool = p;      i->segments = pa_hashmap_new(NULL, NULL);      i->blocks = pa_hashmap_new(NULL, NULL);      i->release_cb = cb;      i->userdata = userdata; +    pa_mutex_lock(p->mutex);      PA_LLIST_PREPEND(pa_memimport, p->imports, i); +    pa_mutex_unlock(p->mutex); +      return i;  }  static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i); +/* Should be called locked */  static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {      pa_memimport_segment* seg; @@ -565,59 +818,79 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {      return seg;  } +/* Should be called locked */  static void segment_detach(pa_memimport_segment *seg) { -    assert(seg); +    pa_assert(seg);      pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));      pa_shm_free(&seg->memory);      pa_xfree(seg);  } +/* Self-locked. Not multiple-caller safe */  void pa_memimport_free(pa_memimport *i) {      pa_memexport *e;      pa_memblock *b; -    assert(i); +    pa_assert(i); + +    pa_mutex_lock(i->mutex); + +    while ((b = pa_hashmap_get_first(i->blocks))) +        memblock_replace_import(b); + +    pa_assert(pa_hashmap_size(i->segments) == 0); + +    pa_mutex_unlock(i->mutex); + +    pa_mutex_lock(i->pool->mutex);      /* If we've exported this block further we need to revoke that export */      for (e = i->pool->exports; e; e = e->next)          memexport_revoke_blocks(e, i); -    while ((b = pa_hashmap_get_first(i->blocks))) -        memblock_replace_import(b); +    PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i); -    assert(pa_hashmap_size(i->segments) == 0); +    pa_mutex_unlock(i->pool->mutex);      pa_hashmap_free(i->blocks, NULL, NULL);      pa_hashmap_free(i->segments, NULL, NULL); -    PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i); +    pa_mutex_free(i->mutex); +      pa_xfree(i);  } +/* Self-locked */  pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) { -    pa_memblock *b; +    pa_memblock *b = NULL;      pa_memimport_segment *seg; -    assert(i); +    pa_assert(i); + +    pa_mutex_lock(i->mutex);      if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX) -        return NULL; +        goto finish;      if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))          if (!(seg = segment_attach(i, shm_id))) -            return NULL; +            goto finish;      if (offset+size > seg->memory.size) -        return NULL; +        goto finish; -    b = pa_xnew(pa_memblock, 1); +    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) +        b = pa_xnew(pa_memblock, 1); + +    PA_REFCNT_INIT(b); +    b->pool = i->pool;      b->type = PA_MEMBLOCK_IMPORTED;      b->read_only = 1; -    PA_REFCNT_INIT(b); +    pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);      b->length = size; -    b->data = (uint8_t*) seg->memory.ptr + offset; -    b->pool = i->pool; +    pa_atomic_store(&b->n_acquired, 0); +    pa_atomic_store(&b->please_signal, 0);      b->per_type.imported.id = block_id;      b->per_type.imported.segment = seg; @@ -625,6 +898,10 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i      seg->n_blocks++; +finish: +    pa_mutex_unlock(i->mutex); + +    if (b)      stat_add(b);      return b; @@ -632,26 +909,36 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i  int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {      pa_memblock *b; -    assert(i); +    int ret = 0; +    pa_assert(i); -    if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) -        return -1; +    pa_mutex_lock(i->mutex); + +    if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) { +        ret = -1; +        goto finish; +    }      memblock_replace_import(b); -    return 0; + +finish: +    pa_mutex_unlock(i->mutex); + +    return ret;  }  /* For sending blocks to other nodes */  pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {      pa_memexport *e; -    assert(p); -    assert(cb); +    pa_assert(p); +    pa_assert(cb);      if (!p->memory.shared)          return NULL;      e = pa_xnew(pa_memexport, 1); +    e->mutex = pa_mutex_new(TRUE, TRUE);      e->pool = p;      PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);      PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots); @@ -659,50 +946,75 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void      e->revoke_cb = cb;      e->userdata = userdata; +    pa_mutex_lock(p->mutex);      PA_LLIST_PREPEND(pa_memexport, p->exports, e); +    pa_mutex_unlock(p->mutex);      return e;  }  void pa_memexport_free(pa_memexport *e) { -    assert(e); +    pa_assert(e); +    pa_mutex_lock(e->mutex);      while (e->used_slots)          pa_memexport_process_release(e, e->used_slots - e->slots); +    pa_mutex_unlock(e->mutex); +    pa_mutex_lock(e->pool->mutex);      PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e); +    pa_mutex_unlock(e->pool->mutex); + +    pa_mutex_free(e->mutex);      pa_xfree(e);  } +/* Self-locked */  int pa_memexport_process_release(pa_memexport *e, uint32_t id) { -    assert(e); +    pa_memblock *b; + +    pa_assert(e); + +    pa_mutex_lock(e->mutex);      if (id >= e->n_init) -        return -1; +        goto fail;      if (!e->slots[id].block) -        return -1; +        goto fail; + +    b = e->slots[id].block; +    e->slots[id].block = NULL; + +    PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]); +    PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]); + +    pa_mutex_unlock(e->mutex);  /*     pa_log("Processing release for %u", id); */ -    assert(pa_atomic_load(&e->pool->stat.n_exported) > 0); -    assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) e->slots[id].block->length); +    pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0); +    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, e->slots[id].block->length); +    pa_atomic_sub(&e->pool->stat.exported_size, b->length); -    pa_memblock_unref(e->slots[id].block); -    e->slots[id].block = NULL; - -    PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]); -    PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]); +    pa_memblock_unref(b);      return 0; + +fail: +    pa_mutex_unlock(e->mutex); + +    return -1;  } +/* Self-locked */  static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {      struct memexport_slot *slot, *next; -    assert(e); -    assert(i); +    pa_assert(e); +    pa_assert(i); + +    pa_mutex_lock(e->mutex);      for (slot = e->used_slots; slot; slot = next) {          uint32_t idx; @@ -716,49 +1028,57 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {          e->revoke_cb(e, idx, e->userdata);          pa_memexport_process_release(e, idx);      } + +    pa_mutex_unlock(e->mutex);  } +/* No lock necessary */  static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {      pa_memblock *n; -    assert(p); -    assert(b); +    pa_assert(p); +    pa_assert(b);      if (b->type == PA_MEMBLOCK_IMPORTED ||          b->type == PA_MEMBLOCK_POOL ||          b->type == PA_MEMBLOCK_POOL_EXTERNAL) { -        assert(b->pool == p); +        pa_assert(b->pool == p);          return pa_memblock_ref(b);      }      if (!(n = pa_memblock_new_pool(p, b->length)))          return NULL; -    memcpy(n->data, b->data, b->length); +    memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);      return n;  } +/* Self-locked */  int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {      pa_shm *memory;      struct memexport_slot *slot; +    void *data; -    assert(e); -    assert(b); -    assert(block_id); -    assert(shm_id); -    assert(offset); -    assert(size); -    assert(b->pool == e->pool); +    pa_assert(e); +    pa_assert(b); +    pa_assert(block_id); +    pa_assert(shm_id); +    pa_assert(offset); +    pa_assert(size); +    pa_assert(b->pool == e->pool);      if (!(b = memblock_shared_copy(e->pool, b)))          return -1; +    pa_mutex_lock(e->mutex); +      if (e->free_slots) {          slot = e->free_slots;          PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot); -    } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) { +    } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)          slot = &e->slots[e->n_init++]; -    } else { +    else { +        pa_mutex_unlock(e->mutex);          pa_memblock_unref(b);          return -1;      } @@ -767,24 +1087,29 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32      slot->block = b;      *block_id = slot - e->slots; +    pa_mutex_unlock(e->mutex);  /*     pa_log("Got block id %u", *block_id); */ +    data = pa_memblock_acquire(b); +      if (b->type == PA_MEMBLOCK_IMPORTED) { -        assert(b->per_type.imported.segment); +        pa_assert(b->per_type.imported.segment);          memory = &b->per_type.imported.segment->memory;      } else { -        assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL); -        assert(b->pool); +        pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL); +        pa_assert(b->pool);          memory = &b->pool->memory;      } -    assert(b->data >= memory->ptr); -    assert((uint8_t*) b->data + b->length <= (uint8_t*) memory->ptr + memory->size); +    pa_assert(data >= memory->ptr); +    pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);      *shm_id = memory->id; -    *offset = (uint8_t*) b->data - (uint8_t*) memory->ptr; +    *offset = (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); diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index fe4773d4..c704014a 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -28,6 +28,7 @@  #include <sys/types.h>  #include <inttypes.h> +#include <pulse/def.h>  #include <pulsecore/llist.h>  #include <pulsecore/refcnt.h>  #include <pulsecore/atomic.h> @@ -58,45 +59,25 @@ typedef struct pa_memexport pa_memexport;  typedef void (*pa_memimport_release_cb_t)(pa_memimport *i, uint32_t block_id, void *userdata);  typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, void *userdata); -struct pa_memblock { -    pa_memblock_type_t type; -    int read_only; /* boolean */ -    PA_REFCNT_DECLARE; /* the reference counter */ -    size_t length; -    void *data; -    pa_mempool *pool; - -    union { -        struct { -            void (*free_cb)(void *p);  /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ -        } user; - -        struct  { -            uint32_t id; -            pa_memimport_segment *segment; -        } imported; -    } per_type; -}; -  /* Please note that updates to this structure are not locked,   * i.e. n_allocated might be updated at a point in time where   * n_accumulated is not yet. Take these values with a grain of salt, - * threy are here for purely statistical reasons.*/ + * they are here for purely statistical reasons.*/  struct pa_mempool_stat { -    pa_atomic_int_t n_allocated; -    pa_atomic_int_t n_accumulated; -    pa_atomic_int_t n_imported; -    pa_atomic_int_t n_exported; -    pa_atomic_int_t allocated_size; -    pa_atomic_int_t accumulated_size; -    pa_atomic_int_t imported_size; -    pa_atomic_int_t exported_size; - -    pa_atomic_int_t n_too_large_for_pool; -    pa_atomic_int_t n_pool_full; - -    pa_atomic_int_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX]; -    pa_atomic_int_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX]; +    pa_atomic_t n_allocated; +    pa_atomic_t n_accumulated; +    pa_atomic_t n_imported; +    pa_atomic_t n_exported; +    pa_atomic_t allocated_size; +    pa_atomic_t accumulated_size; +    pa_atomic_t imported_size; +    pa_atomic_t exported_size; + +    pa_atomic_t n_too_large_for_pool; +    pa_atomic_t n_pool_full; + +    pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX]; +    pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];  };  /* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */ @@ -120,9 +101,20 @@ pa_memblock* pa_memblock_ref(pa_memblock*b);  /* This special unref function has to be called by the owner of the  memory of a static memory block when he wants to release all  references to the memory. This causes the memory to be copied and -converted into a PA_MEMBLOCK_DYNAMIC type memory block */ +converted into a pool or malloc'ed memory block. Please note that this +function is not multiple caller safe, i.e. needs to be locked +manually if called from more than one thread at the same time.  */  void pa_memblock_unref_fixed(pa_memblock*b); +int pa_memblock_is_read_only(pa_memblock *b); +int pa_memblock_ref_is_one(pa_memblock *b); +void* pa_memblock_acquire(pa_memblock *b); +void pa_memblock_release(pa_memblock *b); +size_t pa_memblock_get_length(pa_memblock *b); +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(int shared);  void pa_mempool_free(pa_mempool *p); @@ -130,6 +122,7 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);  void pa_mempool_vacuum(pa_mempool *p);  int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);  int pa_mempool_is_shared(pa_mempool *p); +size_t pa_mempool_block_size_max(pa_mempool *p);  /* For recieving blocks from other nodes */  pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata); diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index e31fb6df..a46155a9 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -28,7 +28,6 @@  #include <sys/time.h>  #include <time.h>  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -36,23 +35,29 @@  #include <pulsecore/log.h>  #include <pulsecore/mcalign.h> +#include <pulsecore/macro.h> +#include <pulsecore/flist.h>  #include "memblockq.h" -struct memblock_list { -    struct memblock_list *next, *prev; +struct list_item { +    struct list_item *next, *prev;      int64_t index;      pa_memchunk chunk;  }; +PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree); +  struct pa_memblockq { -    struct memblock_list *blocks, *blocks_tail; +    struct list_item *blocks, *blocks_tail;      unsigned n_blocks;      size_t maxlength, tlength, base, prebuf, minreq;      int64_t read_index, write_index; -    enum { PREBUF, RUNNING } state; +    pa_bool_t in_prebuf;      pa_memblock *silence;      pa_mcalign *mcalign; +    int64_t missing; +    size_t requested;  };  pa_memblockq* pa_memblockq_new( @@ -66,8 +71,8 @@ pa_memblockq* pa_memblockq_new(      pa_memblockq* bq; -    assert(base > 0); -    assert(maxlength >= base); +    pa_assert(base > 0); +    pa_assert(maxlength >= base);      bq = pa_xnew(pa_memblockq, 1);      bq->blocks = bq->blocks_tail = NULL; @@ -77,13 +82,13 @@ pa_memblockq* pa_memblockq_new(      bq->read_index = bq->write_index = idx;      pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu", -        (unsigned long)maxlength, (unsigned long)tlength, (unsigned long)base, (unsigned long)prebuf, (unsigned long)minreq); +        (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq);      bq->maxlength = ((maxlength+base-1)/base)*base; -    assert(bq->maxlength >= base); +    pa_assert(bq->maxlength >= base);      bq->tlength = ((tlength+base-1)/base)*base; -    if (!bq->tlength || bq->tlength >= bq->maxlength) +    if (bq->tlength <= 0 || bq->tlength > bq->maxlength)          bq->tlength = bq->maxlength;      bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf; @@ -102,15 +107,18 @@ pa_memblockq* pa_memblockq_new(      pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",          (unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq); -    bq->state = bq->prebuf ? PREBUF : RUNNING; +    bq->in_prebuf = bq->prebuf > 0;      bq->silence = silence ? pa_memblock_ref(silence) : NULL;      bq->mcalign = NULL; +    bq->missing = bq->tlength; +    bq->requested = 0; +      return bq;  }  void pa_memblockq_free(pa_memblockq* bq) { -    assert(bq); +    pa_assert(bq);      pa_memblockq_flush(bq); @@ -123,11 +131,11 @@ void pa_memblockq_free(pa_memblockq* bq) {      pa_xfree(bq);  } -static void drop_block(pa_memblockq *bq, struct memblock_list *q) { -    assert(bq); -    assert(q); +static void drop_block(pa_memblockq *bq, struct list_item *q) { +    pa_assert(bq); +    pa_assert(q); -    assert(bq->n_blocks >= 1); +    pa_assert(bq->n_blocks >= 1);      if (q->prev)          q->prev->next = q->next; @@ -140,15 +148,17 @@ static void drop_block(pa_memblockq *bq, struct memblock_list *q) {          bq->blocks_tail = q->prev;      pa_memblock_unref(q->chunk.memblock); -    pa_xfree(q); + +    if (pa_flist_push(PA_STATIC_FLIST_GET(list_items), q) < 0) +        pa_xfree(q);      bq->n_blocks--;  } -static int can_push(pa_memblockq *bq, size_t l) { +static pa_bool_t can_push(pa_memblockq *bq, size_t l) {      int64_t end; -    assert(bq); +    pa_assert(bq);      if (bq->read_index > bq->write_index) {          size_t d =  bq->read_index - bq->write_index; @@ -156,7 +166,7 @@ static int can_push(pa_memblockq *bq, size_t l) {          if (l > d)              l -= d;          else -            return 1; +            return TRUE;      }      end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0; @@ -164,21 +174,21 @@ static int 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) -            return 0; +            return FALSE; -    return 1; +    return TRUE;  }  int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { - -    struct memblock_list *q, *n; +    struct list_item *q, *n;      pa_memchunk chunk; +    int64_t old, delta; -    assert(bq); -    assert(uchunk); -    assert(uchunk->memblock); -    assert(uchunk->length > 0); -    assert(uchunk->index + uchunk->length <= uchunk->memblock->length); +    pa_assert(bq); +    pa_assert(uchunk); +    pa_assert(uchunk->memblock); +    pa_assert(uchunk->length > 0); +    pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));      if (uchunk->length % bq->base)          return -1; @@ -186,6 +196,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {      if (!can_push(bq, uchunk->length))          return -1; +    old = bq->write_index;      chunk = *uchunk;      if (bq->read_index > bq->write_index) { @@ -198,11 +209,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {          if (chunk.length > d) {              chunk.index += d;              chunk.length -= d; -            bq->write_index = bq->read_index; +            bq->write_index += d;          } else {              /* We drop the incoming data completely */              bq->write_index += chunk.length; -            return 0; +            goto finish;          }      } @@ -212,10 +223,10 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {      q = bq->blocks_tail;      while (q) { -        if (bq->write_index >= q->index + (int64_t)q->chunk.length) +        if (bq->write_index >= q->index + (int64_t) q->chunk.length)              /* We found the entry where we need to place the new entry immediately after */              break; -        else if (bq->write_index + (int64_t)chunk.length <= q->index) { +        else if (bq->write_index + (int64_t) chunk.length <= q->index) {              /* This entry isn't touched at all, let's skip it */              q = q->prev;          } else if (bq->write_index <= q->index && @@ -223,7 +234,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              /* This entry is fully replaced by the new entry, so let's drop it */ -            struct memblock_list *p; +            struct list_item *p;              p = q;              q = q->prev;              drop_block(bq, p); @@ -234,17 +245,19 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              if (bq->write_index + chunk.length < q->index + q->chunk.length) {                  /* We need to save the end of this memchunk */ -                struct memblock_list *p; +                struct list_item *p;                  size_t d;                  /* Create a new list entry for the end of thie memchunk */ -                p = pa_xnew(struct memblock_list, 1); +                if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items)))) +                    p = pa_xnew(struct list_item, 1); +                  p->chunk = q->chunk;                  pa_memblock_ref(p->chunk.memblock);                  /* Calculate offset */                  d = bq->write_index + chunk.length - q->index; -                assert(d > 0); +                pa_assert(d > 0);                  /* Drop it from the new entry */                  p->index = q->index + d; @@ -263,7 +276,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              /* Truncate the chunk */              if (!(q->chunk.length = bq->write_index - q->index)) { -                struct memblock_list *p; +                struct list_item *p;                  p = q;                  q = q->prev;                  drop_block(bq, p); @@ -274,7 +287,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {          } else {              size_t d; -            assert(bq->write_index + (int64_t)chunk.length > q->index && +            pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&                     bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&                     bq->write_index < q->index); @@ -287,12 +300,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              q = q->prev;          } -      }      if (q) { -        assert(bq->write_index >=  q->index + (int64_t)q->chunk.length); -        assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index)); +        pa_assert(bq->write_index >=  q->index + (int64_t)q->chunk.length); +        pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));          /* Try to merge memory blocks */ @@ -302,13 +314,14 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              q->chunk.length += chunk.length;              bq->write_index += chunk.length; -            return 0; +            goto finish;          }      } else -        assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index)); +        pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index)); +    if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items)))) +        n = pa_xnew(struct list_item, 1); -    n = pa_xnew(struct memblock_list, 1);      n->chunk = chunk;      pa_memblock_ref(n->chunk.memblock);      n->index = bq->write_index; @@ -328,27 +341,52 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {          bq->blocks = n;      bq->n_blocks++; + +finish: + +    delta = bq->write_index - old; + +    if (delta >= bq->requested) { +        delta -= bq->requested; +        bq->requested = 0; +    } else { +        bq->requested -= delta; +        delta = 0; +    } + +    bq->missing -= delta; +      return 0;  } -int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { -    assert(bq); -    assert(chunk); +static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) { +    pa_assert(bq); -    if (bq->state == PREBUF) { +    if (bq->in_prebuf) { -        /* We need to pre-buffer */          if (pa_memblockq_get_length(bq) < bq->prebuf) -            return -1; +            return TRUE; -        bq->state = RUNNING; +        bq->in_prebuf = FALSE; +        return FALSE; +    } else { -    } else if (bq->prebuf > 0 && bq->read_index >= bq->write_index) { +        if (bq->prebuf > 0 && bq->read_index >= bq->write_index) { +            bq->in_prebuf = TRUE; +            return TRUE; +        } -        /* Buffer underflow protection */ -        bq->state = PREBUF; -        return -1; +        return FALSE;      } +} + +int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { +    pa_assert(bq); +    pa_assert(chunk); + +    /* We need to pre-buffer */ +    if (memblockq_check_prebuf(bq)) +        return -1;      /* Do we need to spit out silence? */      if (!bq->blocks || bq->blocks->index > bq->read_index) { @@ -362,8 +400,8 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {          if (bq->silence) {              chunk->memblock = pa_memblock_ref(bq->silence); -            if (!length || length > chunk->memblock->length) -                length = chunk->memblock->length; +            if (!length || length > pa_memblock_get_length(chunk->memblock)) +                length = pa_memblock_get_length(chunk->memblock);              chunk->length = length;          } else { @@ -382,7 +420,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {      }      /* Ok, let's pass real data to the caller */ -    assert(bq->blocks->index == bq->read_index); +    pa_assert(bq->blocks->index == bq->read_index);      *chunk = bq->blocks->chunk;      pa_memblock_ref(chunk->memblock); @@ -390,48 +428,23 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {      return 0;  } -void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) { -    assert(bq); -    assert(length % bq->base == 0); - -    assert(!chunk || length <= chunk->length); +void pa_memblockq_drop(pa_memblockq *bq, size_t length) { +    int64_t old, delta; +    pa_assert(bq); +    pa_assert(length % bq->base == 0); -    if (chunk) { - -        if (bq->blocks && bq->blocks->index == bq->read_index) { -            /* The first item in queue is valid */ - -            /* Does the chunk match with what the user supplied us? */ -            if (memcmp(chunk, &bq->blocks->chunk, sizeof(pa_memchunk)) != 0) -                return; - -        } else { -            size_t l; - -            /* The first item in the queue is not yet relevant */ - -            assert(!bq->blocks || bq->blocks->index > bq->read_index); -            l = bq->blocks ? bq->blocks->index - bq->read_index : 0; - -            if (bq->silence) { - -                if (!l || l > bq->silence->length) -                    l = bq->silence->length; - -            } - -            /* Do the entries still match? */ -            if (chunk->index != 0 || chunk->length != l || chunk->memblock != bq->silence) -                return; -        } -    } +    old = bq->read_index;      while (length > 0) { +        /* Do not drop any data when we are in prebuffering mode */ +        if (memblockq_check_prebuf(bq)) +            break; +          if (bq->blocks) {              size_t d; -            assert(bq->blocks->index >= bq->read_index); +            pa_assert(bq->blocks->index >= bq->read_index);              d = (size_t) (bq->blocks->index - bq->read_index); @@ -446,7 +459,7 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length                  bq->read_index += d;              } -            assert(bq->blocks->index == bq->read_index); +            pa_assert(bq->blocks->index == bq->read_index);              if (bq->blocks->chunk.length <= length) {                  /* We need to drop the full block */ @@ -472,35 +485,25 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length              break;          }      } + +    delta = bq->read_index - old; +    bq->missing += delta;  }  int pa_memblockq_is_readable(pa_memblockq *bq) { -    assert(bq); - -    if (bq->prebuf > 0) { -        size_t l = pa_memblockq_get_length(bq); - -        if (bq->state == PREBUF && l < bq->prebuf) -            return 0; - -        if (l <= 0) -            return 0; -    } - -    return 1; -} +    pa_assert(bq); -int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) { -    assert(bq); +    if (memblockq_check_prebuf(bq)) +        return 0; -    if (length % bq->base) +    if (pa_memblockq_get_length(bq) <= 0)          return 0; -    return pa_memblockq_get_length(bq) + length <= bq->tlength; +    return 1;  }  size_t pa_memblockq_get_length(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      if (bq->write_index <= bq->read_index)          return 0; @@ -510,76 +513,106 @@ size_t pa_memblockq_get_length(pa_memblockq *bq) {  size_t pa_memblockq_missing(pa_memblockq *bq) {      size_t l; -    assert(bq); +    pa_assert(bq);      if ((l = pa_memblockq_get_length(bq)) >= bq->tlength)          return 0;      l = bq->tlength - l; -    return (l >= bq->minreq) ? l : 0; + +    return l >= bq->minreq ? l : 0;  }  size_t pa_memblockq_get_minreq(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->minreq;  }  void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) { -    assert(bq); +    int64_t old, delta; +    pa_assert(bq); + +    old = bq->write_index;      switch (seek) {          case PA_SEEK_RELATIVE:              bq->write_index += offset; -            return; +            break;          case PA_SEEK_ABSOLUTE:              bq->write_index = offset; -            return; +            break;          case PA_SEEK_RELATIVE_ON_READ:              bq->write_index = bq->read_index + offset; -            return; +            break;          case PA_SEEK_RELATIVE_END: -            bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t)bq->blocks_tail->chunk.length : bq->read_index) + offset; -            return; +            bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset; +            break; +        default: +            pa_assert_not_reached();      } -    assert(0); +    delta = bq->write_index - old; + +    if (delta >= bq->requested) { +        delta -= bq->requested; +        bq->requested = 0; +    } else if (delta >= 0) { +        bq->requested -= delta; +        delta = 0; +    } + +    bq->missing -= delta;  }  void pa_memblockq_flush(pa_memblockq *bq) { -    assert(bq); +    int64_t old, delta; +    pa_assert(bq);      while (bq->blocks)          drop_block(bq, bq->blocks); -    assert(bq->n_blocks == 0); +    pa_assert(bq->n_blocks == 0); +    old = bq->write_index;      bq->write_index = bq->read_index;      pa_memblockq_prebuf_force(bq); + +    delta = bq->write_index - old; + +    if (delta > bq->requested) { +        delta -= bq->requested; +        bq->requested = 0; +    } else if (delta >= 0) { +        bq->requested -= delta; +        delta = 0; +    } + +    bq->missing -= delta;  }  size_t pa_memblockq_get_tlength(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->tlength;  }  int64_t pa_memblockq_get_read_index(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->read_index;  }  int64_t pa_memblockq_get_write_index(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->write_index;  }  int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {      pa_memchunk rchunk; -    assert(bq); -    assert(chunk && bq->base); +    pa_assert(bq); +    pa_assert(chunk);      if (bq->base == 1)          return pa_memblockq_push(bq, chunk); @@ -606,36 +639,52 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {  void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {      size_t l; -    assert(bq); +    pa_assert(bq);      l = pa_memblockq_get_length(bq);      if (l > length) -        pa_memblockq_drop(bq, NULL, l - length); +        pa_memblockq_drop(bq, l - length);  }  void pa_memblockq_prebuf_disable(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq); -    if (bq->state == PREBUF) -        bq->state = RUNNING; +    bq->in_prebuf = FALSE;  }  void pa_memblockq_prebuf_force(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq); -    if (bq->state == RUNNING && bq->prebuf > 0) -        bq->state = PREBUF; +    if (!bq->in_prebuf && bq->prebuf > 0) +        bq->in_prebuf = TRUE;  }  size_t pa_memblockq_get_maxlength(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->maxlength;  }  size_t pa_memblockq_get_prebuf(pa_memblockq *bq) { -    assert(bq); +    pa_assert(bq);      return bq->prebuf;  } + +size_t pa_memblockq_pop_missing(pa_memblockq *bq) { +    size_t l; + +    pa_assert(bq); + +/*     pa_log("pop: %lli", bq->missing); */ + +    if (bq->missing <= 0) +        return 0; + +    l = (size_t) bq->missing; +    bq->missing = 0; +    bq->requested += l; + +    return l; +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 437c5a41..8c3e70fc 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -62,7 +62,7 @@ typedef struct pa_memblockq pa_memblockq;     - minreq:    pa_memblockq_missing() will only return values greater                  than this value. Pass 0 for the default. -   - silence:   return this memblock whzen reading unitialized data +   - silence:   return this memblock when reading unitialized data  */  pa_memblockq* pa_memblockq_new(          int64_t idx, @@ -83,26 +83,30 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);   * you know what you do. */  int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk); -/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ +/* Return a copy of the next memory chunk in the queue. It is not + * removed from the queue. There are two reasons this function might + * fail: 1. prebuffering is active, 2. queue is empty and no silence + * memblock was passed at initialization. If the queue is not empty, + * but we're currently at a hole in the queue and no silence memblock + * was passed we return the length of the hole in chunk->length. */  int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk); -/* Drop the specified bytes from the queue, but only if the first - * chunk in the queue matches the one passed here. If NULL is passed, - * this check isn't done. */ -void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length); +/* Drop the specified bytes from the queue. */ +void pa_memblockq_drop(pa_memblockq *bq, size_t length);  /* Test if the pa_memblockq is currently readable, that is, more data than base */  int pa_memblockq_is_readable(pa_memblockq *bq); -/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ -int pa_memblockq_is_writable(pa_memblockq *bq, size_t length); -  /* Return the length of the queue in bytes */  size_t pa_memblockq_get_length(pa_memblockq *bq);  /* Return how many bytes are missing in queue to the specified fill amount */  size_t pa_memblockq_missing(pa_memblockq *bq); +/* Return the number of bytes that are missing since the last call to + * this function, reset the internal counter to 0. */ +size_t pa_memblockq_pop_missing(pa_memblockq *bq); +  /* Returns the minimal request value */  size_t pa_memblockq_get_minreq(pa_memblockq *bq); diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c index 7111e1ec..4e73b636 100644 --- a/src/pulsecore/memchunk.c +++ b/src/pulsecore/memchunk.c @@ -27,40 +27,66 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <string.h> +#include <errno.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h>  #include "memchunk.h" -void pa_memchunk_make_writable(pa_memchunk *c, size_t min) { +pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {      pa_memblock *n;      size_t l; +    void *tdata, *sdata; -    assert(c); -    assert(c->memblock); -    assert(PA_REFCNT_VALUE(c->memblock) > 0); +    pa_assert(c); +    pa_assert(c->memblock); -    if (PA_REFCNT_VALUE(c->memblock) == 1 && -        !c->memblock->read_only && -        c->memblock->length >= c->index+min) -        return; +    if (pa_memblock_ref_is_one(c->memblock) && +        !pa_memblock_is_read_only(c->memblock) && +        pa_memblock_get_length(c->memblock) >= c->index+min) +        return c;      l = c->length;      if (l < min)          l = min; -    n = pa_memblock_new(c->memblock->pool, l); -    memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); +    n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l); +    tdata = pa_memblock_acquire(n); +    sdata = pa_memblock_acquire(c->memblock); +    memcpy(tdata, (uint8_t*) sdata + c->index, c->length); +    pa_memblock_release(n); +    pa_memblock_release(c->memblock);      pa_memblock_unref(c->memblock);      c->memblock = n;      c->index = 0; + +    return c;  } -void pa_memchunk_reset(pa_memchunk *c) { -    assert(c); +pa_memchunk* pa_memchunk_reset(pa_memchunk *c) { +    pa_assert(c);      c->memblock = NULL;      c->length = c->index = 0; + +    return c; +} + +pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) { +    void *p; + +    pa_assert(c); +    pa_assert(c->memblock); + +    /* A version of pa_memblock_will_need() that works on memchunks +     * instead of memblocks */ + +    p = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index; +    pa_will_need(p, c->length); +    pa_memblock_release(c->memblock); + +    return (pa_memchunk*) c;  } diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h index 0b982b6d..e6105ace 100644 --- a/src/pulsecore/memchunk.h +++ b/src/pulsecore/memchunk.h @@ -37,11 +37,16 @@ typedef struct pa_memchunk {  /* Make a memchunk writable, i.e. make sure that the caller may have   * exclusive access to the memblock and it is not read_only. If needed - * the memblock in the structure is replaced by a copy. */ -void pa_memchunk_make_writable(pa_memchunk *c, size_t min); + * the memblock in the structure is replaced by a copy. If min is not + * 0 it is made sure that the returned memblock is at least of the + * specified size, i.e. is enlarged if necessary. */ +pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min);  /* Invalidate a memchunk. This does not free the cotaining memblock,   * but sets all members to zero. */ -void pa_memchunk_reset(pa_memchunk *c); +pa_memchunk* pa_memchunk_reset(pa_memchunk *c); + +/* Map a memory chunk back into memory if it was swapped out */ +pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);  #endif diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index 3733f655..1311af3c 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -26,7 +26,6 @@  #endif  #include <ctype.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -39,6 +38,7 @@  #include <pulsecore/sink.h>  #include <pulsecore/source.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "modargs.h" @@ -48,7 +48,10 @@ struct entry {  static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {      struct entry *e; -    assert(map && key && value); + +    pa_assert(map); +    pa_assert(key); +    pa_assert(value);      if (valid_keys) {          const char*const* v; @@ -63,10 +66,11 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co          }      } -    e = pa_xmalloc(sizeof(struct entry)); +    e = pa_xnew(struct entry, 1);      e->key = key;      e->value = value;      pa_hashmap_put(map, key, e); +      return 0;  } @@ -74,7 +78,6 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {      pa_hashmap *map = NULL;      map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -    assert(map);      if (args) {          enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; @@ -166,10 +169,10 @@ fail:      return NULL;  } -  static void free_func(void *p, PA_GCC_UNUSED void*userdata) {      struct entry *e = p; -    assert(e); +    pa_assert(e); +      pa_xfree(e->key);      pa_xfree(e->value);      pa_xfree(e); @@ -192,7 +195,10 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de  int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {      const char *v; -    assert(ma && key && value); + +    pa_assert(ma); +    pa_assert(key); +    pa_assert(value);      if (!(v = pa_modargs_get_value(ma, key, NULL)))          return 0; @@ -205,7 +211,10 @@ int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {  int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {      const char *v; -    assert(ma && key && value); + +    pa_assert(ma); +    pa_assert(key); +    pa_assert(value);      if (!(v = pa_modargs_get_value(ma, key, NULL)))          return 0; @@ -219,7 +228,10 @@ int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {  int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {      const char *v;      int r; -    assert(ma && key && value); + +    pa_assert(ma); +    pa_assert(key); +    pa_assert(value);      if (!(v = pa_modargs_get_value(ma, key, NULL)))          return 0; @@ -238,9 +250,9 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {      const char *format;      uint32_t channels;      pa_sample_spec ss; -    assert(ma && rss); -/*    DEBUG_TRAP;*/ +    pa_assert(ma); +    pa_assert(rss);      ss = *rss;      if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) @@ -263,16 +275,16 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {      return 0;  } -int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) { +int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *rmap) {      pa_channel_map map;      const char *cm; -    assert(ma); -    assert(rmap); +    pa_assert(ma); +    pa_assert(rmap);      map = *rmap; -    if ((cm = pa_modargs_get_value(ma, "channel_map", NULL))) +    if ((cm = pa_modargs_get_value(ma, name ? name : "channel_map", NULL)))          if (!pa_channel_map_parse(&map, cm))              return -1; @@ -287,9 +299,9 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r      pa_sample_spec ss;      pa_channel_map map; -    assert(ma); -    assert(rss); -    assert(rmap); +    pa_assert(ma); +    pa_assert(rss); +    pa_assert(rmap);      ss = *rss; @@ -299,7 +311,7 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r      if (!pa_channel_map_init_auto(&map, ss.channels, def))          map.channels = 0; -    if (pa_modargs_get_channel_map(ma, &map) < 0) +    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)          return -1;      if (map.channels != ss.channels) diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h index 77262e1e..aa175885 100644 --- a/src/pulsecore/modargs.h +++ b/src/pulsecore/modargs.h @@ -49,8 +49,8 @@ int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);  /* Return sample spec data from the three arguments "rate", "format" and "channels" */  int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss); -/* Return channel map data from the argument "channel_map" */ -int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *map); +/* Return channel map data from the argument "channel_map" if name is NULL, otherwise read from the specified argument */ +int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *map);  /* Combination of pa_modargs_get_sample_spec() and  pa_modargs_get_channel_map(). Not always suitable, since this routine diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c index 58394e59..da2df653 100644 --- a/src/pulsecore/modinfo.c +++ b/src/pulsecore/modinfo.c @@ -26,12 +26,13 @@  #endif  #include <ltdl.h> -#include <assert.h>  #include <pulse/xmalloc.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/ltdl-helper.h>  #include "modinfo.h" @@ -40,30 +41,24 @@  #define PA_SYMBOL_USAGE "pa__get_usage"  #define PA_SYMBOL_VERSION "pa__get_version" -/* lt_dlsym() violates ISO C, so confide the breakage into this function to - * avoid warnings. */ -typedef void (*fnptr)(void); -static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) { -    return (fnptr) (long) lt_dlsym(handle, symbol); -} - -pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) { +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {      pa_modinfo *i;      const char* (*func)(void); -    assert(dl); + +    pa_assert(dl);      i = pa_xnew0(pa_modinfo, 1); -    if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_AUTHOR))) +    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_AUTHOR)))          i->author = pa_xstrdup(func()); -    if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_DESCRIPTION))) +    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DESCRIPTION)))          i->description = pa_xstrdup(func()); -    if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_USAGE))) +    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_USAGE)))          i->usage = pa_xstrdup(func()); -    if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_VERSION))) +    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))          i->version = pa_xstrdup(func());      return i; @@ -72,21 +67,23 @@ pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {  pa_modinfo *pa_modinfo_get_by_name(const char *name) {      lt_dlhandle dl;      pa_modinfo *i; -    assert(name); + +    pa_assert(name);      if (!(dl = lt_dlopenext(name))) {          pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());          return NULL;      } -    i = pa_modinfo_get_by_handle(dl); +    i = pa_modinfo_get_by_handle(dl, name);      lt_dlclose(dl);      return i;  }  void pa_modinfo_free(pa_modinfo *i) { -    assert(i); +    pa_assert(i); +      pa_xfree(i->author);      pa_xfree(i->description);      pa_xfree(i->usage); diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h index 3ee33ede..02e536c6 100644 --- a/src/pulsecore/modinfo.h +++ b/src/pulsecore/modinfo.h @@ -34,7 +34,7 @@ typedef struct pa_modinfo {  } pa_modinfo;  /* Read meta data from an libtool handle */ -pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl); +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name);  /* Read meta data from a module file */  pa_modinfo *pa_modinfo_get_by_name(const char *name); diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index 09b15b8b..dce91a71 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -29,7 +29,6 @@  #include <limits.h>  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <errno.h>  #include <ctype.h> @@ -40,6 +39,8 @@  #include <pulsecore/core-subscribe.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h> +#include <pulsecore/ltdl-helper.h>  #include "module.h" @@ -48,69 +49,31 @@  #define UNLOAD_POLL_TIME 2 -/* lt_dlsym() violates ISO C, so confide the breakage into this function to - * avoid warnings. */ -typedef void (*fnptr)(void); -static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) { -    return (fnptr) (long) lt_dlsym(handle, symbol); -} -  static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { -    pa_core *c = userdata; +    pa_core *c = PA_CORE(userdata);      struct timeval ntv; -    assert(c && c->mainloop == m && c->module_auto_unload_event == e); + +    pa_core_assert_ref(c); +    pa_assert(c->mainloop == m); +    pa_assert(c->module_auto_unload_event == e);      pa_module_unload_unused(c);      pa_gettimeofday(&ntv); -    ntv.tv_sec += UNLOAD_POLL_TIME; +    pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);      m->time_restart(e, &ntv);  } -static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) { -    char *buffer, *ch; -    size_t buflen; -    fnptr res; - -    res = lt_dlsym_fn(handle, symbol); -    if (res) -        return res; - -    /* As the .la files might have been cleansed from the system, we should -     * try with the ltdl prefix as well. */ - -    buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1; -    buffer = pa_xmalloc(buflen); -    assert(buffer); - -    strcpy(buffer, module); - -    for (ch = buffer;*ch != '\0';ch++) { -        if (!isalnum(*ch)) -            *ch = '_'; -    } - -    strcat(buffer, "_LTX_"); -    strcat(buffer, symbol); - -    res = lt_dlsym_fn(handle, buffer); - -    pa_xfree(buffer); - -    return res; -} -  pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {      pa_module *m = NULL; -    int r; -    assert(c && name); +    pa_assert(c); +    pa_assert(name);      if (c->disallow_module_loading)          goto fail; -    m = pa_xmalloc(sizeof(pa_module)); - +    m = pa_xnew(pa_module, 1);      m->name = pa_xstrdup(name);      m->argument = pa_xstrdup(argument); @@ -119,24 +82,19 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {          goto fail;      } -    if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) { +    if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {          pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);          goto fail;      } -    if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) { -        pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name); -        goto fail; -    } - +    m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);      m->userdata = NULL;      m->core = c;      m->n_used = -1;      m->auto_unload = 0;      m->unload_requested = 0; -    assert(m->init); -    if (m->init(c, m) < 0) { +    if (m->init(m) < 0) {          pa_log_error("Failed to load  module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");          goto fail;      } @@ -147,14 +105,12 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {      if (!c->module_auto_unload_event) {          struct timeval ntv;          pa_gettimeofday(&ntv); -        ntv.tv_sec += UNLOAD_POLL_TIME; +        pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);          c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);      } -    assert(c->module_auto_unload_event); -    assert(c->modules); -    r = pa_idxset_put(c->modules, m, &m->index); -    assert(r >= 0 && m->index != PA_IDXSET_INVALID); +    pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0); +    pa_assert(m->index != PA_IDXSET_INVALID);      pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : ""); @@ -178,14 +134,16 @@ fail:  }  static void pa_module_free(pa_module *m) { -    assert(m && m->done && m->core); +    pa_assert(m); +    pa_assert(m->core);      if (m->core->disallow_module_loading)          return;      pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index); -    m->done(m->core, m); +    if (m->done) +        m->done(m);      lt_dlclose(m->dl); @@ -199,9 +157,10 @@ static void pa_module_free(pa_module *m) {  }  void pa_module_unload(pa_core *c, pa_module *m) { -    assert(c && m); +    pa_assert(c); +    pa_assert(m); -    assert(c->modules); +    pa_assert(c->modules);      if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))          return; @@ -210,9 +169,9 @@ void pa_module_unload(pa_core *c, pa_module *m) {  void pa_module_unload_by_index(pa_core *c, uint32_t idx) {      pa_module *m; -    assert(c && idx != PA_IDXSET_INVALID); +    pa_assert(c); +    pa_assert(idx != PA_IDXSET_INVALID); -    assert(c->modules);      if (!(m = pa_idxset_remove_by_index(c->modules, idx)))          return; @@ -221,13 +180,14 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx) {  static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {      pa_module *m = p; -    assert(m); +    pa_assert(m);      pa_module_free(m);  }  void pa_module_unload_all(pa_core *c) {      pa_module *m; -    assert(c); + +    pa_assert(c);      if (!c->modules)          return; @@ -252,7 +212,10 @@ void pa_module_unload_all(pa_core *c) {  static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {      pa_module *m = p;      time_t *now = userdata; -    assert(p && del && now); + +    pa_assert(m); +    pa_assert(del); +    pa_assert(now);      if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {          pa_module_free(m); @@ -264,7 +227,7 @@ static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *  void pa_module_unload_unused(pa_core *c) {      time_t now; -    assert(c); +    pa_assert(c);      if (!c->modules)          return; @@ -275,7 +238,7 @@ void pa_module_unload_unused(pa_core *c) {  static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {      pa_module *m = p; -    assert(m); +    pa_assert(m);      if (m->unload_requested) {          pa_module_free(m); @@ -286,18 +249,19 @@ static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC  }  static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { -    pa_core *core = userdata; +    pa_core *core = PA_CORE(userdata); + +    pa_core_assert_ref(core);      api->defer_enable(e, 0);      if (!core->modules)          return;      pa_idxset_foreach(core->modules, unload_callback, NULL); -  }  void pa_module_unload_request(pa_module *m) { -    assert(m); +    pa_assert(m);      m->unload_requested = 1; @@ -308,19 +272,19 @@ void pa_module_unload_request(pa_module *m) {  }  void pa_module_set_used(pa_module*m, int used) { -    assert(m); +    pa_assert(m);      if (m->n_used != used)          pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); -    if (m->n_used != used && used == 0) +    if (used == 0 && m->n_used > 0)          time(&m->last_used_time);      m->n_used = used;  }  pa_modinfo *pa_module_get_info(pa_module *m) { -    assert(m); +    pa_assert(m); -    return pa_modinfo_get_by_handle(m->dl); +    return pa_modinfo_get_by_handle(m->dl, m->name);  } diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 750dfaa8..7a93a071 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -39,8 +39,8 @@ struct pa_module {      lt_dlhandle dl; -    int (*init)(pa_core *c, pa_module*m); -    void (*done)(pa_core *c, pa_module*m); +    int (*init)(pa_module*m); +    void (*done)(pa_module*m);      void *userdata; @@ -62,9 +62,9 @@ void pa_module_unload_request(pa_module *m);  void pa_module_set_used(pa_module*m, int used); -#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; } -#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; } -#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; } +#define PA_MODULE_AUTHOR(s) const char *pa__get_author(void) { return s; } +#define PA_MODULE_DESCRIPTION(s) const char *pa__get_description(void) { return s; } +#define PA_MODULE_USAGE(s) const char *pa__get_usage(void) { return s; }  #define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }  pa_modinfo *pa_module_get_info(pa_module *m); diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c new file mode 100644 index 00000000..f54e69f2 --- /dev/null +++ b/src/pulsecore/msgobject.c @@ -0,0 +1,49 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 "msgobject.h" + +PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object); + +pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) { +    pa_msgobject *o; + +    pa_assert(size > sizeof(pa_msgobject)); +    pa_assert(type_name); + +    if (!check_type) +        check_type = pa_msgobject_check_type; + +    pa_assert(check_type(type_name)); +    pa_assert(check_type("pa_object")); +    pa_assert(check_type("pa_msgobject")); + +    o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type)); +    o->process_msg = NULL; +    return o; +} diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h new file mode 100644 index 00000000..8221cc33 --- /dev/null +++ b/src/pulsecore/msgobject.h @@ -0,0 +1,54 @@ +#ifndef foopulsemsgobjecthfoo +#define foopulsemsgobjecthfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <sys/types.h> + +#include <pulse/xmalloc.h> +#include <pulsecore/refcnt.h> +#include <pulsecore/macro.h> +#include <pulsecore/object.h> +#include <pulsecore/memchunk.h> + +typedef struct pa_msgobject pa_msgobject; + +struct pa_msgobject { +    pa_object parent; +    int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +}; + +pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)); + +int pa_msgobject_check_type(const char *type); + +#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type)) +#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free) + +#define PA_MSGOBJECT(o) pa_msgobject_cast(o) + +PA_DECLARE_CLASS(pa_msgobject); + +#endif diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c index 52e731b3..1b13ede1 100644 --- a/src/pulsecore/mutex-posix.c +++ b/src/pulsecore/mutex-posix.c @@ -25,20 +25,16 @@  #include <config.h>  #endif -#include <assert.h>  #include <pthread.h> - -#include <atomic_ops.h> +#include <errno.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/log.h> +#include <pulsecore/core-error.h>  #include "mutex.h" -#define ASSERT_SUCCESS(x) do { \ -    int _r = (x); \ -    assert(_r == 0); \ -} while(0) -  struct pa_mutex {      pthread_mutex_t mutex;  }; @@ -47,68 +43,88 @@ struct pa_cond {      pthread_cond_t cond;  }; -pa_mutex* pa_mutex_new(int recursive) { +pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {      pa_mutex *m;      pthread_mutexattr_t attr; +    int r; -    pthread_mutexattr_init(&attr); +    pa_assert_se(pthread_mutexattr_init(&attr) == 0);      if (recursive) -        ASSERT_SUCCESS(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); +        pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0); + +#ifdef HAVE_PTHREAD_PRIO_INHERIT +    if (inherit_priority) +        pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) == 0); +#endif      m = pa_xnew(pa_mutex, 1); -    ASSERT_SUCCESS(pthread_mutex_init(&m->mutex, &attr)); +#ifndef HAVE_PTHREAD_PRIO_INHERIT +    pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0); + +#else +    if ((r = pthread_mutex_init(&m->mutex, &attr))) { + +        /* If this failed, then this was probably due to non-available +         * priority inheritance. In which case we fall back to normal +         * mutexes. */ +        pa_assert(r == ENOTSUP && inherit_priority); + +        pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0); +        pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0); +    } +#endif +      return m;  }  void pa_mutex_free(pa_mutex *m) { -    assert(m); +    pa_assert(m); -    ASSERT_SUCCESS(pthread_mutex_destroy(&m->mutex)); +    pa_assert_se(pthread_mutex_destroy(&m->mutex) == 0);      pa_xfree(m);  }  void pa_mutex_lock(pa_mutex *m) { -    assert(m); +    pa_assert(m); -    ASSERT_SUCCESS(pthread_mutex_lock(&m->mutex)); +    pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);  }  void pa_mutex_unlock(pa_mutex *m) { -    assert(m); +    pa_assert(m); -    ASSERT_SUCCESS(pthread_mutex_unlock(&m->mutex)); +    pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);  }  pa_cond *pa_cond_new(void) {      pa_cond *c;      c = pa_xnew(pa_cond, 1); - -    ASSERT_SUCCESS(pthread_cond_init(&c->cond, NULL)); +    pa_assert_se(pthread_cond_init(&c->cond, NULL) == 0);      return c;  }  void pa_cond_free(pa_cond *c) { -    assert(c); +    pa_assert(c); -    ASSERT_SUCCESS(pthread_cond_destroy(&c->cond)); +    pa_assert_se(pthread_cond_destroy(&c->cond) == 0);      pa_xfree(c);  }  void pa_cond_signal(pa_cond *c, int broadcast) { -    assert(c); +    pa_assert(c);      if (broadcast) -        ASSERT_SUCCESS(pthread_cond_broadcast(&c->cond)); +        pa_assert_se(pthread_cond_broadcast(&c->cond) == 0);      else -        ASSERT_SUCCESS(pthread_cond_signal(&c->cond)); +        pa_assert_se(pthread_cond_signal(&c->cond) == 0);  }  int pa_cond_wait(pa_cond *c, pa_mutex *m) { -    assert(c); -    assert(m); +    pa_assert(c); +    pa_assert(m);      return pthread_cond_wait(&c->cond, &m->mutex);  } diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c index 1f16e24c..77d63d15 100644 --- a/src/pulsecore/mutex-win32.c +++ b/src/pulsecore/mutex-win32.c @@ -40,7 +40,7 @@ struct pa_cond {      pa_hashmap *wait_events;  }; -pa_mutex* pa_mutex_new(int recursive) { +pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {      pa_mutex *m;      m = pa_xnew(pa_mutex, 1); diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h index b2e34c07..72e88781 100644 --- a/src/pulsecore/mutex.h +++ b/src/pulsecore/mutex.h @@ -24,9 +24,17 @@    USA.  ***/ +#include <pulsecore/macro.h> +  typedef struct pa_mutex pa_mutex; -pa_mutex* pa_mutex_new(int recursive); +/* Please think twice before enabling priority inheritance. This is no + * magic wand! Use it only when the potentially priorized threads are + * good candidates for it. Don't use this blindly! Also, note that + * only very few operating systems actually implement this, hence this + * is merely a hint. */ +pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority); +  void pa_mutex_free(pa_mutex *m);  void pa_mutex_lock(pa_mutex *m);  void pa_mutex_unlock(pa_mutex *m); diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 7f66af05..fe2be467 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -27,7 +27,6 @@  #include <stdlib.h>  #include <string.h> -#include <assert.h>  #include <string.h>  #include <stdio.h> @@ -38,6 +37,7 @@  #include <pulsecore/sink.h>  #include <pulsecore/core-subscribe.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "namereg.h" @@ -90,23 +90,22 @@ static char* cleanup_name(const char *name) {  }  void pa_namereg_free(pa_core *c) { -    assert(c); +    pa_assert(c);      if (!c->namereg)          return; -    assert(pa_hashmap_size(c->namereg) == 0); +    pa_assert(pa_hashmap_size(c->namereg) == 0);      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) {      struct namereg_entry *e;      char *n = NULL; -    int r; -    assert(c); -    assert(name); -    assert(data); +    pa_assert(c); +    pa_assert(name); +    pa_assert(data);      if (!*name)          return NULL; @@ -142,7 +141,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t          k = pa_xnew(char, l+4);          for (i = 2; i <= 99; i++) { -            snprintf(k, l+4, "%s.%u", name, i); +            pa_snprintf(k, l+4, "%s.%u", name, i);              if (!(e = pa_hashmap_get(c->namereg, k)))                  break; @@ -163,8 +162,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t      e->name = n ? n : pa_xstrdup(name);      e->data = data; -    r = pa_hashmap_put(c->namereg, e->name, e); -    assert (r >= 0); +    pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);      return e->name;  } @@ -172,11 +170,10 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t  void pa_namereg_unregister(pa_core *c, const char *name) {      struct namereg_entry *e; -    assert(c); -    assert(name); +    pa_assert(c); +    pa_assert(name); -    e = pa_hashmap_remove(c->namereg, name); -    assert(e); +    pa_assert_se(e = pa_hashmap_remove(c->namereg, name));      pa_xfree(e->name);      pa_xfree(e); @@ -185,7 +182,7 @@ 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, int autoload) {      struct namereg_entry *e;      uint32_t idx; -    assert(c); +    pa_assert(c);      if (!name) { @@ -245,8 +242,8 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int a  int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {      char **s; -    assert(c); -    assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE); +    pa_assert(c); +    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);      s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name; @@ -269,7 +266,7 @@ int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type)  const char *pa_namereg_get_default_sink_name(pa_core *c) {      pa_sink *s; -    assert(c); +    pa_assert(c);      if (c->default_sink_name)          return c->default_sink_name; @@ -284,7 +281,7 @@ const char *pa_namereg_get_default_source_name(pa_core *c) {      pa_source *s;      uint32_t idx; -    assert(c); +    pa_assert(c);      if (c->default_source_name)          return c->default_source_name; diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index f7a7da1d..9defc4a5 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -115,6 +115,11 @@ enum {      PA_COMMAND_MOVE_SINK_INPUT,      PA_COMMAND_MOVE_SOURCE_OUTPUT, +    PA_COMMAND_SET_SINK_INPUT_MUTE, + +    PA_COMMAND_SUSPEND_SINK, +    PA_COMMAND_SUSPEND_SOURCE, +      PA_COMMAND_MAX  }; diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c new file mode 100644 index 00000000..6c36242b --- /dev/null +++ b/src/pulsecore/object.c @@ -0,0 +1,72 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 "object.h" + +pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) { +    pa_object *o; + +    pa_assert(size > sizeof(pa_object)); +    pa_assert(type_name); + +    if (!check_type) +        check_type = pa_object_check_type; + +    pa_assert(check_type(type_name)); +    pa_assert(check_type("pa_object")); + +    o = pa_xmalloc(size); +    PA_REFCNT_INIT(o); +    o->type_name = type_name; +    o->free = pa_object_free; +    o->check_type = check_type; + +    return o; +} + +pa_object *pa_object_ref(pa_object *o) { +    pa_object_assert_ref(o); + +    PA_REFCNT_INC(o); +    return o; +} + +void pa_object_unref(pa_object *o) { +    pa_object_assert_ref(o); + +    if (PA_REFCNT_DEC(o) <= 0) { +        pa_assert(o->free); +        o->free(o); +    } +} + +int pa_object_check_type(const char *type_name) { +    pa_assert(type_name); + +    return strcmp(type_name, "pa_object") == 0; +} diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h new file mode 100644 index 00000000..562fd113 --- /dev/null +++ b/src/pulsecore/object.h @@ -0,0 +1,106 @@ +#ifndef foopulseobjecthfoo +#define foopulseobjecthfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <string.h> +#include <sys/types.h> + +#include <pulse/xmalloc.h> +#include <pulsecore/refcnt.h> +#include <pulsecore/macro.h> + +typedef struct pa_object pa_object; + +struct pa_object { +    PA_REFCNT_DECLARE; +    const char *type_name; +    void (*free)(pa_object *o); +    int (*check_type)(const char *type_name); +}; + +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) + +int pa_object_check_type(const char *type); + +static inline int pa_object_isinstance(void *o) { +    pa_object *obj = (pa_object*) o; +    return obj ? obj->check_type("pa_object") : 0; +} + +pa_object *pa_object_ref(pa_object *o); +void pa_object_unref(pa_object *o); + +static inline int pa_object_refcnt(pa_object *o) { +    return o ? PA_REFCNT_VALUE(o) : 0; +} + +static inline pa_object* pa_object_cast(void *o) { +    pa_object *obj = (pa_object*) o; +    pa_assert(!obj || obj->check_type("pa_object")); +    return obj; +} + +#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o) > 0) + +#define PA_OBJECT(o) pa_object_cast(o) + +#define PA_DECLARE_CLASS(c)                                             \ +    static inline int c##_isinstance(void *o) {                         \ +        pa_object *obj = (pa_object*) o;                                \ +        return obj ? obj->check_type(#c) : 1;                           \ +    }                                                                   \ +    static inline c* c##_cast(void *o) {                                \ +        pa_assert(c##_isinstance(o));                                   \ +        return (c*) o;                                                  \ +    }                                                                   \ +    static inline c* c##_ref(c *o) {                                    \ +        return (c*) pa_object_ref(PA_OBJECT(o));                        \ +    }                                                                   \ +    static inline void c##_unref(c* o) {                                \ +        pa_object_unref(PA_OBJECT(o));                                  \ +    }                                                                   \ +    static inline int c##_refcnt(c* o) {                                \ +        return pa_object_refcnt(PA_OBJECT(o));                          \ +    }                                                                   \ +    static inline void c##_assert_ref(c *o) {                           \ +        pa_object_assert_ref(PA_OBJECT(o));                             \ +    }                                                                   \ +    struct __stupid_useless_struct_to_allow_trailing_semicolon + +#define PA_DEFINE_CHECK_TYPE(c, parent)                                 \ +    int c##_check_type(const char *type) {                              \ +        pa_assert(type);                                                \ +        if (strcmp(type, #c) == 0)                                      \ +            return 1;                                                   \ +        return parent##_check_type(type);                               \ +    }                                                                   \ +    struct __stupid_useless_struct_to_allow_trailing_semicolon + + +#endif diff --git a/src/pulsecore/once-posix.c b/src/pulsecore/once-posix.c deleted file mode 100644 index 4af7b36e..00000000 --- a/src/pulsecore/once-posix.c +++ /dev/null @@ -1,71 +0,0 @@ -/* $Id$ */ - -/*** -  This file is part of PulseAudio. - -  Copyright 2006 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 <pthread.h> -#include <assert.h> - -#include <pulsecore/mutex.h> - -#include "once.h" - -#define ASSERT_SUCCESS(x) do { \ -    int _r = (x); \ -    assert(_r == 0); \ -} while(0) - -static pa_mutex *global_mutex; -static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT; - -static void global_mutex_once_func(void) { -    global_mutex = pa_mutex_new(0); -} - -void pa_once(pa_once_t *control, pa_once_func_t func) { -    assert(control); -    assert(func); - -    /* Create the global mutex */ -    ASSERT_SUCCESS(pthread_once(&global_mutex_once, global_mutex_once_func)); - -    /* Create the local mutex */ -    pa_mutex_lock(global_mutex); -    if (!control->mutex) -        control->mutex = pa_mutex_new(1); -    pa_mutex_unlock(global_mutex); - -    /* Execute function */ -    pa_mutex_lock(control->mutex); -    if (!control->once_value) { -        control->once_value = 1; -        func(); -    } -    pa_mutex_unlock(control->mutex); - -    /* Caveat: We have to make sure that the once func has completed -     * before returning, even if the once func is not actually -     * executed by us. Hence the awkward locking. */ -} diff --git a/src/pulsecore/once-win32.c b/src/pulsecore/once-win32.c deleted file mode 100644 index b30097c8..00000000 --- a/src/pulsecore/once-win32.c +++ /dev/null @@ -1,69 +0,0 @@ -/* $Id$ */ - -/*** -  This file is part of PulseAudio. - -  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB - -  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 <assert.h> -#include <stdio.h> - -#include <windows.h> - -#include <pulsecore/mutex.h> - -#include "once.h" - -void pa_once(pa_once_t *control, pa_once_func_t func) { -    HANDLE mutex; -    char name[64]; - -    assert(control); -    assert(func); - -    /* Create the global mutex */ -    sprintf(name, "pulse%d", (int)GetCurrentProcessId()); - -    mutex = CreateMutex(NULL, FALSE, name); -    assert(mutex); - -    /* Create the local mutex */ -    WaitForSingleObject(mutex, INFINITE); -    if (!control->mutex) -        control->mutex = pa_mutex_new(1); -    ReleaseMutex(mutex); - -    CloseHandle(mutex); - -    /* Execute function */ -    pa_mutex_lock(control->mutex); -    if (!control->once_value) { -        control->once_value = 1; -        func(); -    } -    pa_mutex_unlock(control->mutex); - -    /* Caveat: We have to make sure that the once func has completed -     * before returning, even if the once func is not actually -     * executed by us. Hence the awkward locking. */ -} diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c new file mode 100644 index 00000000..a358cf65 --- /dev/null +++ b/src/pulsecore/once.c @@ -0,0 +1,96 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <pulsecore/macro.h> +#include <pulsecore/mutex.h> + +#include "once.h" + +int pa_once_begin(pa_once *control) { +    pa_mutex *m; + +    pa_assert(control); + +    if (pa_atomic_load(&control->done)) +        return 0; + +    pa_atomic_inc(&control->ref); + +    /* Caveat: We have to make sure that the once func has completed +     * before returning, even if the once func is not actually +     * executed by us. Hence the awkward locking. */ + +    for (;;) { + +        if ((m = pa_atomic_ptr_load(&control->mutex))) { + +            /* The mutex is stored in locked state, hence let's just +             * wait until it is unlocked */ +            pa_mutex_lock(m); + +            pa_once_end(control); +            return 0; +        } + +        pa_assert_se(m = pa_mutex_new(FALSE, FALSE)); +        pa_mutex_lock(m); + +        if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) +            return 1; + +        pa_mutex_unlock(m); +        pa_mutex_free(m); +    } +} + +void pa_once_end(pa_once *control) { +    pa_mutex *m; + +    pa_assert(control); + +    pa_atomic_store(&control->done, 1); + +    pa_assert_se(m = pa_atomic_ptr_load(&control->mutex)); +    pa_mutex_unlock(m); + +    if (pa_atomic_dec(&control->ref) <= 1) { +        pa_assert_se(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL)); +        pa_mutex_free(m); +    } +} + +/* Not reentrant -- how could it be? */ +void pa_run_once(pa_once *control, pa_once_func_t func) { +    pa_assert(control); +    pa_assert(func); + +    if (pa_once_begin(control)) { +        func(); +        pa_once_end(control); +    } +} + diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index c20fc0b4..c9fe6d0a 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -25,16 +25,52 @@  ***/  #include <pulsecore/mutex.h> +#include <pulsecore/atomic.h>  typedef struct pa_once { -    unsigned int once_value; -    pa_mutex *mutex; -} pa_once_t; +    pa_atomic_ptr_t mutex; +    pa_atomic_t ref, done; +} pa_once; -#define PA_ONCE_INIT { .once_value = 0, .mutex = NULL } +#define PA_ONCE_INIT                                                    \ +    {                                                                   \ +        .mutex = PA_ATOMIC_PTR_INIT(NULL),                              \ +        .ref = PA_ATOMIC_INIT(0),                                       \ +        .done = PA_ATOMIC_INIT(0)                                       \ +    } -typedef void (*pa_once_func_t) (void); +/* Not to be called directly, use the macros defined below instead */ +int pa_once_begin(pa_once *o); +void pa_once_end(pa_once *o); + +#define PA_ONCE_BEGIN                                                   \ +    do {                                                                \ +        static pa_once _once = PA_ONCE_INIT;                            \ +        if (pa_once_begin(&_once)) {{ + +#define PA_ONCE_END                                                     \ +            }                                                           \ +            pa_once_end(&_once);                                        \ +        }                                                               \ +    } while(0) + +/* + +  Usage of these macros is like this: + +  void foo() { -void pa_once(pa_once_t *o, pa_once_func_t f); +      PA_ONCE_BEGIN { + +          ... stuff to be called just once ... + +      } PA_ONCE_END; +  } + +*/ + +/* Same API but calls a function */ +typedef void (*pa_once_func_t) (void); +void pa_run_once(pa_once *o, pa_once_func_t f);  #endif diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c index ce57cb3e..2706efea 100644 --- a/src/pulsecore/packet.c +++ b/src/pulsecore/packet.c @@ -25,22 +25,22 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include "packet.h"  pa_packet* pa_packet_new(size_t length) {      pa_packet *p; -    assert(length); +    pa_assert(length > 0); -    p = pa_xmalloc(sizeof(pa_packet)+length); -    p->ref = 1; +    p = pa_xmalloc(PA_ALIGN(sizeof(pa_packet)) + length); +    PA_REFCNT_INIT(p);      p->length = length; -    p->data = (uint8_t*) (p+1); +    p->data = (uint8_t*) p + PA_ALIGN(sizeof(pa_packet));      p->type = PA_PACKET_APPENDED;      return p; @@ -49,11 +49,11 @@ pa_packet* pa_packet_new(size_t length) {  pa_packet* pa_packet_new_dynamic(void* data, size_t length) {      pa_packet *p; -    assert(data); -    assert(length); +    pa_assert(data); +    pa_assert(length > 0);      p = pa_xnew(pa_packet, 1); -    p->ref = 1; +    PA_REFCNT_INIT(p);      p->length = length;      p->data = data;      p->type = PA_PACKET_DYNAMIC; @@ -62,18 +62,18 @@ pa_packet* pa_packet_new_dynamic(void* data, size_t length) {  }  pa_packet* pa_packet_ref(pa_packet *p) { -    assert(p); -    assert(p->ref >= 1); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) >= 1); -    p->ref++; +    PA_REFCNT_INC(p);      return p;  }  void pa_packet_unref(pa_packet *p) { -    assert(p); -    assert(p->ref >= 1); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) >= 1); -    if (--p->ref == 0) { +    if (PA_REFCNT_DEC(p) <= 0) {          if (p->type == PA_PACKET_DYNAMIC)              pa_xfree(p->data);          pa_xfree(p); diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h index 842582c8..bcac4a7f 100644 --- a/src/pulsecore/packet.h +++ b/src/pulsecore/packet.h @@ -27,9 +27,11 @@  #include <sys/types.h>  #include <inttypes.h> +#include <pulsecore/refcnt.h> +  typedef struct pa_packet { +    PA_REFCNT_DECLARE;      enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; -    unsigned ref;      size_t length;      uint8_t *data;  } pa_packet; diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c index a49a09ed..65ba64c1 100644 --- a/src/pulsecore/parseaddr.c +++ b/src/pulsecore/parseaddr.c @@ -26,13 +26,13 @@  #endif  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h> -  #include <pulse/util.h> +  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "parseaddr.h" @@ -45,7 +45,9 @@   *  Return a newly allocated string of the hostname and fill in *ret_port if specified  */  static char *parse_host(const char *s, uint16_t *ret_port) { -    assert(s && ret_port); +    pa_assert(s); +    pa_assert(ret_port); +      if (*s == '[') {          char *e;          if (!(e = strchr(s+1, ']'))) @@ -70,7 +72,10 @@ static char *parse_host(const char *s, uint16_t *ret_port) {  int pa_parse_address(const char *name, pa_parsed_address *ret_p) {      const char *p; -    assert(name && ret_p); + +    pa_assert(name); +    pa_assert(ret_p); +      memset(ret_p, 0, sizeof(pa_parsed_address));      ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; @@ -112,6 +117,5 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {          if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))              return -1; -      return 0;  } diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index 10238acb..2c95d740 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -28,7 +28,6 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> @@ -37,6 +36,8 @@  #include <pulsecore/llist.h>  #include <pulsecore/log.h>  #include <pulsecore/core-util.h> +#include <pulsecore/macro.h> +#include <pulsecore/refcnt.h>  #include "pdispatch.h" @@ -108,7 +109,7 @@ struct reply_info {  };  struct pa_pdispatch { -    int ref; +    PA_REFCNT_DECLARE;      pa_mainloop_api *mainloop;      const pa_pdispatch_cb_t *callback_table;      unsigned n_commands; @@ -119,7 +120,9 @@ struct pa_pdispatch {  };  static void reply_info_free(struct reply_info *r) { -    assert(r && r->pdispatch && r->pdispatch->mainloop); +    pa_assert(r); +    pa_assert(r->pdispatch); +    pa_assert(r->pdispatch->mainloop);      if (r->time_event)          r->pdispatch->mainloop->time_free(r->time_event); @@ -131,12 +134,12 @@ static void reply_info_free(struct reply_info *r) {  pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {      pa_pdispatch *pd; -    assert(mainloop); +    pa_assert(mainloop); -    assert((entries && table) || (!entries && !table)); +    pa_assert((entries && table) || (!entries && !table)); -    pd = pa_xmalloc(sizeof(pa_pdispatch)); -    pd->ref = 1; +    pd = pa_xnew(pa_pdispatch, 1); +    PA_REFCNT_INIT(pd);      pd->mainloop = mainloop;      pd->callback_table = table;      pd->n_commands = entries; @@ -149,7 +152,7 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_  }  static void pdispatch_free(pa_pdispatch *pd) { -    assert(pd); +    pa_assert(pd);      while (pd->replies) {          if (pd->replies->free_cb) @@ -165,7 +168,7 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command,      pa_pdispatch_cb_t callback;      void *userdata;      uint32_t tag; -    assert(r); +    pa_assert(r);      pa_pdispatch_ref(pd); @@ -187,7 +190,12 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,      uint32_t tag, command;      pa_tagstruct *ts = NULL;      int ret = -1; -    assert(pd && packet && packet->data); + +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1); +    pa_assert(packet); +    pa_assert(PA_REFCNT_VALUE(packet) >= 1); +    pa_assert(packet->data);      pa_pdispatch_ref(pd); @@ -195,7 +203,6 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,          goto finish;      ts = pa_tagstruct_new(packet->data, packet->length); -    assert(ts);      if (pa_tagstruct_getu32(ts, &command) < 0 ||          pa_tagstruct_getu32(ts, &tag) < 0) @@ -206,7 +213,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,      char t[256];      char const *p;      if (!(p = command_names[command])) -        snprintf((char*) (p = t), sizeof(t), "%u", command); +        pa_snprintf((char*) (p = t), sizeof(t), "%u", command);      pa_log("Recieved opcode <%s>", p);  } @@ -248,7 +255,12 @@ finish:  static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {      struct reply_info*r = userdata; -    assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback); + +    pa_assert(r); +    pa_assert(r->time_event == e); +    pa_assert(r->pdispatch); +    pa_assert(r->pdispatch->mainloop == m); +    pa_assert(r->callback);      run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);  } @@ -256,7 +268,10 @@ static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED c  void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {      struct reply_info *r;      struct timeval tv; -    assert(pd && pd->ref >= 1 && cb); + +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1); +    pa_assert(cb);      r = pa_xnew(struct reply_info, 1);      r->pdispatch = pd; @@ -268,21 +283,22 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa      pa_gettimeofday(&tv);      tv.tv_sec += timeout; -    r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r); -    assert(r->time_event); +    pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));      PA_LLIST_PREPEND(struct reply_info, pd->replies, r);  }  int pa_pdispatch_is_pending(pa_pdispatch *pd) { -    assert(pd); +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1);      return !!pd->replies;  }  void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) { -    assert(pd); -    assert(!cb || pa_pdispatch_is_pending(pd)); +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1); +    pa_assert(!cb || pa_pdispatch_is_pending(pd));      pd->drain_callback = cb;      pd->drain_userdata = userdata; @@ -290,7 +306,9 @@ void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *  void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {      struct reply_info *r, *n; -    assert(pd); + +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1);      for (r = pd->replies; r; r = n) {          n = r->next; @@ -301,21 +319,24 @@ void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {  }  void pa_pdispatch_unref(pa_pdispatch *pd) { -    assert(pd && pd->ref >= 1); +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1); -    if (!(--(pd->ref))) +    if (PA_REFCNT_DEC(pd) <= 0)          pdispatch_free(pd);  }  pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) { -    assert(pd && pd->ref >= 1); -    pd->ref++; +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1); + +    PA_REFCNT_INC(pd);      return pd;  }  const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) { -    assert(pd); -    assert(pd->ref >= 1); +    pa_assert(pd); +    pa_assert(PA_REFCNT_VALUE(pd) >= 1);      return pd->creds;  } diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index 5e670e17..55ff2088 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -33,7 +33,6 @@  #include <sys/stat.h>  #include <string.h>  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <limits.h>  #include <signal.h> @@ -47,6 +46,7 @@  #include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "pid.h" @@ -57,11 +57,11 @@ static pid_t read_pid(const char *fn, int fd) {      char t[20], *e;      uint32_t pid; -    assert(fn && fd >= 0); +    pa_assert(fn); +    pa_assert(fd >= 0);      if ((r = pa_loop_read(fd, t, sizeof(t)-1, NULL)) < 0) { -        pa_log_warn("WARNING: failed to read PID file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to read PID file '%s': %s", fn, pa_cstrerror(errno));          return (pid_t) -1;      } @@ -73,7 +73,7 @@ static pid_t read_pid(const char *fn, int fd) {          *e = 0;      if (pa_atou(t, &pid) < 0) { -        pa_log("WARNING: failed to parse PID file '%s'", fn); +        pa_log_warn("Failed to parse PID file '%s'", fn);          return (pid_t) -1;      } @@ -83,13 +83,22 @@ static pid_t read_pid(const char *fn, int fd) {  static int open_pid_file(const char *fn, int mode) {      int fd = -1; +    pa_assert(fn); +      for (;;) {          struct stat st; -        if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) { +        if ((fd = open(fn, mode +#ifdef O_NOCTTY +                       |O_NOCTTY +#endif +#ifdef O_NOFOLLOW +                       |O_NOFOLLOW +#endif +                       , S_IRUSR|S_IWUSR +             )) < 0) {              if (mode != O_RDONLY || errno != ENOENT) -                pa_log_warn("WARNING: failed to open PID file '%s': %s", -                    fn, pa_cstrerror(errno)); +                pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));              goto fail;          } @@ -98,8 +107,7 @@ static int open_pid_file(const char *fn, int mode) {              goto fail;          if (fstat(fd, &st) < 0) { -            pa_log_warn("WARNING: failed to fstat() PID file '%s': %s", -                fn, pa_cstrerror(errno)); +            pa_log_warn("Failed to fstat() PID file '%s': %s", fn, pa_cstrerror(errno));              goto fail;          } @@ -110,9 +118,9 @@ static int open_pid_file(const char *fn, int mode) {          if (pa_lock_fd(fd, 0) < 0)              goto fail; -        if (close(fd) < 0) { -            pa_log_warn("WARNING: failed to close file '%s': %s", -                fn, pa_cstrerror(errno)); +        if (pa_close(fd) < 0) { +            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno)); +            fd = -1;              goto fail;          } @@ -125,7 +133,7 @@ fail:      if (fd >= 0) {          pa_lock_fd(fd, 0); -        close(fd); +        pa_close(fd);      }      return -1; @@ -150,7 +158,7 @@ int pa_pid_file_create(void) {          goto fail;      if ((pid = read_pid(fn, fd)) == (pid_t) -1) -        pa_log("corrupt PID file, overwriting."); +        pa_log_warn("Corrupt PID file, overwriting.");      else if (pid > 0) {  #ifdef OS_IS_WIN32          if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { @@ -158,25 +166,24 @@ int pa_pid_file_create(void) {  #else          if (kill(pid, 0) >= 0 || errno != ESRCH) {  #endif -            pa_log("daemon already running."); +            pa_log("Daemon already running.");              goto fail;          } -        pa_log("stale PID file, overwriting."); +        pa_log_warn("Stale PID file, overwriting.");      }      /* Overwrite the current PID file */      if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) { -        pa_log("failed to truncate PID file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } -    snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); +    pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());      l = strlen(t);      if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) { -        pa_log("failed to write PID file."); +        pa_log("Failed to write PID file.");          goto fail;      } @@ -185,7 +192,11 @@ int pa_pid_file_create(void) {  fail:      if (fd >= 0) {          pa_lock_fd(fd, 0); -        close(fd); + +        if (pa_close(fd) < 0) { +            pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno)); +            ret = -1; +        }      }      return ret; @@ -201,8 +212,7 @@ int pa_pid_file_remove(void) {      pa_runtime_path("pid", fn, sizeof(fn));      if ((fd = open_pid_file(fn, O_RDWR)) < 0) { -        pa_log_warn("WARNING: failed to open PID file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } @@ -210,13 +220,12 @@ int pa_pid_file_remove(void) {          goto fail;      if (pid != getpid()) { -        pa_log("WARNING: PID file '%s' not mine!", fn); +        pa_log("PID file '%s' not mine!", fn);          goto fail;      }      if (ftruncate(fd, 0) < 0) { -        pa_log_warn("WARNING: failed to truncate PID file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } @@ -227,8 +236,7 @@ int pa_pid_file_remove(void) {  #endif      if (unlink(fn) < 0) { -        pa_log_warn("WARNING: failed to remove PID file '%s': %s", -            fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } @@ -238,7 +246,11 @@ fail:      if (fd >= 0) {          pa_lock_fd(fd, 0); -        close(fd); + +        if (pa_close(fd) < 0) { +            pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno)); +            ret = -1; +        }      }      return ret; @@ -280,7 +292,7 @@ fail:      if (fd >= 0) {          pa_lock_fd(fd, 0); -        close(fd); +        pa_close(fd);      }      return ret; diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c index 7f6bb2e9..e614c9c6 100644 --- a/src/pulsecore/pipe.c +++ b/src/pulsecore/pipe.c @@ -149,14 +149,14 @@ int pipe(int filedes[2]) {      return 0;  error: -	if (listener >= 0) -		pa_close(listener); -	if (filedes[0] >= 0) -		pa_close(filedes[0]); -	if (filedes[1] >= 0) -		pa_close(filedes[0]); - -	return -1; +        if (listener >= 0) +                pa_close(listener); +        if (filedes[0] >= 0) +                pa_close(filedes[0]); +        if (filedes[1] >= 0) +                pa_close(filedes[0]); + +        return -1;  }  #endif /* HAVE_PIPE */ diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 76edd27a..5d3c2d39 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -26,7 +26,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <stdio.h>  #include <string.h> @@ -34,53 +33,106 @@  #include <pulsecore/sink-input.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/thread-mq.h>  #include "play-memblockq.h" -static void sink_input_kill(pa_sink_input *i) { -    pa_memblockq *q; -    assert(i); -    assert(i->userdata); +typedef struct memblockq_stream { +    pa_msgobject parent; +    pa_core *core; +    pa_sink_input *sink_input; +    pa_memblockq *memblockq; +} memblockq_stream; -    q = i->userdata; +enum { +    MEMBLOCKQ_STREAM_MESSAGE_UNLINK, +}; -    pa_sink_input_disconnect(i); -    pa_sink_input_unref(i); +PA_DECLARE_CLASS(memblockq_stream); +#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject); + +static void memblockq_stream_unlink(memblockq_stream *u) { +    pa_assert(u); + +    if (!u->sink_input) +        return; + +    pa_sink_input_unlink(u->sink_input); -    pa_memblockq_free(q); +    pa_sink_input_unref(u->sink_input); +    u->sink_input = NULL; + +    memblockq_stream_unref(u);  } -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { -    pa_memblockq *q; -    assert(i); -    assert(chunk); -    assert(i->userdata); +static void memblockq_stream_free(pa_object *o) { +    memblockq_stream *u = MEMBLOCKQ_STREAM(o); +    pa_assert(u); + +    memblockq_stream_unlink(u); -    q = i->userdata; +    if (u->memblockq) +        pa_memblockq_free(u->memblockq); -    return pa_memblockq_peek(q, chunk); +    pa_xfree(u);  } -static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { -    sink_input_kill(i); +static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    memblockq_stream *u = MEMBLOCKQ_STREAM(o); +    memblockq_stream_assert_ref(u); + +    switch (code) { +        case MEMBLOCKQ_STREAM_MESSAGE_UNLINK: +            memblockq_stream_unlink(u); +            break; +    } + +    return 0; +} + +static void sink_input_kill_cb(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));  } -static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { -    pa_memblockq *q; +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    memblockq_stream *u; -    assert(i); -    assert(length > 0); -    assert( i->userdata); +    pa_assert(i); +    pa_assert(chunk); +    u = MEMBLOCKQ_STREAM(i->userdata); +    memblockq_stream_assert_ref(u); -    q = i->userdata; +    if (!u->memblockq) +        return -1; -    pa_memblockq_drop(q, chunk, length); +    if (pa_memblockq_peek(u->memblockq, chunk) < 0) { +        pa_memblockq_free(u->memblockq); +        u->memblockq = NULL; +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); +        return -1; +    } -    if (pa_memblockq_get_length(q) <= 0) -        pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); +    return 0;  } -int pa_play_memblockq( +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    memblockq_stream *u; + +    pa_assert(i); +    pa_assert(length > 0); +    u = MEMBLOCKQ_STREAM(i->userdata); +    memblockq_stream_assert_ref(u); + +    if (!u->memblockq) +        return; + +    pa_memblockq_drop(u->memblockq, length); +} + +pa_sink_input* pa_memblockq_sink_input_new(          pa_sink *sink,          const char *name,          const pa_sample_spec *ss, @@ -88,41 +140,97 @@ int pa_play_memblockq(          pa_memblockq *q,          pa_cvolume *volume) { -    pa_sink_input *si; +    memblockq_stream *u = NULL;      pa_sink_input_new_data data; -    assert(sink); -    assert(ss); -    assert(q); +    pa_assert(sink); +    pa_assert(ss); -    if (pa_memblockq_get_length(q) <= 0) { +    /* We allow creating this stream with no q set, so that it can be +     * filled in later */ + +    if (q && pa_memblockq_get_length(q) <= 0) {          pa_memblockq_free(q); -        return 0; +        return NULL;      }      if (volume && pa_cvolume_is_muted(volume)) {          pa_memblockq_free(q); -        return 0; +        return NULL;      } +    u = pa_msgobject_new(memblockq_stream); +    u->parent.parent.free = memblockq_stream_free; +    u->parent.process_msg = memblockq_stream_process_msg; +    u->core = sink->core; +    u->sink_input = NULL; +    u->memblockq = q; +      pa_sink_input_new_data_init(&data);      data.sink = sink;      data.name = name;      data.driver = __FILE__; -    pa_sink_input_new_data_set_channel_map(&data, map);      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); -    if (!(si = pa_sink_input_new(sink->core, &data, 0))) -        return -1; +    if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0))) +        goto fail; + +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb; +    u->sink_input->userdata = u; + +    if (q) +        pa_memblockq_prebuf_disable(q); -    si->peek = sink_input_peek; -    si->drop = sink_input_drop; -    si->kill = sink_input_kill; +    /* The reference to u is dangling here, because we want +     * to keep this stream around until it is fully played. */ -    si->userdata = q; +    /* This sink input is not "put" yet, i.e. pa_sink_input_put() has +     * not been called! */ -    pa_sink_notify(si->sink); +    return pa_sink_input_ref(u->sink_input); + +fail: +    if (u) +        memblockq_stream_unref(u); + +    return NULL; +} + +int pa_play_memblockq( +        pa_sink *sink, +        const char *name, +        const pa_sample_spec *ss, +        const pa_channel_map *map, +        pa_memblockq *q, +        pa_cvolume *volume) { + +    pa_sink_input *i; + +    pa_assert(sink); +    pa_assert(ss); +    pa_assert(q); + +    if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume))) +        return -1; + +    pa_sink_input_put(i); +    pa_sink_input_unref(i);      return 0;  } + +void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { +    memblockq_stream *u; + +    pa_sink_input_assert_ref(i); +    u = MEMBLOCKQ_STREAM(i->userdata); +    memblockq_stream_assert_ref(u); + +    if (u->memblockq) +        pa_memblockq_free(u->memblockq); +    u->memblockq = q; +} diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h index 8248e859..d8790316 100644 --- a/src/pulsecore/play-memblockq.h +++ b/src/pulsecore/play-memblockq.h @@ -27,6 +27,16 @@  #include <pulsecore/sink.h>  #include <pulsecore/memblockq.h> +pa_sink_input* pa_memblockq_sink_input_new( +        pa_sink *sink, +        const char *name, +        const pa_sample_spec *ss, +        const pa_channel_map *map, +        pa_memblockq *q, +        pa_cvolume *volume); + +void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q); +  int pa_play_memblockq(      pa_sink *sink,      const char *name, diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index 9132e294..6aaec567 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -26,7 +26,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <stdio.h>  #include <string.h> @@ -34,53 +33,108 @@  #include <pulsecore/sink-input.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/thread-mq.h>  #include "play-memchunk.h" -static void sink_input_kill(pa_sink_input *i) { -    pa_memchunk *c; -    assert(i && i->userdata); -    c = i->userdata; +typedef struct memchunk_stream { +    pa_msgobject parent; +    pa_core *core; +    pa_sink_input *sink_input; +    pa_memchunk memchunk; +} memchunk_stream; -    pa_sink_input_disconnect(i); -    pa_sink_input_unref(i); +enum { +    MEMCHUNK_STREAM_MESSAGE_UNLINK, +}; -    pa_memblock_unref(c->memblock); -    pa_xfree(c); +PA_DECLARE_CLASS(memchunk_stream); +#define MEMCHUNK_STREAM(o) (memchunk_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(memchunk_stream, pa_msgobject); + +static void memchunk_stream_unlink(memchunk_stream *u) { +    pa_assert(u); + +    if (!u->sink_input) +        return; + +    pa_sink_input_unlink(u->sink_input); + +    pa_sink_input_unref(u->sink_input); +    u->sink_input = NULL; + +    memchunk_stream_unref(u);  } -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { -    pa_memchunk *c; -    assert(i && chunk && i->userdata); -    c = i->userdata; +static void memchunk_stream_free(pa_object *o) { +    memchunk_stream *u = MEMCHUNK_STREAM(o); +    pa_assert(u); -    if (c->length <= 0) -        return -1; +    memchunk_stream_unlink(u); + +    if (u->memchunk.memblock) +        pa_memblock_unref(u->memchunk.memblock); + +    pa_xfree(u); +} + +static int memchunk_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    memchunk_stream *u = MEMCHUNK_STREAM(o); +    memchunk_stream_assert_ref(u); -    assert(c->memblock && c->memblock->length); -    *chunk = *c; -    pa_memblock_ref(c->memblock); +    switch (code) { +        case MEMCHUNK_STREAM_MESSAGE_UNLINK: +            memchunk_stream_unlink(u); +            break; +    }      return 0;  } -static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { -    sink_input_kill(i); +static void sink_input_kill_cb(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    memchunk_stream_unlink(MEMCHUNK_STREAM(i->userdata));  } -static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { -    pa_memchunk *c; -    assert(i && length && i->userdata); -    c = i->userdata; +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    memchunk_stream *u; + +    pa_assert(i); +    pa_assert(chunk); +    u = MEMCHUNK_STREAM(i->userdata); +    memchunk_stream_assert_ref(u); -    assert(!memcmp(chunk, c, sizeof(chunk))); -    assert(length <= c->length); +    if (!u->memchunk.memblock) +        return -1; + +    if (u->memchunk.length <= 0) { +        pa_memblock_unref(u->memchunk.memblock); +        u->memchunk.memblock = NULL; +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMCHUNK_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); +        return -1; +    } -    c->length -= length; -    c->index += length; +    pa_assert(u->memchunk.memblock); +    *chunk = u->memchunk; +    pa_memblock_ref(chunk->memblock); -    if (c->length <= 0) -        pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); +    return 0; +} + +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    memchunk_stream *u; + +    pa_assert(i); +    pa_assert(length > 0); +    u = MEMCHUNK_STREAM(i->userdata); +    memchunk_stream_assert_ref(u); + +    if (length < u->memchunk.length) { +        u->memchunk.length -= length; +        u->memchunk.index += length; +    } else +        u->memchunk.length = 0;  }  int pa_play_memchunk( @@ -91,38 +145,52 @@ int pa_play_memchunk(          const pa_memchunk *chunk,          pa_cvolume *volume) { -    pa_sink_input *si; -    pa_memchunk *nchunk; +    memchunk_stream *u = NULL;      pa_sink_input_new_data data; -    assert(sink); -    assert(ss); -    assert(chunk); +    pa_assert(sink); +    pa_assert(ss); +    pa_assert(chunk);      if (volume && pa_cvolume_is_muted(volume))          return 0; +    pa_memchunk_will_need(chunk); + +    u = pa_msgobject_new(memchunk_stream); +    u->parent.parent.free = memchunk_stream_free; +    u->parent.process_msg = memchunk_stream_process_msg; +    u->core = sink->core; +    u->memchunk = *chunk; +    pa_memblock_ref(u->memchunk.memblock); +      pa_sink_input_new_data_init(&data);      data.sink = sink; -    data.name = name;      data.driver = __FILE__; +    data.name = name;      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); -    if (!(si = pa_sink_input_new(sink->core, &data, 0))) -        return -1; - -    si->peek = sink_input_peek; -    si->drop = sink_input_drop; -    si->kill = sink_input_kill; +    if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0))) +        goto fail; -    si->userdata = nchunk = pa_xnew(pa_memchunk, 1); -    *nchunk = *chunk; +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb; +    u->sink_input->userdata = u; -    pa_memblock_ref(chunk->memblock); +    pa_sink_input_put(u->sink_input); -    pa_sink_notify(si->sink); +    /* The reference to u is dangling here, because we want to keep +     * this stream around until it is fully played. */      return 0; + +fail: +    if (u) +        memchunk_stream_unref(u); + +    return -1;  } + diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c index 2f8eae89..288f7dfb 100644 --- a/src/pulsecore/poll.c +++ b/src/pulsecore/poll.c @@ -45,7 +45,7 @@  #include "winsock.h" -#ifndef HAVE_SYS_POLL_H +#ifndef HAVE_POLL_H  #include <pulsecore/core-util.h> diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c index 4a39f0fb..cbf748df 100644 --- a/src/pulsecore/props.c +++ b/src/pulsecore/props.c @@ -21,11 +21,13 @@    USA.  ***/ -#include <assert.h> +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif  #include <pulse/xmalloc.h> -  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "props.h" @@ -37,9 +39,11 @@ typedef struct pa_property {  /* Allocate a new property object */  static pa_property* property_new(const char *name, void *data) {      pa_property* p; -    assert(name && data); -    p = pa_xmalloc(sizeof(pa_property)); +    pa_assert(name); +    pa_assert(data); + +    p = pa_xnew(pa_property, 1);      p->name = pa_xstrdup(name);      p->data = data; @@ -48,7 +52,7 @@ static pa_property* property_new(const char *name, void *data) {  /* Free a property object */  static void property_free(pa_property *p) { -    assert(p); +    pa_assert(p);      pa_xfree(p->name);      pa_xfree(p); @@ -56,7 +60,10 @@ static void property_free(pa_property *p) {  void* pa_property_get(pa_core *c, const char *name) {      pa_property *p; -    assert(c && name && c->properties); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(c->properties);      if (!(p = pa_hashmap_get(c->properties, name)))          return NULL; @@ -66,7 +73,11 @@ void* pa_property_get(pa_core *c, const char *name) {  int pa_property_set(pa_core *c, const char *name, void *data) {      pa_property *p; -    assert(c && name && data && c->properties); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(data); +    pa_assert(c->properties);      if (pa_hashmap_get(c->properties, name))          return -1; @@ -78,7 +89,10 @@ int pa_property_set(pa_core *c, const char *name, void *data) {  int pa_property_remove(pa_core *c, const char *name) {      pa_property *p; -    assert(c && name && c->properties); + +    pa_assert(c); +    pa_assert(name); +    pa_assert(c->properties);      if (!(p = pa_hashmap_remove(c->properties, name)))          return -1; @@ -88,18 +102,18 @@ int pa_property_remove(pa_core *c, const char *name) {  }  void pa_property_init(pa_core *c) { -    assert(c); +    pa_assert(c);      c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);  }  void pa_property_cleanup(pa_core *c) { -    assert(c); +    pa_assert(c);      if (!c->properties)          return; -    assert(!pa_hashmap_size(c->properties)); +    pa_assert(!pa_hashmap_size(c->properties));      pa_hashmap_free(c->properties, NULL, NULL);      c->properties = NULL; @@ -109,14 +123,17 @@ void pa_property_cleanup(pa_core *c) {  void pa_property_dump(pa_core *c, pa_strbuf *s) {      void *state = NULL;      pa_property *p; -    assert(c && s); + +    pa_assert(c); +    pa_assert(s);      while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))          pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);  }  int pa_property_replace(pa_core *c, const char *name, void *data) { -    assert(c && name); +    pa_assert(c); +    pa_assert(name);      pa_property_remove(c, name);      return pa_property_set(c, name, data); diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c index 1d543ae5..ceb6ae4d 100644 --- a/src/pulsecore/protocol-cli.c +++ b/src/pulsecore/protocol-cli.c @@ -25,13 +25,13 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h>  #include <pulsecore/cli.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "protocol-cli.h" @@ -47,7 +47,8 @@ struct pa_protocol_cli {  static void cli_eof_cb(pa_cli*c, void*userdata) {      pa_protocol_cli *p = userdata; -    assert(p); +    pa_assert(p); +      pa_idxset_remove_by_data(p->connections, c, NULL);      pa_cli_free(c);  } @@ -55,7 +56,10 @@ static void cli_eof_cb(pa_cli*c, void*userdata) {  static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {      pa_protocol_cli *p = userdata;      pa_cli *c; -    assert(s && io && p); + +    pa_assert(s); +    pa_assert(io); +    pa_assert(p);      if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {          pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -64,7 +68,6 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)      }      c = pa_cli_new(p->core, io, p->module); -    assert(c);      pa_cli_set_eof_callback(c, cli_eof_cb, p);      pa_idxset_put(p->connections, c, NULL); @@ -72,9 +75,11 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)  pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {      pa_protocol_cli* p; -    assert(core && server); -    p = pa_xmalloc(sizeof(pa_protocol_cli)); +    pa_core_assert_ref(core); +    pa_assert(server); + +    p = pa_xnew(pa_protocol_cli, 1);      p->module = m;      p->core = core;      p->server = server; @@ -86,12 +91,13 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa  }  static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { -    assert(p); +    pa_assert(p); +      pa_cli_free(p);  }  void pa_protocol_cli_free(pa_protocol_cli *p) { -    assert(p); +    pa_assert(p);      pa_idxset_free(p->connections, free_connection, NULL);      pa_socket_server_unref(p->server); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 49a78d41..76ba9dd0 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -29,7 +29,6 @@  #include <errno.h>  #include <string.h>  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <limits.h> @@ -53,6 +52,8 @@  #include <pulsecore/core-util.h>  #include <pulsecore/core-error.h>  #include <pulsecore/ipacl.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread-mq.h>  #include "endianmacros.h" @@ -77,13 +78,15 @@  /* This is heavily based on esound's code */ -struct connection { +typedef struct connection { +    pa_msgobject parent; +      uint32_t index; -    int dead; +    pa_bool_t dead;      pa_protocol_esound *protocol;      pa_iochannel *io;      pa_client *client; -    int authorized, swap_byte_order; +    pa_bool_t authorized, swap_byte_order;      void *write_data;      size_t write_data_alloc, write_data_index, write_data_length;      void *read_data; @@ -100,6 +103,7 @@ struct connection {      struct {          pa_memblock *current_memblock;          size_t memblock_index, fragment_size; +        pa_atomic_t missing;      } playback;      struct { @@ -109,46 +113,62 @@ struct connection {      } scache;      pa_time_event *auth_timeout_event; -}; +} connection; + +PA_DECLARE_CLASS(connection); +#define CONNECTION(o) (connection_cast(o)) +static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);  struct pa_protocol_esound { -    int public;      pa_module *module;      pa_core *core; +    int public;      pa_socket_server *server;      pa_idxset *connections; +      char *sink_name, *source_name;      unsigned n_player;      uint8_t esd_key[ESD_KEY_LEN];      pa_ip_acl *auth_ip_acl;  }; +enum { +    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */ +    SINK_INPUT_MESSAGE_DISABLE_PREBUF +}; + +enum { +    CONNECTION_MESSAGE_REQUEST_DATA, +    CONNECTION_MESSAGE_POST_DATA, +    CONNECTION_MESSAGE_UNLINK_CONNECTION +}; +  typedef struct proto_handler {      size_t data_length; -    int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); +    int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);      const char *description;  } esd_proto_handler_info_t; -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); +static void sink_input_drop_cb(pa_sink_input *i, size_t length); +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);  static void sink_input_kill_cb(pa_sink_input *i); -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); +static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);  static pa_usec_t source_output_get_latency_cb(pa_source_output *o);  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);  static void source_output_kill_cb(pa_source_output *o); -static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_standby_or_resume(struct connection *c, 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); +static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_record(connection *c, 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); +static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_all_info(connection *c, 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); +static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_free_or_play(connection *c, 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); +static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);  /* the big map of protocol handler info */  static struct proto_handler proto_map[ESD_PROTO_MAX] = { @@ -185,25 +205,56 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {      { 0,                              esd_proto_get_latency, "get latency" }  }; -static void connection_free(struct connection *c) { -    assert(c); -    pa_idxset_remove_by_data(c->protocol->connections, c, NULL); +static void connection_unlink(connection *c) { +    pa_assert(c); -    if (c->state == ESD_STREAMING_DATA) -        c->protocol->n_player--; - -    pa_client_free(c->client); +    if (!c->protocol) +        return;      if (c->sink_input) { -        pa_sink_input_disconnect(c->sink_input); +        pa_sink_input_unlink(c->sink_input);          pa_sink_input_unref(c->sink_input); +        c->sink_input = NULL;      }      if (c->source_output) { -        pa_source_output_disconnect(c->source_output); +        pa_source_output_unlink(c->source_output);          pa_source_output_unref(c->source_output); +        c->source_output = NULL;      } +    if (c->client) { +        pa_client_free(c->client); +        c->client = NULL; +    } + +    if (c->state == ESD_STREAMING_DATA) +        c->protocol->n_player--; + +    if (c->io) { +        pa_iochannel_free(c->io); +        c->io = NULL; +    } + +    if (c->defer_event) { +        c->protocol->core->mainloop->defer_free(c->defer_event); +        c->defer_event = NULL; +    } + +    if (c->auth_timeout_event) { +        c->protocol->core->mainloop->time_free(c->auth_timeout_event); +        c->auth_timeout_event = NULL; +    } + +    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c); +    c->protocol = NULL; +    connection_unref(c); +} + +static void connection_free(pa_object *obj) { +    connection *c = CONNECTION(obj); +    pa_assert(c); +      if (c->input_memblockq)          pa_memblockq_free(c->input_memblockq);      if (c->output_memblockq) @@ -215,54 +266,44 @@ static void connection_free(struct connection *c) {      pa_xfree(c->read_data);      pa_xfree(c->write_data); -    if (c->io) -        pa_iochannel_free(c->io); - -    if (c->defer_event) -        c->protocol->core->mainloop->defer_free(c->defer_event); -      if (c->scache.memchunk.memblock)          pa_memblock_unref(c->scache.memchunk.memblock);      pa_xfree(c->scache.name); -    if (c->auth_timeout_event) -        c->protocol->core->mainloop->time_free(c->auth_timeout_event); -      pa_xfree(c->original_name);      pa_xfree(c);  } -static void connection_write_prepare(struct connection *c, size_t length) { +static void connection_write_prepare(connection *c, size_t length) {      size_t t; -    assert(c); +    pa_assert(c);      t = c->write_data_length+length;      if (c->write_data_alloc < t)          c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t); -    assert(c->write_data); +    pa_assert(c->write_data);  } -static void connection_write(struct connection *c, const void *data, size_t length) { +static void connection_write(connection *c, const void *data, size_t length) {      size_t i; -    assert(c); +    pa_assert(c); -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);      c->protocol->core->mainloop->defer_enable(c->defer_event, 1);      connection_write_prepare(c, length); -    assert(c->write_data); +    pa_assert(c->write_data);      i = c->write_data_length;      c->write_data_length += length; -    memcpy((char*)c->write_data + i, data, length); +    memcpy((uint8_t*) c->write_data + i, data, length);  } -static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) { -    assert(ss); +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;      if ((format & ESD_MASK_BITS) == ESD_BITS16) @@ -289,11 +330,13 @@ static int format_native2esd(pa_sample_spec *ss) {  /*** esound commands ***/ -static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {      uint32_t ekey;      int ok; -    assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));      if (!c->authorized) {          if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { @@ -301,7 +344,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req              return -1;          } -        c->authorized = 1; +        c->authorized = TRUE;          if (c->auth_timeout_event) {              c->protocol->core->mainloop->time_free(c->auth_timeout_event);              c->auth_timeout_event = NULL; @@ -312,11 +355,11 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req      memcpy(&ekey, data, sizeof(uint32_t));      if (ekey == ESD_ENDIAN_KEY) -        c->swap_byte_order = 0; +        c->swap_byte_order = FALSE;      else if (ekey == ESD_SWAP_ENDIAN_KEY) -        c->swap_byte_order = 1; +        c->swap_byte_order = TRUE;      else { -        pa_log("client sent invalid endian key"); +        pa_log_warn("Client sent invalid endian key");          return -1;      } @@ -325,7 +368,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req      return 0;  } -static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED 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; @@ -333,15 +376,17 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t      pa_sink *sink = NULL;      pa_sink_input_new_data sdata; -    assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX)); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));      memcpy(&format, data, sizeof(int32_t)); -    format = MAYBE_INT32_SWAP(c->swap_byte_order, format); -    data = (const char*)data + sizeof(int32_t); +    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format); +    data = (const char*) data + sizeof(int32_t);      memcpy(&rate, data, sizeof(int32_t)); -    rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); -    data = (const char*)data + sizeof(int32_t); +    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate); +    data = (const char*) data + sizeof(int32_t);      ss.rate = rate;      format_esd2native(format, c->swap_byte_order, &ss); @@ -362,7 +407,7 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t      c->original_name = pa_xstrdup(name); -    assert(!c->sink_input && !c->input_memblockq); +    pa_assert(!c->sink_input && !c->input_memblockq);      pa_sink_input_new_data_init(&sdata);      sdata.sink = sink; @@ -385,22 +430,26 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t              l/PLAYBACK_BUFFER_FRAGMENTS,              NULL);      pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); -    c->playback.fragment_size = l/10; +    c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; +    c->sink_input->parent.process_msg = sink_input_process_msg;      c->sink_input->peek = sink_input_peek_cb;      c->sink_input->drop = sink_input_drop_cb;      c->sink_input->kill = sink_input_kill_cb; -    c->sink_input->get_latency = sink_input_get_latency_cb;      c->sink_input->userdata = c;      c->state = ESD_STREAMING_DATA;      c->protocol->n_player++; +    pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); + +    pa_sink_input_put(c->sink_input); +      return 0;  } -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { +static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {      char name[ESD_NAME_MAX], *utf8_name;      int32_t format, rate;      pa_source *source = NULL; @@ -408,15 +457,17 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co      size_t l;      pa_source_output_new_data sdata; -    assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX)); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));      memcpy(&format, data, sizeof(int32_t)); -    format = MAYBE_INT32_SWAP(c->swap_byte_order, format); -    data = (const char*)data + sizeof(int32_t); +    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format); +    data = (const char*) data + sizeof(int32_t);      memcpy(&rate, data, sizeof(int32_t)); -    rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); -    data = (const char*)data + sizeof(int32_t); +    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate); +    data = (const char*) data + sizeof(int32_t);      ss.rate = rate;      format_esd2native(format, c->swap_byte_order, &ss); @@ -436,7 +487,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co              return -1;          }      } else { -        assert(request == ESD_PROTO_STREAM_REC); +        pa_assert(request == ESD_PROTO_STREAM_REC);          if (c->protocol->source_name) {              if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) { @@ -455,7 +506,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co      c->original_name = pa_xstrdup(name); -    assert(!c->output_memblockq && !c->source_output); +    pa_assert(!c->output_memblockq && !c->source_output);      pa_source_output_new_data_init(&sdata);      sdata.source = source; @@ -488,14 +539,18 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co      c->protocol->n_player++; +    pa_source_output_put(c->source_output); +      return 0;  } -static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {      pa_sink *sink;      int32_t latency; -    assert(c && !data && length == 0); +    connection_ref(c); +    pa_assert(!data); +    pa_assert(length == 0);      if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))          latency = 0; @@ -504,17 +559,19 @@ static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t          latency = (int) ((usec*44100)/1000000);      } -    latency = MAYBE_INT32_SWAP(c->swap_byte_order, latency); +    latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);      connection_write(c, &latency, sizeof(int32_t));      return 0;  } -static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_server_info(connection *c, PA_GCC_UNUSED 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; -    assert(c && data && length == sizeof(int32_t)); +    connection_ref(c); +    pa_assert(data); +    pa_assert(length == sizeof(int32_t));      if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {          rate = sink->sample_spec.rate; @@ -525,22 +582,24 @@ static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t      response = 0;      connection_write(c, &response, sizeof(int32_t)); -    rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); +    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);      connection_write(c, &rate, sizeof(int32_t)); -    format = MAYBE_INT32_SWAP(c->swap_byte_order, format); +    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);      connection_write(c, &format, sizeof(int32_t));      return 0;  } -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { +static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {      size_t t, k, s; -    struct connection *conn; +    connection *conn;      uint32_t idx = PA_IDXSET_INVALID;      unsigned nsamples;      char terminator[sizeof(int32_t)*6+ESD_NAME_MAX]; -    assert(c && data && length == sizeof(int32_t)); +    connection_ref(c); +    pa_assert(data); +    pa_assert(length == sizeof(int32_t));      if (esd_proto_server_info(c, request, data, length) < 0)          return -1; @@ -561,7 +620,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v          if (conn->state != ESD_STREAMING_DATA)              continue; -        assert(t >= k*2+s); +        pa_assert(t >= k*2+s);          if (conn->sink_input) {              pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input); @@ -572,7 +631,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v          }          /* id */ -        id = MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1)); +        id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));          connection_write(c, &id, sizeof(int32_t));          /* name */ @@ -584,25 +643,25 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v          connection_write(c, name, ESD_NAME_MAX);          /* rate */ -        rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); +        rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);          connection_write(c, &rate, sizeof(int32_t));          /* left */ -        lvolume = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume); +        lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);          connection_write(c, &lvolume, sizeof(int32_t));          /*right*/ -        rvolume = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume); +        rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);          connection_write(c, &rvolume, sizeof(int32_t));          /*format*/ -        format = MAYBE_INT32_SWAP(c->swap_byte_order, format); +        format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);          connection_write(c, &format, sizeof(int32_t));          t -= k;      } -    assert(t == s*(nsamples+1)+k); +    pa_assert(t == s*(nsamples+1)+k);      t -= k;      connection_write(c, terminator, k); @@ -615,10 +674,10 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v              int32_t id, rate, lvolume, rvolume, format, len;              char name[ESD_NAME_MAX]; -            assert(t >= s*2); +            pa_assert(t >= s*2);              /* id */ -            id = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1)); +            id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));              connection_write(c, &id, sizeof(int32_t));              /* name */ @@ -626,57 +685,59 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v              if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)                  strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);              else -                snprintf(name, ESD_NAME_MAX, "native.%s", ce->name); +                pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);              connection_write(c, name, ESD_NAME_MAX);              /* rate */ -            rate = MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate); +            rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);              connection_write(c, &rate, sizeof(int32_t));              /* left */ -            lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); +            lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);              connection_write(c, &lvolume, sizeof(int32_t));              /*right*/ -            rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); +            rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);              connection_write(c, &rvolume, sizeof(int32_t));              /*format*/ -            format = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec)); +            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));              connection_write(c, &format, sizeof(int32_t));              /*length*/ -            len = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length); +            len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);              connection_write(c, &len, sizeof(int32_t));              t -= s;          }      } -    assert(t == s); +    pa_assert(t == s);      connection_write(c, terminator, s);      return 0;  } -static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {      int32_t ok;      uint32_t idx, lvolume, rvolume; -    struct connection *conn; +    connection *conn; -    assert(c && data && length == sizeof(int32_t)*3); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == sizeof(int32_t)*3);      memcpy(&idx, data, sizeof(uint32_t)); -    idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1; +    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;      data = (const char*)data + sizeof(uint32_t);      memcpy(&lvolume, data, sizeof(uint32_t)); -    lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume); +    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);      data = (const char*)data + sizeof(uint32_t);      memcpy(&rvolume, data, sizeof(uint32_t)); -    rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume); +    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);      data = (const char*)data + sizeof(uint32_t);      if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) { @@ -694,20 +755,22 @@ static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t      return 0;  } -static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {      pa_sample_spec ss;      int32_t format, rate, sc_length;      uint32_t idx;      char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; -    assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int32_t))); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));      memcpy(&format, data, sizeof(int32_t)); -    format = MAYBE_INT32_SWAP(c->swap_byte_order, format); +    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);      data = (const char*)data + sizeof(int32_t);      memcpy(&rate, data, sizeof(int32_t)); -    rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); +    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);      data = (const char*)data + sizeof(int32_t);      ss.rate = rate; @@ -716,7 +779,7 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_      CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");      memcpy(&sc_length, data, sizeof(int32_t)); -    sc_length = MAYBE_INT32_SWAP(c->swap_byte_order, sc_length); +    sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);      data = (const char*)data + sizeof(int32_t);      CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length); @@ -727,12 +790,12 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_      CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); -    assert(!c->scache.memchunk.memblock); +    pa_assert(!c->scache.memchunk.memblock);      c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);      c->scache.memchunk.index = 0;      c->scache.memchunk.length = sc_length;      c->scache.sample_spec = ss; -    assert(!c->scache.name); +    pa_assert(!c->scache.name);      c->scache.name = pa_xstrdup(name);      c->state = ESD_CACHING_SAMPLE; @@ -745,12 +808,14 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_      return 0;  } -static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED 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]; -    assert(c && data && length == ESD_NAME_MAX); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == ESD_NAME_MAX);      strcpy(name, SCACHE_PREFIX);      strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); @@ -767,15 +832,17 @@ static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto      return 0;  } -static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { +static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {      int32_t ok;      const char *name;      uint32_t idx; -    assert(c && data && length == sizeof(int32_t)); +    connection_assert_ref(c); +    pa_assert(data); +    pa_assert(length == sizeof(int32_t));      memcpy(&idx, data, sizeof(uint32_t)); -    idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1; +    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;      ok = 0; @@ -787,7 +854,7 @@ static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t reque                  if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)                      ok = idx + 1;          } else { -            assert(request == ESD_PROTO_SAMPLE_FREE); +            pa_assert(request == ESD_PROTO_SAMPLE_FREE);              if (pa_scache_remove_item(c->protocol->core, name) >= 0)                  ok = idx + 1; @@ -799,9 +866,11 @@ static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t reque      return 0;  } -static int esd_proto_standby_or_resume(struct 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, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {      int32_t ok; +    connection_assert_ref(c); +      connection_write_prepare(c, sizeof(int32_t) * 2);      ok = 1; @@ -814,20 +883,21 @@ static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_p  /*** client callbacks ***/  static void client_kill_cb(pa_client *c) { -    assert(c && c->userdata); -    connection_free(c->userdata); +    pa_assert(c); + +    connection_unlink(CONNECTION(c->userdata));  }  /*** pa_iochannel callbacks ***/ -static int do_read(struct connection *c) { -    assert(c && c->io); +static int do_read(connection *c) { +    connection_assert_ref(c); -/*      pa_log("READ");  */ +/*     pa_log("READ"); */      if (c->state == ESD_NEXT_REQUEST) {          ssize_t r; -        assert(c->read_data_length < sizeof(c->request)); +        pa_assert(c->read_data_length < sizeof(c->request));          if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {              pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); @@ -837,7 +907,7 @@ static int do_read(struct connection *c) {          if ((c->read_data_length+= r) >= sizeof(c->request)) {              struct proto_handler *handler; -            c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request); +            c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);              if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {                  pa_log("recieved invalid request."); @@ -862,7 +932,7 @@ static int do_read(struct connection *c) {              } else {                  if (c->read_data_alloc < handler->data_length)                      c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length); -                assert(c->read_data); +                pa_assert(c->read_data);                  c->state = ESD_NEEDS_REQDATA;                  c->read_data_length = 0; @@ -873,18 +943,21 @@ static int do_read(struct connection *c) {          ssize_t r;          struct proto_handler *handler = proto_map+c->request; -        assert(handler->proc); +        pa_assert(handler->proc); -        assert(c->read_data && c->read_data_length < handler->data_length); +        pa_assert(c->read_data && c->read_data_length < handler->data_length);          if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { +            if (r < 0 && (errno == EINTR || errno == EAGAIN)) +                return 0; +              pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");              return -1;          }          if ((c->read_data_length += r) >= handler->data_length) {              size_t l = c->read_data_length; -            assert(handler->proc); +            pa_assert(handler->proc);              c->state = ESD_NEXT_REQUEST;              c->read_data_length = 0; @@ -894,16 +967,26 @@ static int do_read(struct connection *c) {          }      } else if (c->state == ESD_CACHING_SAMPLE) {          ssize_t r; +        void *p; -        assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length); +        pa_assert(c->scache.memchunk.memblock); +        pa_assert(c->scache.name); +        pa_assert(c->scache.memchunk.index < c->scache.memchunk.length); + +        p = pa_memblock_acquire(c->scache.memchunk.memblock); +        r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index); +        pa_memblock_release(c->scache.memchunk.memblock); + +        if (r <= 0) { +            if (r < 0 && (errno == EINTR || errno == EAGAIN)) +                return 0; -        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {              pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");              return -1;          }          c->scache.memchunk.index += r; -        assert(c->scache.memchunk.index <= c->scache.memchunk.length); +        pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);          if (c->scache.memchunk.index == c->scache.memchunk.length) {              uint32_t idx; @@ -928,31 +1011,39 @@ static int do_read(struct connection *c) {          pa_memchunk chunk;          ssize_t r;          size_t l; +        void *p; -        assert(c->input_memblockq); +        pa_assert(c->input_memblockq);  /*         pa_log("STREAMING_DATA"); */ -        if (!(l = pa_memblockq_missing(c->input_memblockq))) +        if (!(l = pa_atomic_load(&c->playback.missing)))              return 0;          if (l > c->playback.fragment_size)              l = c->playback.fragment_size;          if (c->playback.current_memblock) -            if (c->playback.current_memblock->length - c->playback.memblock_index < l) { +            if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {                  pa_memblock_unref(c->playback.current_memblock);                  c->playback.current_memblock = NULL;                  c->playback.memblock_index = 0;              }          if (!c->playback.current_memblock) { -            c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2); -            assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); +            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));              c->playback.memblock_index = 0;          } -        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { +        p = pa_memblock_acquire(c->playback.current_memblock); +        r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l); +        pa_memblock_release(c->playback.current_memblock); + +        if (r <= 0) { + +            if (r < 0 && (errno == EINTR || errno == EAGAIN)) +                return 0; +              pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");              return -1;          } @@ -960,29 +1051,30 @@ static int do_read(struct connection *c) {          chunk.memblock = c->playback.current_memblock;          chunk.index = c->playback.memblock_index;          chunk.length = r; -        assert(chunk.memblock);          c->playback.memblock_index += r; -        assert(c->input_memblockq); -        pa_memblockq_push_align(c->input_memblockq, &chunk); -        assert(c->sink_input); -        pa_sink_notify(c->sink_input->sink); +        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);      }      return 0;  } -static int do_write(struct connection *c) { -    assert(c && c->io); +static int do_write(connection *c) { +    connection_assert_ref(c);  /*     pa_log("WRITE"); */      if (c->write_data_length) {          ssize_t r; -        assert(c->write_data_index < c->write_data_length); +        pa_assert(c->write_data_index < c->write_data_length);          if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { + +            if (r < 0 && (errno == EINTR || errno == EAGAIN)) +                return 0; +              pa_log("write(): %s", pa_cstrerror(errno));              return -1;          } @@ -993,32 +1085,38 @@ static int do_write(struct connection *c) {      } else if (c->state == ESD_STREAMING_DATA && c->source_output) {          pa_memchunk chunk;          ssize_t r; +        void *p; -        assert(c->output_memblockq);          if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)              return 0; -        assert(chunk.memblock && chunk.length); +        pa_assert(chunk.memblock); +        pa_assert(chunk.length); + +        p = pa_memblock_acquire(chunk.memblock); +        r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length); +        pa_memblock_release(chunk.memblock); + +        pa_memblock_unref(chunk.memblock); + +        if (r < 0) { + +            if (r < 0 && (errno == EINTR || errno == EAGAIN)) +                return 0; -        if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { -            pa_memblock_unref(chunk.memblock);              pa_log("write(): %s", pa_cstrerror(errno));              return -1;          } -        pa_memblockq_drop(c->output_memblockq, &chunk, r); -        pa_memblock_unref(chunk.memblock); - -        pa_source_notify(c->source_output->source); +        pa_memblockq_drop(c->output_memblockq, r);      }      return 0;  } -static void do_work(struct connection *c) { -    assert(c); +static void do_work(connection *c) { +    connection_assert_ref(c); -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);      c->protocol->core->mainloop->defer_enable(c->defer_event, 0);      if (c->dead) @@ -1044,122 +1142,196 @@ static void do_work(struct connection *c) {  fail:      if (c->state == ESD_STREAMING_DATA && c->sink_input) { -        c->dead = 1; +        c->dead = TRUE;          pa_iochannel_free(c->io);          c->io = NULL; -        pa_memblockq_prebuf_disable(c->input_memblockq); -        pa_sink_notify(c->sink_input->sink); +        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);      } else -        connection_free(c); +        connection_unlink(c);  }  static void io_callback(pa_iochannel*io, void *userdata) { -    struct connection *c = userdata; -    assert(io && c && c->io == io); +    connection *c = CONNECTION(userdata); + +    connection_assert_ref(c); +    pa_assert(io);      do_work(c);  } -/*** defer callback ***/ -  static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { -    struct connection *c = userdata; -    assert(a && c && c->defer_event == e); +    connection *c = CONNECTION(userdata); -/*     pa_log("DEFER"); */ +    connection_assert_ref(c); +    pa_assert(e);      do_work(c);  } -/*** sink_input callbacks ***/ +static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    connection *c = CONNECTION(o); +    connection_assert_ref(c); -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { -    struct connection*c; -    assert(i && i->userdata && chunk); -    c = i->userdata; +    switch (code) { +        case CONNECTION_MESSAGE_REQUEST_DATA: +            do_work(c); +            break; -    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { +        case CONNECTION_MESSAGE_POST_DATA: +/*             pa_log("got data %u", chunk->length); */ +            pa_memblockq_push_align(c->output_memblockq, chunk); +            do_work(c); +            break; -        if (c->dead) -            connection_free(c); - -        return -1; +        case CONNECTION_MESSAGE_UNLINK_CONNECTION: +            connection_unlink(c); +            break;      }      return 0;  } -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { -    struct connection*c = i->userdata; -    assert(i && c && length); +/*** sink_input callbacks ***/ + +/* Called from thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_sink_input *i = PA_SINK_INPUT(o); +    connection*c; + +    pa_sink_input_assert_ref(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); + +    switch (code) { + +        case SINK_INPUT_MESSAGE_POST_DATA: { +            pa_assert(chunk); + +            /* New data from the main loop */ +            pa_memblockq_push_align(c->input_memblockq, chunk); + +/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ + +            return 0; +        } -/*     pa_log("DROP"); */ +        case SINK_INPUT_MESSAGE_DISABLE_PREBUF: { +            pa_memblockq_prebuf_disable(c->input_memblockq); +            return 0; +        } -    pa_memblockq_drop(c->input_memblockq, chunk, length); +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { +            pa_usec_t *r = userdata; -    /* do something */ -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); +            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -    if (!c->dead) -        c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +        } -/*     assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */ +        default: +            return pa_sink_input_process_msg(o, code, userdata, offset, chunk); +    }  } -static void sink_input_kill_cb(pa_sink_input *i) { -    assert(i && i->userdata); -    connection_free((struct connection *) i->userdata); +/* Called from thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    connection*c; +    int r; + +    pa_assert(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); +    pa_assert(chunk); + +    if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0 && c->dead) +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + +    return r; +} + +/* Called from thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    connection*c; +    size_t old, new; + +    pa_assert(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); +    pa_assert(length); + +    /*     pa_log("DROP"); */ + +    old = pa_memblockq_missing(c->input_memblockq); +    pa_memblockq_drop(c->input_memblockq, length); +    new = pa_memblockq_missing(c->input_memblockq); + +    if (new > old) { +        if (pa_atomic_add(&c->playback.missing, new - old) <= 0) +            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); +    }  } -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { -    struct connection*c = i->userdata; -    assert(i && c); -    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +static void sink_input_kill_cb(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    connection_unlink(CONNECTION(i->userdata));  }  /*** source_output callbacks ***/ +/* Called from thread context */  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { -    struct connection *c = o->userdata; -    assert(o && c && chunk); - -    pa_memblockq_push(c->output_memblockq, chunk); +    connection *c; -    /* do something */ -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); +    pa_assert(o); +    c = CONNECTION(o->userdata); +    pa_assert(c); +    pa_assert(chunk); -    if (!c->dead) -        c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);  }  static void source_output_kill_cb(pa_source_output *o) { -    assert(o && o->userdata); -    connection_free((struct connection *) o->userdata); +    pa_source_output_assert_ref(o); + +    connection_unlink(CONNECTION(o->userdata));  }  static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { -    struct connection*c = o->userdata; -    assert(o && c); +    connection*c; + +    pa_assert(o); +    c = CONNECTION(o->userdata); +    pa_assert(c); +      return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);  }  /*** socket server callback ***/  static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { -    struct connection *c = userdata; -    assert(m && tv && c && c->auth_timeout_event == e); +    connection *c = CONNECTION(userdata); + +    pa_assert(m); +    pa_assert(tv); +    connection_assert_ref(c); +    pa_assert(c->auth_timeout_event == e);      if (!c->authorized) -        connection_free(c); +        connection_unlink(c);  }  static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { -    struct connection *c; +    connection *c;      pa_protocol_esound *p = userdata;      char cname[256], pname[128]; -    assert(s && io && p); + +    pa_assert(s); +    pa_assert(io); +    pa_assert(p);      if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {          pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -1167,23 +1339,23 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)          return;      } -    c = pa_xnew(struct connection, 1); +    c = pa_msgobject_new(connection); +    c->parent.parent.free = connection_free; +    c->parent.process_msg = connection_process_msg;      c->protocol = p;      c->io = io;      pa_iochannel_set_callback(c->io, io_callback, c);      pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); -    snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); -    assert(p->core); +    pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);      c->client = pa_client_new(p->core, __FILE__, cname); -    assert(c->client);      c->client->owner = p->module;      c->client->kill = client_kill_cb;      c->client->userdata = c;      c->authorized = !!p->public; -    c->swap_byte_order = 0; -    c->dead = 0; +    c->swap_byte_order = FALSE; +    c->dead = FALSE;      c->read_data_length = 0;      c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); @@ -1203,6 +1375,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)      c->playback.current_memblock = NULL;      c->playback.memblock_index = 0;      c->playback.fragment_size = 0; +    pa_atomic_store(&c->playback.missing, 0);      c->scache.memchunk.length = c->scache.memchunk.index = 0;      c->scache.memchunk.memblock = NULL; @@ -1212,7 +1385,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)      if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {          pa_log_info("Client authenticated by IP ACL."); -        c->authorized = 1; +        c->authorized = TRUE;      }      if (!c->authorized) { @@ -1224,7 +1397,6 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)          c->auth_timeout_event = NULL;      c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); -    assert(c->defer_event);      p->core->mainloop->defer_enable(c->defer_event, 0);      pa_idxset_put(p->connections, c, &c->index); @@ -1233,22 +1405,22 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)  /*** entry points ***/  pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { -    pa_protocol_esound *p; +    pa_protocol_esound *p = NULL;      int public = 0;      const char *acl; -    assert(core); -    assert(server); -    assert(m); -    assert(ma); - -    p = pa_xnew(pa_protocol_esound, 1); +    pa_assert(core); +    pa_assert(server); +    pa_assert(m); +    pa_assert(ma);      if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {          pa_log("auth-anonymous= expects a boolean argument.");          goto fail;      } +    p = pa_xnew(pa_protocol_esound, 1); +      if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)          goto fail; @@ -1261,13 +1433,12 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve      } else          p->auth_ip_acl = NULL; +    p->core = core;      p->module = m;      p->public = public;      p->server = server;      pa_socket_server_set_callback(p->server, on_connection, p); -    p->core = core;      p->connections = pa_idxset_new(NULL, NULL); -    assert(p->connections);      p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));      p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); @@ -1281,17 +1452,20 @@ fail:  }  void pa_protocol_esound_free(pa_protocol_esound *p) { -    struct connection *c; -    assert(p); +    connection *c; +    pa_assert(p);      while ((c = pa_idxset_first(p->connections, NULL))) -        connection_free(c); - +        connection_unlink(c);      pa_idxset_free(p->connections, NULL, NULL); +      pa_socket_server_unref(p->server);      if (p->auth_ip_acl)          pa_ip_acl_free(p->auth_ip_acl); +    pa_xfree(p->sink_name); +    pa_xfree(p->source_name); +      pa_xfree(p);  } diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index 3541721a..d91ae142 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <stdio.h>  #include <string.h> @@ -34,6 +33,7 @@  #include <pulse/xmalloc.h>  #include <pulsecore/ioline.h> +#include <pulsecore/macro.h>  #include <pulsecore/log.h>  #include <pulsecore/namereg.h>  #include <pulsecore/cli-text.h> @@ -65,11 +65,12 @@ struct pa_protocol_http {  static void http_response(struct connection *c, int code, const char *msg, const char *mime) {      char s[256]; -    assert(c); -    assert(msg); -    assert(mime); -    snprintf(s, sizeof(s), +    pa_assert(c); +    pa_assert(msg); +    pa_assert(mime); + +    pa_snprintf(s, sizeof(s),               "HTTP/1.0 %i %s\n"               "Connection: close\n"               "Content-Type: %s\n" @@ -83,14 +84,14 @@ static void http_response(struct connection *c, int code, const char *msg, const  static void http_message(struct connection *c, int code, const char *msg, const char *text) {      char s[256]; -    assert(c); +    pa_assert(c);      http_response(c, code, msg, "text/html");      if (!text)          text = msg; -    snprintf(s, sizeof(s), +    pa_snprintf(s, sizeof(s),               "<?xml version=\"1.0\"?>\n"               "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"               "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n" @@ -103,21 +104,22 @@ static void http_message(struct connection *c, int code, const char *msg, const  static void connection_free(struct connection *c, int del) { -    assert(c); +    pa_assert(c);      if (c->url)          pa_xfree(c->url);      if (del)          pa_idxset_remove_by_data(c->protocol->connections, c, NULL); +      pa_ioline_unref(c->line);      pa_xfree(c);  }  static void line_callback(pa_ioline *line, const char *s, void *userdata) {      struct connection *c = userdata; -    assert(line); -    assert(c); +    pa_assert(line); +    pa_assert(c);      if (!s) {          /* EOF */ @@ -223,7 +225,10 @@ fail:  static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {      pa_protocol_http *p = userdata;      struct connection *c; -    assert(s && io && p); + +    pa_assert(s); +    pa_assert(io); +    pa_assert(p);      if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {          pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -231,7 +236,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)          return;      } -    c = pa_xmalloc(sizeof(struct connection)); +    c = pa_xnew(struct connection, 1);      c->protocol = p;      c->line = pa_ioline_new(io);      c->state = REQUEST_LINE; @@ -243,9 +248,11 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)  pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {      pa_protocol_http* p; -    assert(core && server); -    p = pa_xmalloc(sizeof(pa_protocol_http)); +    pa_core_assert_ref(core); +    pa_assert(server); + +    p = pa_xnew(pa_protocol_http, 1);      p->module = m;      p->core = core;      p->server = server; @@ -257,12 +264,12 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,  }  static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { -    assert(p); +    pa_assert(p);      connection_free(p, 0);  }  void pa_protocol_http_free(pa_protocol_http *p) { -    assert(p); +    pa_assert(p);      pa_idxset_free(p->connections, free_connection, NULL);      pa_socket_server_unref(p->server); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index dd41b3d5..9ae0f083 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -28,7 +28,6 @@  #include <string.h>  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <unistd.h> @@ -61,6 +60,7 @@  #include <pulsecore/creds.h>  #include <pulsecore/core-util.h>  #include <pulsecore/ipacl.h> +#include <pulsecore/thread-mq.h>  #include "protocol-native.h" @@ -72,54 +72,61 @@  #define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */ -struct connection; +typedef struct connection connection;  struct pa_protocol_native; -struct record_stream { -    struct connection *connection; +typedef struct record_stream { +    pa_msgobject parent; + +    connection *connection;      uint32_t index; +      pa_source_output *source_output;      pa_memblockq *memblockq;      size_t fragment_size; -}; +} record_stream; + +typedef struct output_stream { +    pa_msgobject parent; +} output_stream; -struct playback_stream { -    int type; -    struct connection *connection; +typedef struct playback_stream { +    output_stream parent; + +    connection *connection;      uint32_t index; +      pa_sink_input *sink_input;      pa_memblockq *memblockq; -    size_t requested_bytes;      int drain_request;      uint32_t drain_tag;      uint32_t syncid;      int underrun; -    /* Sync group members */ -    PA_LLIST_FIELDS(struct playback_stream); -}; +    pa_atomic_t missing; +    size_t minreq; + +    /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */ +    int64_t read_index, write_index; +    size_t resampled_chunk_length; +} playback_stream; -struct upload_stream { -    int type; -    struct connection *connection; +typedef struct upload_stream { +    output_stream parent; + +    connection *connection;      uint32_t index; +      pa_memchunk memchunk;      size_t length;      char *name;      pa_sample_spec sample_spec;      pa_channel_map channel_map; -}; - -struct output_stream { -    int type; -}; - -enum { -    UPLOAD_STREAM, -    PLAYBACK_STREAM -}; +} upload_stream;  struct connection { +    pa_msgobject parent; +      int authorized;      uint32_t version;      pa_protocol_native *protocol; @@ -132,10 +139,30 @@ struct connection {      pa_time_event *auth_timeout_event;  }; +PA_DECLARE_CLASS(record_stream); +#define RECORD_STREAM(o) (record_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject); + +PA_DECLARE_CLASS(output_stream); +#define OUTPUT_STREAM(o) (output_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject); + +PA_DECLARE_CLASS(playback_stream); +#define PLAYBACK_STREAM(o) (playback_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream); + +PA_DECLARE_CLASS(upload_stream); +#define UPLOAD_STREAM(o) (upload_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream); + +PA_DECLARE_CLASS(connection); +#define CONNECTION(o) (connection_cast(o)) +static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject); +  struct pa_protocol_native {      pa_module *module; -    int public;      pa_core *core; +    int public;      pa_socket_server *server;      pa_idxset *connections;      uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; @@ -146,17 +173,45 @@ struct pa_protocol_native {      pa_ip_acl *auth_ip_acl;  }; -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); +enum { +    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */ +    SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */ +    SINK_INPUT_MESSAGE_FLUSH, +    SINK_INPUT_MESSAGE_TRIGGER, +    SINK_INPUT_MESSAGE_SEEK, +    SINK_INPUT_MESSAGE_PREBUF_FORCE, +    SINK_INPUT_MESSAGE_UPDATE_LATENCY +}; + +enum { +    PLAYBACK_STREAM_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */ +    PLAYBACK_STREAM_MESSAGE_UNDERFLOW, +    PLAYBACK_STREAM_MESSAGE_OVERFLOW, +    PLAYBACK_STREAM_MESSAGE_DRAIN_ACK +}; + +enum { +    RECORD_STREAM_MESSAGE_POST_DATA         /* data from source output to main loop */ +}; + +enum { +    CONNECTION_MESSAGE_RELEASE, +    CONNECTION_MESSAGE_REVOKE +}; + +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); +static void sink_input_drop_cb(pa_sink_input *i, size_t length);  static void sink_input_kill_cb(pa_sink_input *i); -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); +static void send_memblock(connection *c);  static void request_bytes(struct playback_stream*s);  static void source_output_kill_cb(pa_source_output *o);  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);  static pa_usec_t source_output_get_latency_cb(pa_source_output *o); +static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +  static void command_exit(pa_pdispatch *pd, 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);  static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); @@ -179,8 +234,7 @@ static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag,  static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  static void command_set_mute(pa_pdispatch *pd, 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); -static void command_flush_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_trigger_or_prebuf_playback_stream(pa_pdispatch *pd, 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);  static void command_set_default_sink_or_source(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);  static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); @@ -193,6 +247,7 @@ static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, u  static void command_cork_record_stream(pa_pdispatch *pd, 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);  static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_suspend(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] = {      [PA_COMMAND_ERROR] = NULL, @@ -239,12 +294,16 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {      [PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,      [PA_COMMAND_SET_SINK_MUTE] = command_set_mute, +    [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,      [PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute, +    [PA_COMMAND_SUSPEND_SINK] = command_suspend, +    [PA_COMMAND_SUSPEND_SOURCE] = command_suspend, +      [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream, -    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_playback_stream, -    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream, -    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream, +    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream, +    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream, +    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,      [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,      [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream, @@ -269,74 +328,146 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {  /* structure management */ -static struct upload_stream* upload_stream_new( -    struct connection *c, -    const pa_sample_spec *ss, -    const pa_channel_map *map, -    const char *name, size_t length) { +static void upload_stream_unlink(upload_stream *s) { +    pa_assert(s); + +    if (!s->connection) +        return; + +    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s); +    s->connection = NULL; +    upload_stream_unref(s); +} + +static void upload_stream_free(pa_object *o) { +    upload_stream *s = UPLOAD_STREAM(o); +    pa_assert(s); + +    upload_stream_unlink(s); + +    pa_xfree(s->name); -    struct upload_stream *s; -    assert(c && ss && name && length); +    if (s->memchunk.memblock) +        pa_memblock_unref(s->memchunk.memblock); -    s = pa_xnew(struct upload_stream, 1); -    s->type = UPLOAD_STREAM; +    pa_xfree(s); +} + +static upload_stream* upload_stream_new( +        connection *c, +        const pa_sample_spec *ss, +        const pa_channel_map *map, +        const char *name, size_t length) { + +    upload_stream *s; + +    pa_assert(c); +    pa_assert(ss); +    pa_assert(name); +    pa_assert(length > 0); + +    s = pa_msgobject_new(upload_stream); +    s->parent.parent.parent.free = upload_stream_free;      s->connection = c;      s->sample_spec = *ss;      s->channel_map = *map;      s->name = pa_xstrdup(name); - -    s->memchunk.memblock = NULL; -    s->memchunk.index = 0; -    s->memchunk.length = 0; - +    pa_memchunk_reset(&s->memchunk);      s->length = length;      pa_idxset_put(c->output_streams, s, &s->index); +      return s;  } -static void upload_stream_free(struct upload_stream *o) { -    assert(o && o->connection); +static void record_stream_unlink(record_stream *s) { +    pa_assert(s); + +    if (!s->connection) +        return; + +    if (s->source_output) { +        pa_source_output_unlink(s->source_output); +        pa_source_output_unref(s->source_output); +        s->source_output = NULL; +    } + +    pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s); +    s->connection = NULL; +    record_stream_unref(s); +} + +static void record_stream_free(pa_object *o) { +    record_stream *s = RECORD_STREAM(o); +    pa_assert(s); + +    record_stream_unlink(s); + +    pa_memblockq_free(s->memblockq); +    pa_xfree(s); +} + +static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    record_stream *s = RECORD_STREAM(o); +    record_stream_assert_ref(s); -    pa_idxset_remove_by_data(o->connection->output_streams, o, NULL); +    if (!s->connection) +        return -1; -    pa_xfree(o->name); +    switch (code) { -    if (o->memchunk.memblock) -        pa_memblock_unref(o->memchunk.memblock); +        case RECORD_STREAM_MESSAGE_POST_DATA: -    pa_xfree(o); +            if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { +/*                 pa_log_warn("Failed to push data into output queue."); */ +                return -1; +            } + +            if (!pa_pstream_is_pending(s->connection->pstream)) +                send_memblock(s->connection); + +            break; +    } + +    return 0;  } -static struct record_stream* record_stream_new( -    struct connection *c, -    pa_source *source, -    const pa_sample_spec *ss, -    const pa_channel_map *map, -    const char *name, -    size_t maxlength, -    size_t fragment_size) { +static record_stream* record_stream_new( +        connection *c, +        pa_source *source, +        const pa_sample_spec *ss, +        const pa_channel_map *map, +        const char *name, +        uint32_t *maxlength, +        uint32_t fragment_size, +        int corked) { -    struct record_stream *s; +    record_stream *s;      pa_source_output *source_output;      size_t base;      pa_source_output_new_data data; -    assert(c && ss && name && maxlength); +    pa_assert(c); +    pa_assert(ss); +    pa_assert(name); +    pa_assert(maxlength); +    pa_assert(*maxlength > 0);      pa_source_output_new_data_init(&data); +    data.module = c->protocol->module; +    data.client = c->client;      data.source = source;      data.driver = __FILE__;      data.name = name;      pa_source_output_new_data_set_sample_spec(&data, ss);      pa_source_output_new_data_set_channel_map(&data, map); -    data.module = c->protocol->module; -    data.client = c->client; -    if (!(source_output = pa_source_output_new(c->protocol->core, &data, 0))) +    if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))          return NULL; -    s = pa_xnew(struct record_stream, 1); +    s = pa_msgobject_new(record_stream); +    s->parent.parent.free = record_stream_free; +    s->parent.process_msg = record_stream_process_msg;      s->connection = c;      s->source_output = source_output;      s->source_output->push = source_output_push_cb; @@ -346,58 +477,159 @@ static struct record_stream* record_stream_new(      s->memblockq = pa_memblockq_new(              0, -            maxlength, +            *maxlength,              0,              base = pa_frame_size(ss),              1,              0,              NULL); -    assert(s->memblockq);      s->fragment_size = (fragment_size/base)*base; -    if (!s->fragment_size) +    if (s->fragment_size <= 0)          s->fragment_size = base; +    *maxlength = pa_memblockq_get_maxlength(s->memblockq);      pa_idxset_put(c->record_streams, s, &s->index); + +    pa_source_output_put(s->source_output);      return s;  } -static void record_stream_free(struct record_stream* r) { -    assert(r && r->connection); +static void playback_stream_unlink(playback_stream *s) { +    pa_assert(s); + +    if (!s->connection) +        return; + +    if (s->sink_input) { +        pa_sink_input_unlink(s->sink_input); +        pa_sink_input_unref(s->sink_input); +        s->sink_input = NULL; +    } + +    if (s->drain_request) +        pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY); + +    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s); +    s->connection = NULL; +    playback_stream_unref(s); +} + +static void playback_stream_free(pa_object* o) { +    playback_stream *s = PLAYBACK_STREAM(o); +    pa_assert(s); + +    playback_stream_unlink(s); + +    pa_memblockq_free(s->memblockq); +    pa_xfree(s); +} + +static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    playback_stream *s = PLAYBACK_STREAM(o); +    playback_stream_assert_ref(s); + +    if (!s->connection) +        return -1; + +    switch (code) { +        case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: { +            pa_tagstruct *t; +            uint32_t l = 0; + +            for (;;) { +                int32_t k; + +                if ((k = pa_atomic_load(&s->missing)) <= 0) +                    break; + +                l += k; + +                if (l < s->minreq) +                    break; + +                if (pa_atomic_sub(&s->missing, k) <= k) +                    break; +            } + +            if (l < s->minreq) +                break; + +            t = pa_tagstruct_new(NULL, 0); +            pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); +            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ +            pa_tagstruct_putu32(t, s->index); +            pa_tagstruct_putu32(t, l); +            pa_pstream_send_tagstruct(s->connection->pstream, t); + +/*             pa_log("Requesting %u bytes", l);     */ +            break; +        } + +        case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: { +            pa_tagstruct *t; + +            /* Report that we're empty */ +            t = pa_tagstruct_new(NULL, 0); +            pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW); +            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ +            pa_tagstruct_putu32(t, s->index); +            pa_pstream_send_tagstruct(s->connection->pstream, t); +            break; +        } + +        case PLAYBACK_STREAM_MESSAGE_OVERFLOW: { +            pa_tagstruct *t; + +            /* Notify the user we're overflowed*/ +            t = pa_tagstruct_new(NULL, 0); +            pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW); +            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ +            pa_tagstruct_putu32(t, s->index); +            pa_pstream_send_tagstruct(s->connection->pstream, t); +            break; +        } -    pa_idxset_remove_by_data(r->connection->record_streams, r, NULL); -    pa_source_output_disconnect(r->source_output); -    pa_source_output_unref(r->source_output); -    pa_memblockq_free(r->memblockq); -    pa_xfree(r); +        case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK: +            pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata)); +            break; + +    } + +    return 0;  } -static struct playback_stream* playback_stream_new( -        struct connection *c, +static playback_stream* playback_stream_new( +        connection *c,          pa_sink *sink,          const pa_sample_spec *ss,          const pa_channel_map *map,          const char *name, -        size_t maxlength, -        size_t tlength, -        size_t prebuf, -        size_t minreq, +        uint32_t *maxlength, +        uint32_t *tlength, +        uint32_t *prebuf, +        uint32_t *minreq,          pa_cvolume *volume, -        uint32_t syncid) { +        uint32_t syncid, +        int corked, +        uint32_t *missing) { -    struct playback_stream *s, *ssync; +    playback_stream *s, *ssync;      pa_sink_input *sink_input;      pa_memblock *silence;      uint32_t idx;      int64_t start_index;      pa_sink_input_new_data data; -    assert(c && ss && name && maxlength); +    pa_assert(c); +    pa_assert(ss); +    pa_assert(name); +    pa_assert(maxlength);      /* Find syncid group */      for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) { -        if (ssync->type != PLAYBACK_STREAM) +        if (!playback_stream_isinstance(ssync))              continue;          if (ssync->syncid == syncid) @@ -405,8 +637,13 @@ static struct playback_stream* playback_stream_new(      }      /* Synced streams must connect to the same sink */ -    if (ssync) -        sink = ssync->sink_input->sink; +    if (ssync) { + +        if (!sink) +            sink = ssync->sink_input->sink; +        else if (sink != ssync->sink_input->sink) +            return NULL; +    }      pa_sink_input_new_data_init(&data);      data.sink = sink; @@ -417,146 +654,158 @@ static struct playback_stream* playback_stream_new(      pa_sink_input_new_data_set_volume(&data, volume);      data.module = c->protocol->module;      data.client = c->client; +    data.sync_base = ssync ? ssync->sink_input : NULL; -    if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, 0))) +    if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))          return NULL; -    s = pa_xnew(struct playback_stream, 1); -    s->type = PLAYBACK_STREAM; +    s = pa_msgobject_new(playback_stream); +    s->parent.parent.parent.free = playback_stream_free; +    s->parent.parent.process_msg = playback_stream_process_msg;      s->connection = c;      s->syncid = syncid;      s->sink_input = sink_input;      s->underrun = 1; +    s->sink_input->parent.process_msg = sink_input_process_msg;      s->sink_input->peek = sink_input_peek_cb;      s->sink_input->drop = sink_input_drop_cb;      s->sink_input->kill = sink_input_kill_cb; -    s->sink_input->get_latency = sink_input_get_latency_cb;      s->sink_input->userdata = s; -    if (ssync) { -        /* Sync id found, now find head of list */ -        PA_LLIST_FIND_HEAD(struct playback_stream, ssync, &ssync); - -        /* Prepend ourselves */ -        PA_LLIST_PREPEND(struct playback_stream, ssync, s); - -        /* Set our start index to the current read index of the other grozp member(s) */ -        assert(ssync->next); -        start_index = pa_memblockq_get_read_index(ssync->next->memblockq); -    } else { -        /* This ia a new sync group */ -        PA_LLIST_INIT(struct playback_stream, s); -        start_index = 0; -    } +    start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;      silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);      s->memblockq = pa_memblockq_new(              start_index, -            maxlength, -            tlength, +            *maxlength, +            *tlength,              pa_frame_size(ss), -            prebuf, -            minreq, +            *prebuf, +            *minreq,              silence);      pa_memblock_unref(silence); -    s->requested_bytes = 0; +    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); +    *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); +    *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); +    *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); +    *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq); + +    s->minreq = pa_memblockq_get_minreq(s->memblockq); +    pa_atomic_store(&s->missing, 0);      s->drain_request = 0;      pa_idxset_put(c->output_streams, s, &s->index); +    pa_sink_input_put(s->sink_input); +      return s;  } -static void playback_stream_free(struct playback_stream* p) { -    struct playback_stream *head; -    assert(p && p->connection); +static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    connection *c = CONNECTION(o); +    connection_assert_ref(c); -    if (p->drain_request) -        pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERR_NOENTITY); +    if (!c->protocol) +        return -1; -    PA_LLIST_FIND_HEAD(struct playback_stream, p, &head); -    PA_LLIST_REMOVE(struct playback_stream, head, p); +    switch (code) { -    pa_idxset_remove_by_data(p->connection->output_streams, p, NULL); -    pa_sink_input_disconnect(p->sink_input); -    pa_sink_input_unref(p->sink_input); -    pa_memblockq_free(p->memblockq); -    pa_xfree(p); +        case CONNECTION_MESSAGE_REVOKE: +            pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata)); +            break; + +        case CONNECTION_MESSAGE_RELEASE: +            pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata)); +            break; +    } + +    return 0;  } -static void connection_free(struct connection *c) { -    struct record_stream *r; -    struct output_stream *o; -    assert(c && c->protocol); +static void connection_unlink(connection *c) { +    record_stream *r; +    output_stream *o; + +    pa_assert(c); + +    if (!c->protocol) +        return; -    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);      while ((r = pa_idxset_first(c->record_streams, NULL))) -        record_stream_free(r); -    pa_idxset_free(c->record_streams, NULL, NULL); +        record_stream_unlink(r);      while ((o = pa_idxset_first(c->output_streams, NULL))) -        if (o->type == PLAYBACK_STREAM) -            playback_stream_free((struct playback_stream*) o); +        if (playback_stream_isinstance(o)) +            playback_stream_unlink(PLAYBACK_STREAM(o));          else -            upload_stream_free((struct upload_stream*) o); -    pa_idxset_free(c->output_streams, NULL, NULL); - -    pa_pdispatch_unref(c->pdispatch); -    pa_pstream_close(c->pstream); -    pa_pstream_unref(c->pstream); -    pa_client_free(c->client); +            upload_stream_unlink(UPLOAD_STREAM(o));      if (c->subscription)          pa_subscription_free(c->subscription); -    if (c->auth_timeout_event) +    if (c->pstream) +        pa_pstream_unlink(c->pstream); + +    if (c->auth_timeout_event) {          c->protocol->core->mainloop->time_free(c->auth_timeout_event); +        c->auth_timeout_event = NULL; +    } -    pa_xfree(c); +    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c); +    c->protocol = NULL; +    connection_unref(c);  } -static void request_bytes(struct playback_stream *s) { -    pa_tagstruct *t; -    size_t l; -    assert(s); +static void connection_free(pa_object *o) { +    connection *c = CONNECTION(o); -    if (!(l = pa_memblockq_missing(s->memblockq))) -        return; +    pa_assert(c); -    if (l <= s->requested_bytes) -        return; +    connection_unlink(c); -    l -= s->requested_bytes; +    pa_idxset_free(c->record_streams, NULL, NULL); +    pa_idxset_free(c->output_streams, NULL, NULL); -    if (l < pa_memblockq_get_minreq(s->memblockq)) -        return; +    pa_pdispatch_unref(c->pdispatch); +    pa_pstream_unref(c->pstream); +    pa_client_free(c->client); -    s->requested_bytes += l; +    pa_xfree(c); +} -    t = pa_tagstruct_new(NULL, 0); -    assert(t); -    pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); -    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ -    pa_tagstruct_putu32(t, s->index); -    pa_tagstruct_putu32(t, l); -    pa_pstream_send_tagstruct(s->connection->pstream, t); +/* Called from thread context */ +static void request_bytes(playback_stream *s) { +    size_t m, previous_missing; -/*     pa_log("Requesting %u bytes", l);  */ +    playback_stream_assert_ref(s); + +    m = pa_memblockq_pop_missing(s->memblockq); + +    if (m <= 0) +        return; + +/*     pa_log("request_bytes(%u)", m); */ + +    previous_missing = pa_atomic_add(&s->missing, m); +    if (previous_missing < s->minreq && previous_missing+m >= s->minreq) { +        pa_assert(pa_thread_mq_get()); +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); +    }  } -static void send_memblock(struct connection *c) { +static void send_memblock(connection *c) {      uint32_t start; -    struct record_stream *r; +    record_stream *r;      start = PA_IDXSET_INVALID;      for (;;) {          pa_memchunk chunk; -        if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index))) +        if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))              return;          if (start == PA_IDXSET_INVALID) @@ -571,7 +820,8 @@ static void send_memblock(struct connection *c) {                  schunk.length = r->fragment_size;              pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk); -            pa_memblockq_drop(r->memblockq, &chunk, schunk.length); + +            pa_memblockq_drop(r->memblockq, schunk.length);              pa_memblock_unref(schunk.memblock);              return; @@ -579,9 +829,9 @@ static void send_memblock(struct connection *c) {      }  } -static void send_playback_stream_killed(struct playback_stream *p) { +static void send_playback_stream_killed(playback_stream *p) {      pa_tagstruct *t; -    assert(p); +    playback_stream_assert_ref(p);      t = pa_tagstruct_new(NULL, 0);      pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED); @@ -590,9 +840,9 @@ static void send_playback_stream_killed(struct playback_stream *p) {      pa_pstream_send_tagstruct(p->connection->pstream, t);  } -static void send_record_stream_killed(struct record_stream *r) { +static void send_record_stream_killed(record_stream *r) {      pa_tagstruct *t; -    assert(r); +    record_stream_assert_ref(r);      t = pa_tagstruct_new(NULL, 0);      pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED); @@ -601,96 +851,219 @@ static void send_record_stream_killed(struct record_stream *r) {      pa_pstream_send_tagstruct(r->connection->pstream, t);  } -/*** sinkinput callbacks ***/ +/*** sink input callbacks ***/ -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { -    struct playback_stream *s; -    assert(i && i->userdata && chunk); -    s = i->userdata; +/* Called from thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_sink_input *i = PA_SINK_INPUT(o); +    playback_stream *s; -    if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) { -        pa_tagstruct *t; +    pa_sink_input_assert_ref(i); +    s = PLAYBACK_STREAM(i->userdata); +    playback_stream_assert_ref(s); + +    switch (code) { + +        case SINK_INPUT_MESSAGE_SEEK: +            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata)); +            request_bytes(s); +            return 0; + +        case SINK_INPUT_MESSAGE_POST_DATA: { +            pa_assert(chunk); + +/*             pa_log("sink input post: %u", chunk->length); */ + +            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); +            } -        /* Report that we're empty */ +            request_bytes(s); -        t = pa_tagstruct_new(NULL, 0); -        pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW); -        pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ -        pa_tagstruct_putu32(t, s->index); -        pa_pstream_send_tagstruct(s->connection->pstream, t); +            s->underrun = 0; +            return 0; +        } + +        case SINK_INPUT_MESSAGE_DRAIN: { + +            pa_memblockq_prebuf_disable(s->memblockq); +            if (!pa_memblockq_is_readable(s->memblockq)) +                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL); +            else { +                s->drain_tag = PA_PTR_TO_UINT(userdata); +                s->drain_request = 1; +            } +            request_bytes(s); + +            return 0; +        } + +        case SINK_INPUT_MESSAGE_FLUSH: +        case SINK_INPUT_MESSAGE_PREBUF_FORCE: +        case SINK_INPUT_MESSAGE_TRIGGER: { + +            pa_sink_input *isync; +            void (*func)(pa_memblockq *bq); + +            switch  (code) { +                case SINK_INPUT_MESSAGE_FLUSH: +                    func = pa_memblockq_flush; +                    break; + +                case SINK_INPUT_MESSAGE_PREBUF_FORCE: +                    func = pa_memblockq_prebuf_force; +                    break; + +                case SINK_INPUT_MESSAGE_TRIGGER: +                    func = pa_memblockq_prebuf_disable; +                    break; + +                default: +                    pa_assert_not_reached(); +            } + +            func(s->memblockq); +            s->underrun = 0; +            request_bytes(s); + +            /* Do the same for all other members in the sync group */ +            for (isync = i->sync_prev; isync; isync = isync->sync_prev) { +                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); +                func(ssync->memblockq); +                ssync->underrun = 0; +                request_bytes(ssync); +            } + +            for (isync = i->sync_next; isync; isync = isync->sync_next) { +                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); +                func(ssync->memblockq); +                ssync->underrun = 0; +                request_bytes(ssync); +            } + +            return 0; +        } + +        case SINK_INPUT_MESSAGE_UPDATE_LATENCY: + +            s->read_index = pa_memblockq_get_read_index(s->memblockq); +            s->write_index = pa_memblockq_get_write_index(s->memblockq); +            s->resampled_chunk_length = s->sink_input->thread_info.resampled_chunk.memblock ? s->sink_input->thread_info.resampled_chunk.length : 0; +            return 0; + +        case PA_SINK_INPUT_MESSAGE_SET_STATE: + +            pa_memblockq_prebuf_force(s->memblockq); +            request_bytes(s); +            break; + +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { +            pa_usec_t *r = userdata; + +            *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec); + +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +            break; +        } +    } + +    return pa_sink_input_process_msg(o, code, userdata, offset, chunk); +} + +/* Called from thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    playback_stream *s; + +    pa_sink_input_assert_ref(i); +    s = PLAYBACK_STREAM(i->userdata); +    playback_stream_assert_ref(s); +    pa_assert(chunk); + +    if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) { +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);          s->underrun = 1;      }      if (pa_memblockq_peek(s->memblockq, chunk) < 0) { -/*         pa_log("peek: failure");    */ +/*         pa_log("peek: failure");     */          return -1;      } -/*     pa_log("peek: %u", chunk->length);    */ +/*     pa_log("peek: %u", chunk->length); */ + +    request_bytes(s);      return 0;  } -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { -    struct playback_stream *s; -    assert(i && i->userdata && length); -    s = i->userdata; +/* Called from thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    playback_stream *s; -    pa_memblockq_drop(s->memblockq, chunk, length); +    pa_sink_input_assert_ref(i); +    s = PLAYBACK_STREAM(i->userdata); +    playback_stream_assert_ref(s); +    pa_assert(length > 0); -    request_bytes(s); +    pa_memblockq_drop(s->memblockq, length);      if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { -        pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);          s->drain_request = 0; +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);      } -/*     pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq));   */ -} +    request_bytes(s); -static void sink_input_kill_cb(pa_sink_input *i) { -    assert(i && i->userdata); -    send_playback_stream_killed((struct playback_stream *) i->userdata); -    playback_stream_free((struct playback_stream *) i->userdata); +/*     pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */  } -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { -    struct playback_stream *s; -    assert(i && i->userdata); -    s = i->userdata; +static void sink_input_kill_cb(pa_sink_input *i) { +    playback_stream *s; -    /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/ +    pa_sink_input_assert_ref(i); +    s = PLAYBACK_STREAM(i->userdata); +    playback_stream_assert_ref(s); -    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); +    send_playback_stream_killed(s); +    playback_stream_unlink(s);  }  /*** source_output callbacks ***/ +/* Called from thread context */  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { -    struct record_stream *s; -    assert(o && o->userdata && chunk); -    s = o->userdata; +    record_stream *s; -    if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { -        pa_log_warn("Failed to push data into output queue."); -        return; -    } +    pa_source_output_assert_ref(o); +    s = RECORD_STREAM(o->userdata); +    record_stream_assert_ref(s); +    pa_assert(chunk); -    if (!pa_pstream_is_pending(s->connection->pstream)) -        send_memblock(s->connection); +    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);  }  static void source_output_kill_cb(pa_source_output *o) { -    assert(o && o->userdata); -    send_record_stream_killed((struct record_stream *) o->userdata); -    record_stream_free((struct record_stream *) o->userdata); +    record_stream *s; + +    pa_source_output_assert_ref(o); +    s = RECORD_STREAM(o->userdata); +    record_stream_assert_ref(s); + +    send_record_stream_killed(s); +    record_stream_unlink(s);  }  static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { -    struct record_stream *s; -    assert(o && o->userdata); -    s = o->userdata; +    record_stream *s; + +    pa_source_output_assert_ref(o); +    s = RECORD_STREAM(o->userdata); +    record_stream_assert_ref(s);      /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/ @@ -699,9 +1072,9 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {  /*** pdispatch callbacks ***/ -static void protocol_error(struct connection *c) { +static void protocol_error(connection *c) {      pa_log("protocol error, kicking client"); -    connection_free(c); +    connection_unlink(c);  }  #define CHECK_VALIDITY(pstream, expression, tag, error) do { \ @@ -721,9 +1094,9 @@ static pa_tagstruct *reply_new(uint32_t tag) {  }  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) { -    struct connection *c = userdata; -    struct playback_stream *s; -    uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid; +    connection *c = CONNECTION(userdata); +    playback_stream *s; +    uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;      const char *name, *sink_name;      pa_sample_spec ss;      pa_channel_map map; @@ -732,7 +1105,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC      pa_cvolume volume;      int corked; -    assert(c && t && c->protocol && c->protocol->core); +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_get(              t, @@ -773,34 +1147,35 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC          CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);      } -    s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume, syncid); +    s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); -    pa_sink_input_cork(s->sink_input, corked); -      reply = reply_new(tag);      pa_tagstruct_putu32(reply, s->index); -    assert(s->sink_input); +    pa_assert(s->sink_input);      pa_tagstruct_putu32(reply, s->sink_input->index); -    pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq)); +    pa_tagstruct_putu32(reply, missing); + +/*     pa_log("initial request is %u", missing); */      if (c->version >= 9) {          /* Since 0.9 we support sending the buffer metrics back to the client */ -        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq)); -        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq)); -        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq)); -        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq)); +        pa_tagstruct_putu32(reply, (uint32_t) maxlength); +        pa_tagstruct_putu32(reply, (uint32_t) tlength); +        pa_tagstruct_putu32(reply, (uint32_t) prebuf); +        pa_tagstruct_putu32(reply, (uint32_t) minreq);      }      pa_pstream_send_tagstruct(c->pstream, reply); -    request_bytes(s);  }  static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t channel; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &channel) < 0 ||          !pa_tagstruct_eof(t)) { @@ -810,39 +1185,52 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) { -        struct playback_stream *s; -        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) { -            pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); -            return; +    switch (command) { + +        case PA_COMMAND_DELETE_PLAYBACK_STREAM: { +            playback_stream *s; +            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); +                return; +            } + +            playback_stream_unlink(s); +            break;          } -        playback_stream_free(s); -    } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) { -        struct record_stream *s; -        if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { -            pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); -            return; +        case PA_COMMAND_DELETE_RECORD_STREAM: { +            record_stream *s; +            if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); +                return; +            } + +            record_stream_unlink(s); +            break;          } -        record_stream_free(s); -    } else { -        struct upload_stream *s; -        assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM); -        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { -            pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); -            return; +        case PA_COMMAND_DELETE_UPLOAD_STREAM: { +            upload_stream *s; + +            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); +                return; +            } + +            upload_stream_unlink(s); +            break;          } -        upload_stream_free(s); +        default: +            pa_assert_not_reached();      }      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) { -    struct connection *c = userdata; -    struct record_stream *s; +    connection *c = CONNECTION(userdata); +    record_stream *s;      uint32_t maxlength, fragment_size;      uint32_t source_index;      const char *name, *source_name; @@ -851,7 +1239,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      pa_tagstruct *reply;      pa_source *source = NULL;      int corked; -    assert(c && t && c->protocol && c->protocol->core); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          pa_tagstruct_get_sample_spec(t, &ss) < 0 || @@ -882,20 +1272,18 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_          CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);      } -    s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size); +    s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); -    pa_source_output_cork(s->source_output, corked); -      reply = reply_new(tag);      pa_tagstruct_putu32(reply, s->index); -    assert(s->source_output); +    pa_assert(s->source_output);      pa_tagstruct_putu32(reply, s->source_output->index);      if (c->version >= 9) {          /* Since 0.9 we support sending the buffer metrics back to the client */ -        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq)); +        pa_tagstruct_putu32(reply, (uint32_t) maxlength);          pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);      } @@ -903,8 +1291,10 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_  }  static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; -    assert(c && t); +    connection *c = CONNECTION(userdata); + +    connection_assert_ref(c); +    pa_assert(t);      if (!pa_tagstruct_eof(t)) {          protocol_error(c); @@ -913,16 +1303,17 @@ static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop);      c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);      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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const void*cookie;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &c->version) < 0 ||          pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || @@ -1015,9 +1406,11 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *name; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1032,10 +1425,12 @@ static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE  }  static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *name;      uint32_t idx = PA_IDXSET_INVALID; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1052,7 +1447,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin              idx = sink->index;      } else {          pa_source *source; -        assert(command == PA_COMMAND_LOOKUP_SOURCE); +        pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);          if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))              idx = source->index;      } @@ -1068,10 +1463,12 @@ 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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx; -    struct playback_stream *s; -    assert(c && t); +    playback_stream *s; + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1082,29 +1479,18 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);      s = pa_idxset_get_by_index(c->output_streams, idx);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY); -    s->drain_request = 0; - -    pa_memblockq_prebuf_disable(s->memblockq); - -    if (!pa_memblockq_is_readable(s->memblockq)) { -/*         pa_log("immediate drain: %u", pa_memblockq_get_length(s->memblockq));  */ -        pa_pstream_send_simple_ack(c->pstream, tag); -    } else { -/*         pa_log("slow drain triggered");  */ -        s->drain_request = 1; -        s->drain_tag = tag; - -        pa_sink_notify(s->sink_input->sink); -    } +    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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_tagstruct *reply;      const pa_mempool_stat *stat; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (!pa_tagstruct_eof(t)) {          protocol_error(c); @@ -1125,13 +1511,15 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_tagstruct *reply; -    struct playback_stream *s; +    playback_stream *s;      struct timeval tv, now;      uint32_t idx;      pa_usec_t latency; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_get_timeval(t, &tv) < 0 || @@ -1143,31 +1531,34 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);      s = pa_idxset_get_by_index(c->output_streams, idx);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)      reply = reply_new(tag);      latency = pa_sink_get_latency(s->sink_input->sink); -    if (s->sink_input->resampled_chunk.memblock) -        latency += pa_bytes_to_usec(s->sink_input->resampled_chunk.length, &s->sink_input->sample_spec); +    latency += pa_bytes_to_usec(s->resampled_chunk_length, &s->sink_input->sample_spec); +      pa_tagstruct_put_usec(reply, latency);      pa_tagstruct_put_usec(reply, 0); -    pa_tagstruct_put_boolean(reply, s->sink_input->state == PA_SINK_INPUT_RUNNING); +    pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);      pa_tagstruct_put_timeval(reply, &tv);      pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); -    pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq)); -    pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq)); +    pa_tagstruct_puts64(reply, s->write_index); +    pa_tagstruct_puts64(reply, s->read_index);      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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_tagstruct *reply; -    struct record_stream *s; +    record_stream *s;      struct timeval tv, now;      uint32_t idx; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_get_timeval(t, &tv) < 0 || @@ -1192,14 +1583,16 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN  }  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) { -    struct connection *c = userdata; -    struct upload_stream *s; +    connection *c = CONNECTION(userdata); +    upload_stream *s;      uint32_t length;      const char *name;      pa_sample_spec ss;      pa_channel_map map;      pa_tagstruct *reply; -    assert(c && t && c->protocol && c->protocol->core); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          pa_tagstruct_get_sample_spec(t, &ss) < 0 || @@ -1228,11 +1621,13 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t channel; -    struct upload_stream *s; +    upload_stream *s;      uint32_t idx; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &channel) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1244,23 +1639,25 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      s = pa_idxset_get_by_index(c->output_streams, channel);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == UPLOAD_STREAM, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);      if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx) < 0)          pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);      else          pa_pstream_send_simple_ack(c->pstream, tag); -    upload_stream_free(s); +    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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t sink_index;      pa_volume_t volume;      pa_sink *sink;      const char *name, *sink_name; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &sink_index) < 0 ||          pa_tagstruct_gets(t, &sink_name) < 0 || @@ -1291,9 +1688,11 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *name; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1313,7 +1712,9 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED  }  static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) { -    assert(t && sink); +    pa_assert(t); +    pa_sink_assert_ref(sink); +      pa_tagstruct_put(          t,          PA_TAG_U32, sink->index, @@ -1321,22 +1722,21 @@ static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {          PA_TAG_STRING, sink->description,          PA_TAG_SAMPLE_SPEC, &sink->sample_spec,          PA_TAG_CHANNEL_MAP, &sink->channel_map, -        PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX, -        PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE), -        PA_TAG_BOOLEAN, pa_sink_get_mute(sink, PA_MIXER_HARDWARE), +        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_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),          PA_TAG_STRING, sink->driver, -        PA_TAG_U32, -        (sink->get_hw_volume ? PA_SINK_HW_VOLUME_CTRL : 0) | -        (sink->get_latency ? PA_SINK_LATENCY : 0) | -        (sink->is_hardware ? PA_SINK_HARDWARE : 0), +        PA_TAG_U32, sink->flags,          PA_TAG_INVALID);  }  static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) { -    assert(t && source); +    pa_assert(t); +    pa_source_assert_ref(source); +      pa_tagstruct_put(          t,          PA_TAG_U32, source->index, @@ -1344,22 +1744,21 @@ static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {          PA_TAG_STRING, source->description,          PA_TAG_SAMPLE_SPEC, &source->sample_spec,          PA_TAG_CHANNEL_MAP, &source->channel_map, -        PA_TAG_U32, source->owner ? source->owner->index : PA_INVALID_INDEX, -        PA_TAG_CVOLUME, pa_source_get_volume(source, PA_MIXER_HARDWARE), -        PA_TAG_BOOLEAN, pa_source_get_mute(source, PA_MIXER_HARDWARE), +        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_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),          PA_TAG_STRING, source->driver, -        PA_TAG_U32, -        (source->get_hw_volume ? PA_SOURCE_HW_VOLUME_CTRL : 0) | -        (source->get_latency ? PA_SOURCE_LATENCY : 0) | -        (source->is_hardware ? PA_SOURCE_HARDWARE : 0), +        PA_TAG_U32, source->flags,          PA_TAG_INVALID);  }  static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) { -    assert(t && client); +    pa_assert(t); +    pa_assert(client); +      pa_tagstruct_putu32(t, client->index);      pa_tagstruct_puts(t, client->name);      pa_tagstruct_putu32(t, client->owner ? client->owner->index : PA_INVALID_INDEX); @@ -1367,7 +1766,9 @@ static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {  }  static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { -    assert(t && module); +    pa_assert(t); +    pa_assert(module); +      pa_tagstruct_putu32(t, module->index);      pa_tagstruct_puts(t, module->name);      pa_tagstruct_puts(t, module->argument); @@ -1375,8 +1776,10 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {      pa_tagstruct_put_boolean(t, module->auto_unload);  } -static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) { -    assert(t && s); +static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) { +    pa_assert(t); +    pa_sink_input_assert_ref(s); +      pa_tagstruct_putu32(t, s->index);      pa_tagstruct_puts(t, s->name);      pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); @@ -1389,10 +1792,14 @@ static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) {      pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));      pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));      pa_tagstruct_puts(t, s->driver); +    if (c->version >= 11) +        pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));  }  static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) { -    assert(t && s); +    pa_assert(t); +    pa_source_output_assert_ref(s); +      pa_tagstruct_putu32(t, s->index);      pa_tagstruct_puts(t, s->name);      pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); @@ -1407,7 +1814,9 @@ static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {  }  static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) { -    assert(t && e); +    pa_assert(t); +    pa_assert(e); +      pa_tagstruct_putu32(t, e->index);      pa_tagstruct_puts(t, e->name);      pa_tagstruct_put_cvolume(t, &e->volume); @@ -1420,7 +1829,7 @@ static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {  }  static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      pa_sink *sink = NULL;      pa_source *source = NULL; @@ -1431,7 +1840,9 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      pa_scache_entry *sce = NULL;      const char *name;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          (command != PA_COMMAND_GET_CLIENT_INFO && @@ -1466,7 +1877,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)          so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);      else { -        assert(command == PA_COMMAND_GET_SAMPLE_INFO); +        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);          if (idx != PA_INVALID_INDEX)              sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);          else @@ -1488,7 +1899,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      else if (module)          module_fill_tagstruct(reply, module);      else if (si) -        sink_input_fill_tagstruct(reply, si); +        sink_input_fill_tagstruct(c, reply, si);      else if (so)          source_output_fill_tagstruct(reply, so);      else @@ -1497,12 +1908,14 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u  }  static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_idxset *i;      uint32_t idx;      void *p;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (!pa_tagstruct_eof(t)) {          protocol_error(c); @@ -1526,7 +1939,7 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma      else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)          i = c->protocol->core->source_outputs;      else { -        assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); +        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);          i = c->protocol->core->scache;      } @@ -1541,11 +1954,11 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma              else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)                  module_fill_tagstruct(reply, p);              else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) -                sink_input_fill_tagstruct(reply, p); +                sink_input_fill_tagstruct(c, reply, p);              else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)                  source_output_fill_tagstruct(reply, p);              else { -                assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); +                pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);                  scache_fill_tagstruct(reply, p);              }          } @@ -1555,11 +1968,13 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_tagstruct *reply;      char txt[256];      const char *n; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (!pa_tagstruct_eof(t)) {          protocol_error(c); @@ -1587,8 +2002,9 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE  static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {      pa_tagstruct *t; -    struct connection *c = userdata; -    assert(c && core); +    connection *c = CONNECTION(userdata); + +    connection_assert_ref(c);      t = pa_tagstruct_new(NULL, 0);      pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT); @@ -1599,9 +2015,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3  }  static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_subscription_mask_t m; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &m) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1617,7 +2035,7 @@ static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint      if (m != 0) {          c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c); -        assert(c->subscription); +        pa_assert(c->subscription);      } else          c->subscription = NULL; @@ -1631,14 +2049,16 @@ static void command_set_volume(          pa_tagstruct *t,          void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      pa_cvolume volume;      pa_sink *sink = NULL;      pa_source *source = NULL;      pa_sink_input *si = NULL;      const char *name = NULL; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) || @@ -1653,27 +2073,36 @@ static void command_set_volume(      CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); -    if (command == PA_COMMAND_SET_SINK_VOLUME) { -        if (idx != PA_INVALID_INDEX) -            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); -        else -            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); -    } else if (command == PA_COMMAND_SET_SOURCE_VOLUME) { -        if (idx != (uint32_t) -1) -            source = pa_idxset_get_by_index(c->protocol->core->sources, idx); -        else -            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); -    }  else { -        assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME); -        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); +    switch (command) { + +        case PA_COMMAND_SET_SINK_VOLUME: +            if (idx != PA_INVALID_INDEX) +                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); +            else +                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); +            break; + +        case PA_COMMAND_SET_SOURCE_VOLUME: +            if (idx != PA_INVALID_INDEX) +                source = pa_idxset_get_by_index(c->protocol->core->sources, idx); +            else +                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); +            break; + +        case PA_COMMAND_SET_SINK_INPUT_VOLUME: +            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); +            break; + +        default: +            pa_assert_not_reached();      }      CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);      if (sink) -        pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume); +        pa_sink_set_volume(sink, &volume);      else if (source) -        pa_source_set_volume(source, PA_MIXER_HARDWARE, &volume); +        pa_source_set_volume(source, &volume);      else if (si)          pa_sink_input_set_volume(si, &volume); @@ -1687,16 +2116,20 @@ static void command_set_mute(          pa_tagstruct *t,          void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      int mute;      pa_sink *sink = NULL;      pa_source *source = NULL; +    pa_sink_input *si = NULL;      const char *name = NULL; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 || -        pa_tagstruct_gets(t, &name) < 0 || +        (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) || +        (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||          pa_tagstruct_get_boolean(t, &mute) ||          !pa_tagstruct_eof(t)) {          protocol_error(c); @@ -1706,35 +2139,53 @@ 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); -    if (command == PA_COMMAND_SET_SINK_MUTE) { -        if (idx != PA_INVALID_INDEX) -            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); -        else -            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); -    } else { -        assert(command == PA_COMMAND_SET_SOURCE_MUTE); -        if (idx != (uint32_t) -1) -            source = pa_idxset_get_by_index(c->protocol->core->sources, idx); -        else -            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); +    switch (command) { + +        case PA_COMMAND_SET_SINK_MUTE: + +            if (idx != PA_INVALID_INDEX) +                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); +            else +                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + +            break; + +        case PA_COMMAND_SET_SOURCE_MUTE: +            if (idx != PA_INVALID_INDEX) +                source = pa_idxset_get_by_index(c->protocol->core->sources, idx); +            else +                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + +            break; + +        case PA_COMMAND_SET_SINK_INPUT_MUTE: +            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); +            break; + +        default: +            pa_assert_not_reached();      } -    CHECK_VALIDITY(c->pstream, sink || source, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);      if (sink) -        pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute); +        pa_sink_set_mute(sink, mute);      else if (source) -        pa_source_set_mute(source, PA_MIXER_HARDWARE, mute); +        pa_source_set_mute(source, mute); +    else if (si) +        pa_sink_input_set_mute(si, 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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      int b; -    struct playback_stream *s, *ssync; -    assert(c && t); +    playback_stream *s; + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_get_boolean(t, &b) < 0 || @@ -1747,73 +2198,19 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);      s = pa_idxset_get_by_index(c->output_streams, idx);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);      pa_sink_input_cork(s->sink_input, b); -    pa_memblockq_prebuf_force(s->memblockq); - -    /* Do the same for all other members in the sync group */ -    for (ssync = s->prev; ssync; ssync = ssync->prev) { -        pa_sink_input_cork(ssync->sink_input, b); -        pa_memblockq_prebuf_force(ssync->memblockq); -    } - -    for (ssync = s->next; ssync; ssync = ssync->next) { -        pa_sink_input_cork(ssync->sink_input, b); -        pa_memblockq_prebuf_force(ssync->memblockq); -    } -      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +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) { +    connection *c = CONNECTION(userdata);      uint32_t idx; -    struct playback_stream *s, *ssync; -    assert(c && t); - -    if (pa_tagstruct_getu32(t, &idx) < 0 || -        !pa_tagstruct_eof(t)) { -        protocol_error(c); -        return; -    } - -    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID); -    s = pa_idxset_get_by_index(c->output_streams, idx); -    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); - -    pa_memblockq_flush(s->memblockq); -    s->underrun = 0; +    playback_stream *s; -    /* Do the same for all other members in the sync group */ -    for (ssync = s->prev; ssync; ssync = ssync->prev) { -        pa_memblockq_flush(ssync->memblockq); -        ssync->underrun = 0; -    } - -    for (ssync = s->next; ssync; ssync = ssync->next) { -        pa_memblockq_flush(ssync->memblockq); -        ssync->underrun = 0; -    } - -    pa_pstream_send_simple_ack(c->pstream, tag); -    pa_sink_notify(s->sink_input->sink); -    request_bytes(s); - -    for (ssync = s->prev; ssync; ssync = ssync->prev) -        request_bytes(ssync); - -    for (ssync = s->next; ssync; ssync = ssync->next) -        request_bytes(ssync); -} - -static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; -    uint32_t idx; -    struct playback_stream *s; -    assert(c && t); +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1825,32 +2222,36 @@ static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch      CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);      s = pa_idxset_get_by_index(c->output_streams, idx);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -    CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); +    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);      switch (command) { +        case PA_COMMAND_FLUSH_PLAYBACK_STREAM: +            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL); +            break; +          case PA_COMMAND_PREBUF_PLAYBACK_STREAM: -            pa_memblockq_prebuf_force(s->memblockq); +            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);              break;          case PA_COMMAND_TRIGGER_PLAYBACK_STREAM: -            pa_memblockq_prebuf_disable(s->memblockq); +            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);              break;          default: -            abort(); +            pa_assert_not_reached();      } -    pa_sink_notify(s->sink_input->sink);      pa_pstream_send_simple_ack(c->pstream, tag); -    request_bytes(s);  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx; -    struct record_stream *s; +    record_stream *s;      int b; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_get_boolean(t, &b) < 0 || @@ -1869,10 +2270,12 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx; -    struct record_stream *s; -    assert(c && t); +    record_stream *s; + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1889,9 +2292,11 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *s; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &s) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1907,10 +2312,12 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u  }  static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      const char *name; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_gets(t, &name) < 0 || @@ -1923,16 +2330,16 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com      CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);      if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) { -        struct playback_stream *s; +        playback_stream *s;          s = pa_idxset_get_by_index(c->output_streams, idx);          CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); -        CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY); +        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);          pa_sink_input_set_name(s->sink_input, name);      } else { -        struct record_stream *s; +        record_stream *s;          s = pa_idxset_get_by_index(c->record_streams, idx);          CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); @@ -1944,9 +2351,11 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com  }  static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          !pa_tagstruct_eof(t)) { @@ -1961,6 +2370,8 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3          client = pa_idxset_get_by_index(c->protocol->core->clients, idx);          CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY); + +        connection_ref(c);          pa_client_kill(client);      } else if (command == PA_COMMAND_KILL_SINK_INPUT) { @@ -1969,27 +2380,32 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3          s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);          CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); +        connection_ref(c);          pa_sink_input_kill(s);      } else {          pa_source_output *s; -        assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT); +        pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);          s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);          CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); +        connection_ref(c);          pa_source_output_kill(s);      }      pa_pstream_send_simple_ack(c->pstream, tag); +    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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_module *m;      const char *name, *argument;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          pa_tagstruct_gets(t, &argument) < 0 || @@ -2013,10 +2429,12 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx;      pa_module *m; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          !pa_tagstruct_eof(t)) { @@ -2033,12 +2451,14 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *name, *module, *argument;      uint32_t type;      uint32_t idx;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_gets(t, &name) < 0 ||          pa_tagstruct_getu32(t, &type) < 0 || @@ -2066,11 +2486,13 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const char *name = NULL;      uint32_t type, idx = PA_IDXSET_INVALID;      int r; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if ((pa_tagstruct_getu32(t, &idx) < 0 &&          (pa_tagstruct_gets(t, &name) < 0 || @@ -2095,7 +2517,7 @@ static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE  }  static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) { -    assert(t && e); +    pa_assert(t && e);      pa_tagstruct_putu32(t, e->index);      pa_tagstruct_puts(t, e->name); @@ -2105,12 +2527,14 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      const pa_autoload_entry *a = NULL;      uint32_t type, idx;      const char *name;      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if ((pa_tagstruct_getu32(t, &idx) < 0 &&          (pa_tagstruct_gets(t, &name) < 0 || @@ -2137,9 +2561,11 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU  }  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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      pa_tagstruct *reply; -    assert(c && t); + +    connection_assert_ref(c); +    pa_assert(t);      if (!pa_tagstruct_eof(t)) {          protocol_error(c); @@ -2162,12 +2588,12 @@ 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) { -    struct connection *c = userdata; +    connection *c = CONNECTION(userdata);      uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;      const char *name = NULL; -    assert(c); -    assert(t); +    connection_assert_ref(c); +    pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_getu32(t, &idx_device) < 0 || @@ -2202,6 +2628,8 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag          pa_source_output *so = NULL;          pa_source *source; +        pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT); +          so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);          if (idx_device != PA_INVALID_INDEX) @@ -2218,67 +2646,122 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag      }      pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +    connection *c = CONNECTION(userdata); +    uint32_t idx = PA_INVALID_INDEX; +    const char *name = NULL; +    int b; + +    connection_assert_ref(c); +    pa_assert(t); + +    if (pa_tagstruct_getu32(t, &idx) < 0 || +        pa_tagstruct_gets(t, &name) < 0 || +        pa_tagstruct_get_boolean(t, &b) < 0 || +        !pa_tagstruct_eof(t)) { +        protocol_error(c); +        return; +    } + +    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); +    if (command == PA_COMMAND_SUSPEND_SINK) { + +        if (idx == PA_INVALID_INDEX && name && !*name) { + +            if (pa_sink_suspend_all(c->protocol->core, b) < 0) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); +                return; +            } +        } else { +            pa_sink *sink = NULL; + +            if (idx != PA_INVALID_INDEX) +                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); +            else +                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + +            CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); + +            if (pa_sink_suspend(sink, b) < 0) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); +                return; +            } +        } +    } else { + +        pa_assert(command == PA_COMMAND_SUSPEND_SOURCE); + +        if (idx == PA_INVALID_INDEX && name && !*name) { + +            if (pa_source_suspend_all(c->protocol->core, b) < 0) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); +                return; +            } + +        } else { +            pa_source *source; + +            if (idx != PA_INVALID_INDEX) +                source = pa_idxset_get_by_index(c->protocol->core->sources, idx); +            else +                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + +            CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); + +            if (pa_source_suspend(source, b) < 0) { +                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); +                return; +            } +        } +    } + +    pa_pstream_send_simple_ack(c->pstream, tag);  }  /*** pstream callbacks ***/  static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { -    struct connection *c = userdata; -    assert(p && packet && packet->data && c); +    connection *c = CONNECTION(userdata); + +    pa_assert(p); +    pa_assert(packet); +    connection_assert_ref(c);      if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {          pa_log("invalid packet."); -        connection_free(c); +        connection_unlink(c);      }  }  static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) { -    struct connection *c = userdata; -    struct output_stream *stream; -    assert(p && chunk && userdata); +    connection *c = CONNECTION(userdata); +    output_stream *stream; + +    pa_assert(p); +    pa_assert(chunk); +    connection_assert_ref(c); -    if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) { +    if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {          pa_log("client sent block for invalid stream.");          /* Ignoring */          return;      } -    if (stream->type == PLAYBACK_STREAM) { -        struct playback_stream *ps = (struct playback_stream*) stream; -        if (chunk->length >= ps->requested_bytes) -            ps->requested_bytes = 0; -        else -            ps->requested_bytes -= chunk->length; - -        pa_memblockq_seek(ps->memblockq, offset, seek); - -        if (pa_memblockq_push_align(ps->memblockq, chunk) < 0) { -            pa_tagstruct *t; - -            pa_log_warn("failed to push data into queue"); - -            /* Pushing this block into the queue failed, so we simulate -             * it by skipping ahead */ - -            pa_memblockq_seek(ps->memblockq, chunk->length, PA_SEEK_RELATIVE); +    if (playback_stream_isinstance(stream)) { +        playback_stream *ps = PLAYBACK_STREAM(stream); -            /* Notify the user */ -            t = pa_tagstruct_new(NULL, 0); -            pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW); -            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ -            pa_tagstruct_putu32(t, ps->index); -            pa_pstream_send_tagstruct(p, t); -        } +        if (seek != PA_SEEK_RELATIVE || offset != 0) +            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL); -        ps->underrun = 0; - -        pa_sink_notify(ps->sink_input->sink); +        pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);      } else { -        struct upload_stream *u = (struct upload_stream*) stream; +        upload_stream *u = UPLOAD_STREAM(stream);          size_t l; -        assert(u->type == UPLOAD_STREAM);          if (!u->memchunk.memblock) {              if (u->length == chunk->length) { @@ -2291,15 +2774,24 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o              }          } -        assert(u->memchunk.memblock); +        pa_assert(u->memchunk.memblock);          l = u->length;          if (l > chunk->length)              l = chunk->length; +          if (l > 0) { -            memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, -                   (uint8_t*) chunk->memblock->data+chunk->index, l); +            void *src, *dst; +            dst = pa_memblock_acquire(u->memchunk.memblock); +            src = pa_memblock_acquire(chunk->memblock); + +            memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length, +                   (uint8_t*) src+chunk->index, l); + +            pa_memblock_release(u->memchunk.memblock); +            pa_memblock_release(chunk->memblock); +              u->memchunk.length += l;              u->length -= l;          } @@ -2307,43 +2799,72 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o  }  static void pstream_die_callback(pa_pstream *p, void *userdata) { -    struct connection *c = userdata; -    assert(p && c); -    connection_free(c); +    connection *c = CONNECTION(userdata); -/*    pa_log("connection died.");*/ -} +    pa_assert(p); +    connection_assert_ref(c); +    connection_unlink(c); +    pa_log_info("connection died."); +}  static void pstream_drain_callback(pa_pstream *p, void *userdata) { -    struct connection *c = userdata; -    assert(p && c); +    connection *c = CONNECTION(userdata); + +    pa_assert(p); +    connection_assert_ref(c);      send_memblock(c);  } +static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) { +    pa_thread_mq *q; + +    if (!(q = pa_thread_mq_get())) +        pa_pstream_send_revoke(p, block_id); +    else +        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL); +} + +static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) { +    pa_thread_mq *q; + +    if (!(q = pa_thread_mq_get())) +        pa_pstream_send_release(p, block_id); +    else +        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL); +} +  /*** client callbacks ***/  static void client_kill_cb(pa_client *c) { -    assert(c && c->userdata); -    connection_free(c->userdata); +    pa_assert(c); + +    connection_unlink(CONNECTION(c->userdata));  }  /*** socket server callbacks ***/  static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { -    struct connection *c = userdata; -    assert(m && tv && c && c->auth_timeout_event == e); +    connection *c = CONNECTION(userdata); + +    pa_assert(m); +    pa_assert(tv); +    connection_assert_ref(c); +    pa_assert(c->auth_timeout_event == e);      if (!c->authorized) -        connection_free(c); +        connection_unlink(c);  }  static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {      pa_protocol_native *p = userdata; -    struct connection *c; +    connection *c;      char cname[256], pname[128]; -    assert(io && p); + +    pa_assert(s); +    pa_assert(io); +    pa_assert(p);      if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {          pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -2351,7 +2872,9 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo          return;      } -    c = pa_xmalloc(sizeof(struct connection)); +    c = pa_msgobject_new(connection); +    c->parent.parent.free = connection_free; +    c->parent.process_msg = connection_process_msg;      c->authorized = !!p->public; @@ -2371,35 +2894,31 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo      c->version = 8;      c->protocol = p;      pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); -    snprintf(cname, sizeof(cname), "Native client (%s)", pname); -    assert(p->core); +    pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);      c->client = pa_client_new(p->core, __FILE__, cname); -    assert(c->client);      c->client->kill = client_kill_cb;      c->client->userdata = c;      c->client->owner = p->module;      c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); -    assert(c->pstream);      pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);      pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);      pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);      pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); +    pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c); +    pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);      c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); -    assert(c->pdispatch);      c->record_streams = pa_idxset_new(NULL, NULL);      c->output_streams = pa_idxset_new(NULL, NULL); -    assert(c->record_streams && c->output_streams);      c->rrobin_index = PA_IDXSET_INVALID;      c->subscription = NULL;      pa_idxset_put(p->connections, c, NULL); -  #ifdef HAVE_CREDS      if (pa_iochannel_creds_supported(io))          pa_iochannel_creds_enable(io); @@ -2410,7 +2929,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo  /*** module entry points ***/  static int load_key(pa_protocol_native*p, const char*fn) { -    assert(p); +    pa_assert(p);      p->auth_cookie_in_property = 0; @@ -2440,8 +2959,8 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo      int public = 0;      const char *acl; -    assert(c); -    assert(ma); +    pa_assert(c); +    pa_assert(ma);      if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {          pa_log("auth-anonymous= expects a boolean argument."); @@ -2482,7 +3001,6 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo          goto fail;      p->connections = pa_idxset_new(NULL, NULL); -    assert(p->connections);      return p; @@ -2517,11 +3035,11 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv  }  void pa_protocol_native_free(pa_protocol_native *p) { -    struct connection *c; -    assert(p); +    connection *c; +    pa_assert(p);      while ((c = pa_idxset_first(p->connections, NULL))) -        connection_free(c); +        connection_unlink(c);      pa_idxset_free(p->connections, NULL, NULL);      if (p->server) { @@ -2553,7 +3071,12 @@ void pa_protocol_native_free(pa_protocol_native *p) {      pa_xfree(p);  } -pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) { +pa_protocol_native* pa_protocol_native_new_iochannel( +        pa_core*core, +        pa_iochannel *io, +        pa_module *m, +        pa_modargs *ma) { +      pa_protocol_native *p;      if (!(p = protocol_new_internal(core, m, ma))) diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 31ad6ddd..64e2a81c 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -25,7 +25,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <limits.h>  #include <stdio.h> @@ -41,101 +40,154 @@  #include <pulsecore/namereg.h>  #include <pulsecore/log.h>  #include <pulsecore/core-error.h> +#include <pulsecore/atomic.h> +#include <pulsecore/thread-mq.h>  #include "protocol-simple.h"  /* Don't allow more than this many concurrent connections */  #define MAX_CONNECTIONS 10 -struct connection { +typedef struct connection { +    pa_msgobject parent;      pa_protocol_simple *protocol;      pa_iochannel *io;      pa_sink_input *sink_input;      pa_source_output *source_output;      pa_client *client;      pa_memblockq *input_memblockq, *output_memblockq; -    pa_defer_event *defer_event;      int dead;      struct {          pa_memblock *current_memblock;          size_t memblock_index, fragment_size; +        pa_atomic_t missing;      } playback; -}; +} connection; + +PA_DECLARE_CLASS(connection); +#define CONNECTION(o) (connection_cast(o)) +static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);  struct pa_protocol_simple {      pa_module *module;      pa_core *core;      pa_socket_server*server;      pa_idxset *connections; +      enum {          RECORD = 1,          PLAYBACK = 2,          DUPLEX = 3      } mode; +      pa_sample_spec sample_spec;      char *source_name, *sink_name;  }; +enum { +    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */ +    SINK_INPUT_MESSAGE_DISABLE_PREBUF /* disabled prebuf, get playback started. */ +}; + +enum { +    CONNECTION_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */ +    CONNECTION_MESSAGE_POST_DATA,         /* data from source output to main loop */ +    CONNECTION_MESSAGE_UNLINK_CONNECTION    /* Please drop a aconnection now */ +}; + +  #define PLAYBACK_BUFFER_SECONDS (.5)  #define PLAYBACK_BUFFER_FRAGMENTS (10)  #define RECORD_BUFFER_SECONDS (5)  #define RECORD_BUFFER_FRAGMENTS (100) -static void connection_free(struct connection *c) { -    assert(c); +static void connection_unlink(connection *c) { +    pa_assert(c); -    pa_idxset_remove_by_data(c->protocol->connections, c, NULL); +    if (!c->protocol) +        return; -    if (c->playback.current_memblock) -        pa_memblock_unref(c->playback.current_memblock);      if (c->sink_input) { -        pa_sink_input_disconnect(c->sink_input); +        pa_sink_input_unlink(c->sink_input);          pa_sink_input_unref(c->sink_input); +        c->sink_input = NULL;      } +      if (c->source_output) { -        pa_source_output_disconnect(c->source_output); +        pa_source_output_unlink(c->source_output);          pa_source_output_unref(c->source_output); +        c->source_output = NULL;      } -    if (c->client) + +    if (c->client) {          pa_client_free(c->client); -    if (c->io) +        c->client = NULL; +    } + +    if (c->io) {          pa_iochannel_free(c->io); +        c->io = NULL; +    } + +    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c); +    c->protocol = NULL; +    connection_unref(c); +} + +static void connection_free(pa_object *o) { +    connection *c = CONNECTION(o); +    pa_assert(c); + +    connection_unref(c); + +    if (c->playback.current_memblock) +        pa_memblock_unref(c->playback.current_memblock); +      if (c->input_memblockq)          pa_memblockq_free(c->input_memblockq);      if (c->output_memblockq)          pa_memblockq_free(c->output_memblockq); -    if (c->defer_event) -        c->protocol->core->mainloop->defer_free(c->defer_event); +      pa_xfree(c);  } -static int do_read(struct connection *c) { +static int do_read(connection *c) {      pa_memchunk chunk;      ssize_t r;      size_t l; +    void *p; + +    connection_assert_ref(c); -    if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) +    if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)          return 0;      if (l > c->playback.fragment_size)          l = c->playback.fragment_size;      if (c->playback.current_memblock) -        if (c->playback.current_memblock->length - c->playback.memblock_index < l) { +        if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {              pa_memblock_unref(c->playback.current_memblock);              c->playback.current_memblock = NULL;              c->playback.memblock_index = 0;          }      if (!c->playback.current_memblock) { -        c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2); -        assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); +        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l));          c->playback.memblock_index = 0;      } -    if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { +    p = pa_memblock_acquire(c->playback.current_memblock); +    r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l); +    pa_memblock_release(c->playback.current_memblock); + +    if (r <= 0) { + +        if (r < 0 && (errno == EINTR || errno == EAGAIN)) +            return 0; +          pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));          return -1;      } @@ -143,50 +195,55 @@ static int do_read(struct connection *c) {      chunk.memblock = c->playback.current_memblock;      chunk.index = c->playback.memblock_index;      chunk.length = r; -    assert(chunk.memblock);      c->playback.memblock_index += r; -    assert(c->input_memblockq); -    pa_memblockq_push_align(c->input_memblockq, &chunk); -    assert(c->sink_input); -    pa_sink_notify(c->sink_input->sink); +    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);      return 0;  } -static int do_write(struct connection *c) { +static int do_write(connection *c) {      pa_memchunk chunk;      ssize_t r; +    void *p; + +    connection_assert_ref(c);      if (!c->source_output)          return 0; -    assert(c->output_memblockq); -    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) +    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) { +/*         pa_log("peek failed"); */          return 0; +    } + +    pa_assert(chunk.memblock); +    pa_assert(chunk.length); -    assert(chunk.memblock && chunk.length); +    p = pa_memblock_acquire(chunk.memblock); +    r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length); +    pa_memblock_release(chunk.memblock); + +    pa_memblock_unref(chunk.memblock); + +    if (r < 0) { + +        if (errno == EINTR || errno == EAGAIN) +            return 0; -    if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { -        pa_memblock_unref(chunk.memblock);          pa_log("write(): %s", pa_cstrerror(errno));          return -1;      } -    pa_memblockq_drop(c->output_memblockq, &chunk, r); -    pa_memblock_unref(chunk.memblock); - -    pa_source_notify(c->source_output->source); +    pa_memblockq_drop(c->output_memblockq, r);      return 0;  } -static void do_work(struct connection *c) { -    assert(c); - -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); -    c->protocol->core->mainloop->defer_enable(c->defer_event, 0); +static void do_work(connection *c) { +    connection_assert_ref(c);      if (c->dead)          return; @@ -207,103 +264,182 @@ static void do_work(struct connection *c) {  fail:      if (c->sink_input) { + +        /* If there is a sink input, we first drain what we already have read before shutting down the connection */          c->dead = 1;          pa_iochannel_free(c->io);          c->io = NULL; -        pa_memblockq_prebuf_disable(c->input_memblockq); -        pa_sink_notify(c->sink_input->sink); +        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);      } else -        connection_free(c); +        connection_unlink(c);  } -/*** sink_input callbacks ***/ +static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    connection *c = CONNECTION(o); +    connection_assert_ref(c); -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { -    struct connection*c; -    assert(i && i->userdata && chunk); -    c = i->userdata; +    switch (code) { +        case CONNECTION_MESSAGE_REQUEST_DATA: +            do_work(c); +            break; -    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { +        case CONNECTION_MESSAGE_POST_DATA: +/*             pa_log("got data %u", chunk->length); */ +            pa_memblockq_push_align(c->output_memblockq, chunk); +            do_work(c); +            break; -        if (c->dead) -            connection_free(c); - -        return -1; +        case CONNECTION_MESSAGE_UNLINK_CONNECTION: +            connection_unlink(c); +            break;      }      return 0;  } -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { -    struct connection*c = i->userdata; -    assert(i && c && length); +/*** sink_input callbacks ***/ + +/* Called from thread context */ +static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_sink_input *i = PA_SINK_INPUT(o); +    connection*c; + +    pa_sink_input_assert_ref(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); + +    switch (code) { + +        case SINK_INPUT_MESSAGE_POST_DATA: { +            pa_assert(chunk); + +            /* New data from the main loop */ +            pa_memblockq_push_align(c->input_memblockq, chunk); + +/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ + +            return 0; +        } + +        case SINK_INPUT_MESSAGE_DISABLE_PREBUF: { +            pa_memblockq_prebuf_disable(c->input_memblockq); +            return 0; +        } + +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { +            pa_usec_t *r = userdata; -    pa_memblockq_drop(c->input_memblockq, chunk, length); +            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -    /* do something */ -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); -    c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +            /* Fall through, the default handler will add in the extra +             * latency added by the resampler */ +        } + +        default: +            return pa_sink_input_process_msg(o, code, userdata, offset, chunk); +    }  } -static void sink_input_kill_cb(pa_sink_input *i) { -    assert(i && i->userdata); -    connection_free((struct connection *) i->userdata); +/* Called from thread context */ +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    connection *c; +    int r; + +    pa_assert(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); +    pa_assert(chunk); + +    r = pa_memblockq_peek(c->input_memblockq, chunk); + +/*     pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */ + +    if (c->dead && r < 0) +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + +    return r;  } +/* Called from thread context */ +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    connection *c; +    size_t old, new; + +    pa_assert(i); +    c = CONNECTION(i->userdata); +    connection_assert_ref(c); +    pa_assert(length); + +    old = pa_memblockq_missing(c->input_memblockq); +    pa_memblockq_drop(c->input_memblockq, length); +    new = pa_memblockq_missing(c->input_memblockq); -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { -    struct connection*c = i->userdata; -    assert(i && c); -    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +    if (new > old) { +        if (pa_atomic_add(&c->playback.missing, new - old) <= 0) +            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); +    } +} + +/* Called from main context */ +static void sink_input_kill_cb(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    connection_unlink(CONNECTION(i->userdata));  }  /*** source_output callbacks ***/ +/* Called from thread context */  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { -    struct connection *c = o->userdata; -    assert(o && c && chunk); +    connection *c; -    pa_memblockq_push(c->output_memblockq, chunk); +    pa_assert(o); +    c = CONNECTION(o->userdata); +    pa_assert(c); +    pa_assert(chunk); -    /* do something */ -    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); -    c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);  } +/* Called from main context */  static void source_output_kill_cb(pa_source_output *o) { -    assert(o && o->userdata); -    connection_free((struct connection *) o->userdata); +    pa_source_output_assert_ref(o); + +    connection_unlink(CONNECTION(o->userdata));  } +/* Called from main context */  static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { -    struct connection*c = o->userdata; -    assert(o && c); +    connection*c; + +    pa_assert(o); +    c = CONNECTION(o->userdata); +    pa_assert(c); +      return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);  }  /*** client callbacks ***/ -static void client_kill_cb(pa_client *c) { -    assert(c && c->userdata); -    connection_free((struct connection *) c->userdata); +static void client_kill_cb(pa_client *client) { +    connection*c; + +    pa_assert(client); +    c = CONNECTION(client->userdata); +    pa_assert(c); + +    connection_unlink(c);  }  /*** pa_iochannel callbacks ***/  static void io_callback(pa_iochannel*io, void *userdata) { -    struct connection *c = userdata; -    assert(io && c && c->io == io); - -    do_work(c); -} - -/*** fixed callback ***/ +    connection *c = CONNECTION(userdata); -static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { -    struct connection *c = userdata; -    assert(a && c && c->defer_event == e); +    connection_assert_ref(c); +    pa_assert(io);      do_work(c);  } @@ -312,9 +448,12 @@ static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata)  static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {      pa_protocol_simple *p = userdata; -    struct connection *c = NULL; +    connection *c = NULL;      char cname[256]; -    assert(s && io && p); + +    pa_assert(s); +    pa_assert(io); +    pa_assert(p);      if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {          pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -322,21 +461,22 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)          return;      } -    c = pa_xmalloc(sizeof(struct connection)); +    c = pa_msgobject_new(connection); +    c->parent.parent.free = connection_free; +    c->parent.process_msg = connection_process_msg;      c->io = io;      c->sink_input = NULL;      c->source_output = NULL; -    c->defer_event = NULL;      c->input_memblockq = c->output_memblockq = NULL;      c->protocol = p;      c->playback.current_memblock = NULL;      c->playback.memblock_index = 0;      c->playback.fragment_size = 0;      c->dead = 0; +    pa_atomic_store(&c->playback.missing, 0);      pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); -    c->client = pa_client_new(p->core, __FILE__, cname); -    assert(c->client); +    pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));      c->client->owner = p->module;      c->client->kill = client_kill_cb;      c->client->userdata = c; @@ -357,10 +497,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)              goto fail;          } +        c->sink_input->parent.process_msg = sink_input_process_msg;          c->sink_input->peek = sink_input_peek_cb;          c->sink_input->drop = sink_input_drop_cb;          c->sink_input->kill = sink_input_kill_cb; -        c->sink_input->get_latency = sink_input_get_latency_cb;          c->sink_input->userdata = c;          l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); @@ -372,11 +512,12 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)                  (size_t) -1,                  l/PLAYBACK_BUFFER_FRAGMENTS,                  NULL); -        assert(c->input_memblockq);          pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); -        c->playback.fragment_size = l/10; +        c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; + +        pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); -        pa_sink_notify(c->sink_input->sink); +        pa_sink_input_put(c->sink_input);      }      if (p->mode & RECORD) { @@ -409,29 +550,29 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)                  0,                  NULL);          pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); -        pa_source_notify(c->source_output->source); + +        pa_source_output_put(c->source_output);      }      pa_iochannel_set_callback(c->io, io_callback, c);      pa_idxset_put(p->connections, c, NULL); -    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); -    assert(c->defer_event); -    p->core->mainloop->defer_enable(c->defer_event, 0); -      return;  fail:      if (c) -        connection_free(c); +        connection_unlink(c);  }  pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {      pa_protocol_simple* p = NULL;      int enable; -    assert(core && server && ma); -    p = pa_xmalloc0(sizeof(pa_protocol_simple)); +    pa_assert(core); +    pa_assert(server); +    pa_assert(ma); + +    p = pa_xnew0(pa_protocol_simple, 1);      p->module = m;      p->core = core;      p->server = server; @@ -472,23 +613,24 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv  fail:      if (p)          pa_protocol_simple_free(p); +      return NULL;  }  void pa_protocol_simple_free(pa_protocol_simple *p) { -    struct connection *c; -    assert(p); +    connection *c; +    pa_assert(p);      if (p->connections) {          while((c = pa_idxset_first(p->connections, NULL))) -            connection_free(c); +            connection_unlink(c);          pa_idxset_free(p->connections, NULL, NULL);      }      if (p->server)          pa_socket_server_unref(p->server); +      pa_xfree(p);  } - diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c index fae1e49b..a6932158 100644 --- a/src/pulsecore/pstream-util.c +++ b/src/pulsecore/pstream-util.c @@ -25,9 +25,8 @@  #include <config.h>  #endif -#include <assert.h> -  #include <pulsecore/native-common.h> +#include <pulsecore/macro.h>  #include "pstream-util.h" @@ -35,20 +34,20 @@ void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const      size_t length;      uint8_t *data;      pa_packet *packet; -    assert(p); -    assert(t); -    data = pa_tagstruct_free_data(t, &length); -    assert(data && length); -    packet = pa_packet_new_dynamic(data, length); -    assert(packet); +    pa_assert(p); +    pa_assert(t); + +    pa_assert_se(data = pa_tagstruct_free_data(t, &length)); +    pa_assert_se(packet = pa_packet_new_dynamic(data, length));      pa_pstream_send_packet(p, packet, creds);      pa_packet_unref(packet);  }  void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) { -    pa_tagstruct *t = pa_tagstruct_new(NULL, 0); -    assert(t); +    pa_tagstruct *t; + +    pa_assert_se(t = pa_tagstruct_new(NULL, 0));      pa_tagstruct_putu32(t, PA_COMMAND_ERROR);      pa_tagstruct_putu32(t, tag);      pa_tagstruct_putu32(t, error); @@ -56,8 +55,9 @@ void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {  }  void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) { -    pa_tagstruct *t = pa_tagstruct_new(NULL, 0); -    assert(t); +    pa_tagstruct *t; + +    pa_assert_se(t = pa_tagstruct_new(NULL, 0));      pa_tagstruct_putu32(t, PA_COMMAND_REPLY);      pa_tagstruct_putu32(t, tag);      pa_pstream_send_tagstruct(p, t); diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index fdb1a66a..9d32a363 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -28,7 +28,6 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <unistd.h>  #ifdef HAVE_SYS_SOCKET_H @@ -41,16 +40,17 @@  #include <netinet/in.h>  #endif -#include "winsock.h"  #include <pulse/xmalloc.h> +#include <pulsecore/winsock.h>  #include <pulsecore/queue.h>  #include <pulsecore/log.h>  #include <pulsecore/core-scache.h>  #include <pulsecore/creds.h> -#include <pulsecore/mutex.h>  #include <pulsecore/refcnt.h> +#include <pulsecore/flist.h> +#include <pulsecore/macro.h>  #include "pstream.h" @@ -84,7 +84,8 @@ typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];  #define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))  #define FRAME_SIZE_MAX_ALLOW PA_SCACHE_ENTRY_SIZE_MAX /* allow uploading a single sample in one frame at max */ -#define FRAME_SIZE_MAX_USE (1024*64) + +PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);  struct item_info {      enum { @@ -94,7 +95,6 @@ struct item_info {          PA_PSTREAM_ITEM_SHMREVOKE      } type; -      /* packet info */      pa_packet *packet;  #ifdef HAVE_CREDS @@ -118,8 +118,8 @@ struct pa_pstream {      pa_mainloop_api *mainloop;      pa_defer_event *defer_event;      pa_iochannel *io; +      pa_queue *send_queue; -    pa_mutex *mutex;      int dead; @@ -129,6 +129,7 @@ struct pa_pstream {          uint32_t shm_info[PA_PSTREAM_SHM_MAX];          void *data;          size_t index; +        pa_memchunk memchunk;      } write;      struct { @@ -156,6 +157,12 @@ struct pa_pstream {      pa_pstream_notify_cb_t die_callback;      void *die_callback_userdata; +    pa_pstream_block_id_cb_t revoke_callback; +    void *revoke_callback_userdata; + +    pa_pstream_block_id_cb_t release_callback; +    void *release_callback_userdata; +      pa_mempool *mempool;  #ifdef HAVE_CREDS @@ -168,13 +175,11 @@ static int do_write(pa_pstream *p);  static int do_read(pa_pstream *p);  static void do_something(pa_pstream *p) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      pa_pstream_ref(p); -    pa_mutex_lock(p->mutex); -      p->mainloop->defer_enable(p->defer_event, 0);      if (!p->dead && pa_iochannel_is_readable(p->io)) { @@ -188,28 +193,24 @@ static void do_something(pa_pstream *p) {              goto fail;      } -    pa_mutex_unlock(p->mutex); -      pa_pstream_unref(p);      return;  fail: -    p->dead = 1; -      if (p->die_callback)          p->die_callback(p, p->die_callback_userdata); -    pa_mutex_unlock(p->mutex); - +    pa_pstream_unlink(p);      pa_pstream_unref(p);  }  static void io_callback(pa_iochannel*io, void *userdata) {      pa_pstream *p = userdata; -    assert(p); -    assert(p->io == io); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p->io == io);      do_something(p);  } @@ -217,9 +218,10 @@ static void io_callback(pa_iochannel*io, void *userdata) {  static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {      pa_pstream *p = userdata; -    assert(p); -    assert(p->defer_event == e); -    assert(p->mainloop == m); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p->defer_event == e); +    pa_assert(p->mainloop == m);      do_something(p);  } @@ -229,9 +231,9 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd  pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {      pa_pstream *p; -    assert(m); -    assert(io); -    assert(pool); +    pa_assert(m); +    pa_assert(io); +    pa_assert(pool);      p = pa_xnew(pa_pstream, 1);      PA_REFCNT_INIT(p); @@ -239,17 +241,15 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo      pa_iochannel_set_callback(io, io_callback, p);      p->dead = 0; -    p->mutex = pa_mutex_new(1); -      p->mainloop = m;      p->defer_event = m->defer_new(m, defer_callback, p);      m->defer_enable(p->defer_event, 0);      p->send_queue = pa_queue_new(); -    assert(p->send_queue);      p->write.current = NULL;      p->write.index = 0; +    pa_memchunk_reset(&p->write.memchunk);      p->read.memblock = NULL;      p->read.packet = NULL;      p->read.index = 0; @@ -262,6 +262,10 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo      p->drain_callback_userdata = NULL;      p->die_callback = NULL;      p->die_callback_userdata = NULL; +    p->revoke_callback = NULL; +    p->revoke_callback_userdata = NULL; +    p->release_callback = NULL; +    p->release_callback_userdata = NULL;      p->mempool = pool; @@ -281,56 +285,57 @@ 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 *p) { +static void item_free(void *item, PA_GCC_UNUSED void *q) {      struct item_info *i = item; -    assert(i); +    pa_assert(i);      if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) { -        assert(i->chunk.memblock); +        pa_assert(i->chunk.memblock);          pa_memblock_unref(i->chunk.memblock);      } else if (i->type == PA_PSTREAM_ITEM_PACKET) { -        assert(i->packet); +        pa_assert(i->packet);          pa_packet_unref(i->packet);      } -    pa_xfree(i); +    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0) +        pa_xfree(i);  }  static void pstream_free(pa_pstream *p) { -    assert(p); +    pa_assert(p); -    pa_pstream_close(p); +    pa_pstream_unlink(p);      pa_queue_free(p->send_queue, item_free, NULL);      if (p->write.current)          item_free(p->write.current, NULL); +    if (p->write.memchunk.memblock) +        pa_memblock_unref(p->write.memchunk.memblock); +      if (p->read.memblock)          pa_memblock_unref(p->read.memblock);      if (p->read.packet)          pa_packet_unref(p->read.packet); -    if (p->mutex) -        pa_mutex_free(p->mutex); -      pa_xfree(p);  }  void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds) {      struct item_info *i; -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); -    assert(packet); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(packet);      if (p->dead) -        goto finish; +        return; + +    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +        i = pa_xnew(struct item_info, 1); -    i = pa_xnew(struct item_info, 1);      i->type = PA_PSTREAM_ITEM_PACKET;      i->packet = pa_packet_ref(packet); @@ -340,37 +345,36 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *cre  #endif      pa_queue_push(p->send_queue, i); -    p->mainloop->defer_enable(p->defer_event, 1); -finish: - -    pa_mutex_unlock(p->mutex); +    p->mainloop->defer_enable(p->defer_event, 1);  }  void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {      size_t length, idx; +    size_t bsm; -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); -    assert(channel != (uint32_t) -1); -    assert(chunk); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(channel != (uint32_t) -1); +    pa_assert(chunk);      if (p->dead) -        goto finish; +        return; -    length = chunk->length;      idx = 0; +    length = chunk->length; + +    bsm = pa_mempool_block_size_max(p->mempool);      while (length > 0) {          struct item_info *i;          size_t n; -        i = pa_xnew(struct item_info, 1); +        if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +            i = pa_xnew(struct item_info, 1);          i->type = PA_PSTREAM_ITEM_MEMBLOCK; -        n = length < FRAME_SIZE_MAX_USE ? length : FRAME_SIZE_MAX_USE; +        n = MIN(length, bsm);          i->chunk.index = chunk->index + idx;          i->chunk.length = n;          i->chunk.memblock = pa_memblock_ref(chunk->memblock); @@ -389,27 +393,20 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa      }      p->mainloop->defer_enable(p->defer_event, 1); - -finish: - -    pa_mutex_unlock(p->mutex);  } -static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) { +void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {      struct item_info *item; -    pa_pstream *p = userdata; - -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (p->dead) -        goto finish; +        return;  /*     pa_log("Releasing block %u", block_id); */ -    item = pa_xnew(struct item_info, 1); +    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +        item = pa_xnew(struct item_info, 1);      item->type = PA_PSTREAM_ITEM_SHMRELEASE;      item->block_id = block_id;  #ifdef HAVE_CREDS @@ -418,27 +415,35 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd      pa_queue_push(p->send_queue, item);      p->mainloop->defer_enable(p->defer_event, 1); - -finish: - -    pa_mutex_unlock(p->mutex);  } -static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) { -    struct item_info *item; +/* might be called from thread context */ +static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {      pa_pstream *p = userdata; -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (p->dead) -        goto finish; +        return; +    if (p->release_callback) +        p->release_callback(p, block_id, p->release_callback_userdata); +    else +        pa_pstream_send_release(p, block_id); +} + +void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) { +    struct item_info *item; +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); + +    if (p->dead) +        return;  /*     pa_log("Revoking block %u", block_id); */ -    item = pa_xnew(struct item_info, 1); +    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +        item = pa_xnew(struct item_info, 1);      item->type = PA_PSTREAM_ITEM_SHMREVOKE;      item->block_id = block_id;  #ifdef HAVE_CREDS @@ -447,21 +452,33 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda      pa_queue_push(p->send_queue, item);      p->mainloop->defer_enable(p->defer_event, 1); +} + +/* might be called from thread context */ +static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) { +    pa_pstream *p = userdata; -finish: +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    pa_mutex_unlock(p->mutex); +    if (p->revoke_callback) +        p->revoke_callback(p, block_id, p->revoke_callback_userdata); +    else +        pa_pstream_send_revoke(p, block_id);  }  static void prepare_next_write_item(pa_pstream *p) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    if (!(p->write.current = pa_queue_pop(p->send_queue))) +    p->write.current = pa_queue_pop(p->send_queue); + +    if (!p->write.current)          return;      p->write.index = 0;      p->write.data = NULL; +    pa_memchunk_reset(&p->write.memchunk);      p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = 0;      p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); @@ -471,7 +488,7 @@ static void prepare_next_write_item(pa_pstream *p) {      if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) { -        assert(p->write.current->packet); +        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); @@ -489,8 +506,8 @@ static void prepare_next_write_item(pa_pstream *p) {          uint32_t flags;          int send_payload = 1; -        assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK); -        assert(p->write.current->chunk.memblock); +        pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK); +        pa_assert(p->write.current->chunk.memblock);          p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);          p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32)); @@ -502,7 +519,7 @@ static void prepare_next_write_item(pa_pstream *p) {              uint32_t block_id, shm_id;              size_t offset, length; -            assert(p->export); +            pa_assert(p->export);              if (pa_memexport_put(p->export,                                   p->write.current->chunk.memblock, @@ -528,7 +545,9 @@ 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.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index; +            p->write.memchunk = p->write.current->chunk; +            pa_memblock_ref(p->write.memchunk.memblock); +            p->write.data = NULL;          }          p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags); @@ -544,9 +563,10 @@ static int do_write(pa_pstream *p) {      void *d;      size_t l;      ssize_t r; +    pa_memblock *release_memblock = NULL; -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (!p->write.current)          prepare_next_write_item(p); @@ -558,72 +578,105 @@ static int do_write(pa_pstream *p) {          d = (uint8_t*) p->write.descriptor + p->write.index;          l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;      } else { -        assert(p->write.data); +        pa_assert(p->write.data || p->write.memchunk.memblock); + +        if (p->write.data) +            d = p->write.data; +        else { +            d = (uint8_t*) pa_memblock_acquire(p->write.memchunk.memblock) + p->write.memchunk.index; +            release_memblock = p->write.memchunk.memblock; +        } -        d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; +        d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;          l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);      } -    assert(l > 0); +    pa_assert(l > 0);  #ifdef HAVE_CREDS      if (p->send_creds_now) {          if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0) -            return -1; +            goto fail;          p->send_creds_now = 0;      } else  #endif      if ((r = pa_iochannel_write(p->io, d, l)) < 0) -        return -1; +        goto fail; + +    if (release_memblock) +        pa_memblock_release(release_memblock);      p->write.index += r;      if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) { -        assert(p->write.current); -        item_free(p->write.current, (void *) 1); +        pa_assert(p->write.current); +        item_free(p->write.current, NULL);          p->write.current = NULL; +        if (p->write.memchunk.memblock) +            pa_memblock_unref(p->write.memchunk.memblock); + +        pa_memchunk_reset(&p->write.memchunk); +          if (p->drain_callback && !pa_pstream_is_pending(p))              p->drain_callback(p, p->drain_callback_userdata);      }      return 0; + +fail: + +    if (release_memblock) +        pa_memblock_release(release_memblock); + +    return -1;  }  static int do_read(pa_pstream *p) {      void *d;      size_t l;      ssize_t r; - -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_memblock *release_memblock = NULL; +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {          d = (uint8_t*) p->read.descriptor + p->read.index;          l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;      } else { -        assert(p->read.data); -        d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; +        pa_assert(p->read.data || p->read.memblock); + +        if (p->read.data) +            d = p->read.data; +        else { +            d = pa_memblock_acquire(p->read.memblock); +            release_memblock = p->read.memblock; +        } + +        d = (uint8_t*) d + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;          l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);      }  #ifdef HAVE_CREDS      { -        int b = 0; +        pa_bool_t b = 0;          if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->read_creds, &b)) <= 0) -            return -1; +            goto fail;          p->read_creds_valid = p->read_creds_valid || b;      }  #else      if ((r = pa_iochannel_read(p->io, d, l)) <= 0) -        return -1; +        goto fail;  #endif +    if (release_memblock) +        pa_memblock_release(release_memblock); +      p->read.index += r;      if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { @@ -643,7 +696,7 @@ static int do_read(pa_pstream *p) {  /*             pa_log("Got release frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */ -            assert(p->export); +            pa_assert(p->export);              pa_memexport_process_release(p->export, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));              goto frame_done; @@ -654,7 +707,7 @@ static int do_read(pa_pstream *p) {  /*             pa_log("Got revoke frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */ -            assert(p->import); +            pa_assert(p->import);              pa_memimport_process_revoke(p->import, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));              goto frame_done; @@ -667,7 +720,7 @@ static int do_read(pa_pstream *p) {              return -1;          } -        assert(!p->read.packet && !p->read.memblock); +        pa_assert(!p->read.packet && !p->read.memblock);          channel = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]); @@ -704,7 +757,7 @@ static int do_read(pa_pstream *p) {                  /* Frame is a memblock frame */                  p->read.memblock = pa_memblock_new(p->mempool, length); -                p->read.data = p->read.memblock->data; +                p->read.data = NULL;              } else {                  pa_log_warn("Recieved memblock frame with invalid flags value."); @@ -771,9 +824,9 @@ static int do_read(pa_pstream *p) {              } else {                  pa_memblock *b; -                assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA); +                pa_assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA); -                assert(p->import); +                pa_assert(p->import);                  if (!(b = pa_memimport_get(p->import,                                            ntohl(p->read.shm_info[PA_PSTREAM_SHM_BLOCKID]), @@ -791,7 +844,7 @@ static int do_read(pa_pstream *p) {                      chunk.memblock = b;                      chunk.index = 0; -                    chunk.length = b->length; +                    chunk.length = pa_memblock_get_length(b);                      offset = (int64_t) (                              (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) | @@ -819,92 +872,104 @@ frame_done:      p->read.memblock = NULL;      p->read.packet = NULL;      p->read.index = 0; +    p->read.data = NULL;  #ifdef HAVE_CREDS      p->read_creds_valid = 0;  #endif      return 0; + +fail: +    if (release_memblock) +        pa_memblock_release(release_memblock); + +    return -1;  }  void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    pa_mutex_lock(p->mutex);      p->die_callback = cb;      p->die_callback_userdata = userdata; -    pa_mutex_unlock(p->mutex);  }  void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    pa_mutex_lock(p->mutex);      p->drain_callback = cb;      p->drain_callback_userdata = userdata; -    pa_mutex_unlock(p->mutex);  }  void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    pa_mutex_lock(p->mutex);      p->recieve_packet_callback = cb;      p->recieve_packet_callback_userdata = userdata; -    pa_mutex_unlock(p->mutex);  }  void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); -    pa_mutex_lock(p->mutex);      p->recieve_memblock_callback = cb;      p->recieve_memblock_callback_userdata = userdata; -    pa_mutex_unlock(p->mutex); +} + +void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) { +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); + +    p->release_callback = cb; +    p->release_callback_userdata = userdata; +} + +void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) { +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0); + +    p->release_callback = cb; +    p->release_callback_userdata = userdata;  }  int pa_pstream_is_pending(pa_pstream *p) {      int b; -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (p->dead)          b = 0;      else          b = p->write.current || !pa_queue_is_empty(p->send_queue); -    pa_mutex_unlock(p->mutex); -      return b;  }  void pa_pstream_unref(pa_pstream*p) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      if (PA_REFCNT_DEC(p) <= 0)          pstream_free(p);  }  pa_pstream* pa_pstream_ref(pa_pstream*p) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      PA_REFCNT_INC(p);      return p;  } -void pa_pstream_close(pa_pstream *p) { -    assert(p); +void pa_pstream_unlink(pa_pstream *p) { +    pa_assert(p); -    pa_mutex_lock(p->mutex); +    if (p->dead) +        return;      p->dead = 1; @@ -932,15 +997,11 @@ void pa_pstream_close(pa_pstream *p) {      p->drain_callback = NULL;      p->recieve_packet_callback = NULL;      p->recieve_memblock_callback = NULL; - -    pa_mutex_unlock(p->mutex);  }  void pa_pstream_use_shm(pa_pstream *p, int enable) { -    assert(p); -    assert(PA_REFCNT_VALUE(p) > 0); - -    pa_mutex_lock(p->mutex); +    pa_assert(p); +    pa_assert(PA_REFCNT_VALUE(p) > 0);      p->use_shm = enable; @@ -956,6 +1017,4 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {              p->export = NULL;          }      } - -    pa_mutex_unlock(p->mutex);  } diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h index 5900ecea..72babea9 100644 --- a/src/pulsecore/pstream.h +++ b/src/pulsecore/pstream.h @@ -41,6 +41,7 @@ typedef struct pa_pstream pa_pstream;  typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata);  typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);  typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata); +typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);  pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);  void pa_pstream_unref(pa_pstream*p); @@ -48,17 +49,20 @@ pa_pstream* pa_pstream_ref(pa_pstream*p);  void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds);  void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk); +void pa_pstream_send_release(pa_pstream *p, uint32_t block_id); +void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);  void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);  void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);  void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata); -  void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata); +void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata); +void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);  int pa_pstream_is_pending(pa_pstream *p);  void pa_pstream_use_shm(pa_pstream *p, int enable); -void pa_pstream_close(pa_pstream *p); +void pa_pstream_unlink(pa_pstream *p);  #endif diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c index 1dd0f606..9b6a37f0 100644 --- a/src/pulsecore/queue.c +++ b/src/pulsecore/queue.c @@ -25,13 +25,16 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h> +#include <pulsecore/flist.h>  #include "queue.h" +PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); +  struct queue_entry {      struct queue_entry *next;      void *data; @@ -44,25 +47,24 @@ struct pa_queue {  pa_queue* pa_queue_new(void) {      pa_queue *q = pa_xnew(pa_queue, 1); +      q->front = q->back = NULL;      q->length = 0; +      return q;  }  void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) { -    struct queue_entry *e; -    assert(q); - -    e = q->front; -    while (e) { -        struct queue_entry *n = e->next; +    void *data; +    pa_assert(q); +    while ((data = pa_queue_pop(q)))          if (destroy) -            destroy(e->data, userdata); +            destroy(data, userdata); -        pa_xfree(e); -        e = n; -    } +    pa_assert(!q->front); +    pa_assert(!q->back); +    pa_assert(q->length == 0);      pa_xfree(q);  } @@ -70,14 +72,20 @@ void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *  void pa_queue_push(pa_queue *q, void *p) {      struct queue_entry *e; -    e = pa_xnew(struct queue_entry, 1); +    pa_assert(q); +    pa_assert(p); + +    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) +        e = pa_xnew(struct queue_entry, 1); +      e->data = p;      e->next = NULL; -    if (q->back) +    if (q->back) { +        pa_assert(q->front);          q->back->next = e; -    else { -        assert(!q->front); +    } else { +        pa_assert(!q->front);          q->front = e;      } @@ -88,17 +96,22 @@ void pa_queue_push(pa_queue *q, void *p) {  void* pa_queue_pop(pa_queue *q) {      void *p;      struct queue_entry *e; -    assert(q); +    pa_assert(q);      if (!(e = q->front))          return NULL;      q->front = e->next; -    if (q->back == e) + +    if (q->back == e) { +        pa_assert(!e->next);          q->back = NULL; +    }      p = e->data; -    pa_xfree(e); + +    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) +        pa_xfree(e);      q->length--; @@ -106,6 +119,7 @@ void* pa_queue_pop(pa_queue *q) {  }  int pa_queue_is_empty(pa_queue *q) { -    assert(q); +    pa_assert(q); +      return q->length == 0;  } diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c index 3f591917..87afebfa 100644 --- a/src/pulsecore/random.c +++ b/src/pulsecore/random.c @@ -31,21 +31,22 @@  #include <errno.h>  #include <string.h>  #include <stdlib.h> -#include <assert.h>  #include <time.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "random.h"  static int has_whined = 0; -static const char *devices[] = { "/dev/urandom", "/dev/random", NULL }; +static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };  static int random_proper(void *ret_data, size_t length) {  #ifdef OS_IS_WIN32 -    assert(ret_data && length); +    pa_assert(ret_data); +    pa_assert(length > 0);      return -1; @@ -53,9 +54,10 @@ static int random_proper(void *ret_data, size_t length) {      int fd, ret = -1;      ssize_t r = 0; -    const char **device; +    const char *const * device; -    assert(ret_data && length); +    pa_assert(ret_data); +    pa_assert(length > 0);      device = devices; @@ -67,7 +69,7 @@ static int random_proper(void *ret_data, size_t length) {              if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)                  ret = -1; -            close(fd); +            pa_close(fd);          } else              ret = -1; @@ -84,7 +86,7 @@ void pa_random_seed(void) {      if (random_proper(&seed, sizeof(unsigned int)) < 0) {          if (!has_whined) -            pa_log_warn("failed to get proper entropy. Falling back to seeding with current time."); +            pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");          has_whined = 1;          seed = (unsigned int) time(NULL); @@ -97,13 +99,14 @@ void pa_random(void *ret_data, size_t length) {      uint8_t *p;      size_t l; -    assert(ret_data && length); +    pa_assert(ret_data); +    pa_assert(length > 0);      if (random_proper(ret_data, length) >= 0)          return;      if (!has_whined) -        pa_log_warn("failed to get proper entropy. Falling back to unsecure pseudo RNG."); +        pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");      has_whined = 1;      for (p = ret_data, l = length; l > 0; p++, l--) diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h index 43433ff8..64271ab2 100644 --- a/src/pulsecore/refcnt.h +++ b/src/pulsecore/refcnt.h @@ -27,18 +27,18 @@  #include <pulsecore/atomic.h>  #define PA_REFCNT_DECLARE \ -  pa_atomic_int_t _ref +    pa_atomic_t _ref  #define PA_REFCNT_INIT(p) \ -  pa_atomic_store(&p->_ref, 1) +    pa_atomic_store(&(p)->_ref, 1)  #define PA_REFCNT_INC(p) \ -  pa_atomic_inc(&p->_ref) +    pa_atomic_inc(&(p)->_ref)  #define PA_REFCNT_DEC(p) \ -  (pa_atomic_dec(&p->_ref)-1) +    (pa_atomic_dec(&(p)->_ref)-1)  #define PA_REFCNT_VALUE(p) \ -  pa_atomic_load(&p->_ref) +    pa_atomic_load(&(p)->_ref)  #endif diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 3827ff94..5bbc6bf4 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -25,53 +25,133 @@  #include <config.h>  #endif -#include <assert.h>  #include <string.h> +#if HAVE_LIBSAMPLERATE  #include <samplerate.h> +#endif +  #include <liboil/liboilfuncs.h>  #include <liboil/liboil.h>  #include <pulse/xmalloc.h> -  #include <pulsecore/sconv.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> + +#include "speexwrap.h" + +#include "ffmpeg/avcodec.h"  #include "resampler.h" +/* Number of samples of extra space we allow the resamplers to return */ +#define EXTRA_SAMPLES 128 +  struct pa_resampler {      pa_resample_method_t resample_method;      pa_sample_spec i_ss, o_ss;      pa_channel_map i_cm, o_cm; -    size_t i_fz, o_fz; +    size_t i_fz, o_fz, w_sz;      pa_mempool *mempool; -    void (*impl_free)(pa_resampler *r); -    void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate); -    void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out); -    void *impl_data; -}; - -struct impl_libsamplerate { -    pa_memblock *buf1_block, *buf2_block, *buf3_block, *buf4_block; -    float* buf1, *buf2, *buf3, *buf4; +    pa_memchunk buf1, buf2, buf3, buf4;      unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples; -    pa_convert_to_float32ne_func_t to_float32ne_func; -    pa_convert_from_float32ne_func_t from_float32ne_func; -    SRC_STATE *src_state; +    pa_sample_format_t work_format; + +    pa_convert_func_t to_work_format_func; +    pa_convert_func_t from_work_format_func;      int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];      int map_required; -}; -struct impl_trivial { -    unsigned o_counter; -    unsigned i_counter; +    void (*impl_free)(pa_resampler *r); +    void (*impl_update_rates)(pa_resampler *r); +    void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples); + +    struct { /* data specific to the trivial resampler */ +        unsigned o_counter; +        unsigned i_counter; +    } trivial; + +#ifdef HAVE_LIBSAMPLERATE +    struct { /* data specific to libsamplerate */ +        SRC_STATE *state; +    } src; +#endif + +    struct { /* data specific to speex */ +        SpeexResamplerState* state; +    } speex; + +    struct { /* data specific to ffmpeg */ +        struct AVResampleContext *state; +        pa_memchunk buf[PA_CHANNELS_MAX]; +    } ffmpeg;  }; -static int libsamplerate_init(pa_resampler*r); +static int copy_init(pa_resampler *r);  static int trivial_init(pa_resampler*r); +static int speex_init(pa_resampler*r); +static int ffmpeg_init(pa_resampler*r); +#ifdef HAVE_LIBSAMPLERATE +static int libsamplerate_init(pa_resampler*r); +#endif + +static void calc_map_table(pa_resampler *r); + +static int (* const init_table[])(pa_resampler*r) = { +#ifdef HAVE_LIBSAMPLERATE +    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = libsamplerate_init, +    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = libsamplerate_init, +    [PA_RESAMPLER_SRC_SINC_FASTEST]        = libsamplerate_init, +    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = libsamplerate_init, +    [PA_RESAMPLER_SRC_LINEAR]              = libsamplerate_init, +#else +    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = NULL, +    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL, +    [PA_RESAMPLER_SRC_SINC_FASTEST]        = NULL, +    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = NULL, +    [PA_RESAMPLER_SRC_LINEAR]              = NULL, +#endif +    [PA_RESAMPLER_TRIVIAL]                 = trivial_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+3]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+4]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+5]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+6]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+7]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+8]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+9]      = speex_init, +    [PA_RESAMPLER_SPEEX_FLOAT_BASE+10]     = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+0]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+1]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+2]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+3]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+4]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+5]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+6]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+7]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = speex_init, +    [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = speex_init, +    [PA_RESAMPLER_FFMPEG]                  = ffmpeg_init, +    [PA_RESAMPLER_AUTO]                    = NULL, +    [PA_RESAMPLER_COPY]                    = copy_init +}; + +static inline size_t sample_size(pa_sample_format_t f) { +    pa_sample_spec ss = { +        .format = f, +        .rate = 0, +        .channels = 1 +    }; + +    return pa_sample_size(&ss); +}  pa_resampler* pa_resampler_new(          pa_mempool *pool, @@ -79,25 +159,51 @@ pa_resampler* pa_resampler_new(          const pa_channel_map *am,          const pa_sample_spec *b,          const pa_channel_map *bm, -        pa_resample_method_t resample_method) { +        pa_resample_method_t resample_method, +        int variable_rate) {      pa_resampler *r = NULL; -    assert(pool); -    assert(a); -    assert(b); -    assert(pa_sample_spec_valid(a)); -    assert(pa_sample_spec_valid(b)); -    assert(resample_method != PA_RESAMPLER_INVALID); +    pa_assert(pool); +    pa_assert(a); +    pa_assert(b); +    pa_assert(pa_sample_spec_valid(a)); +    pa_assert(pa_sample_spec_valid(b)); +    pa_assert(resample_method >= 0); +    pa_assert(resample_method < PA_RESAMPLER_MAX); + +    /* Fix method */ + +    if (!variable_rate && a->rate == b->rate) { +        pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates."); +        resample_method = PA_RESAMPLER_COPY; +    } + +    if (!pa_resample_method_supported(resample_method)) { +        pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(resample_method)); +        resample_method = PA_RESAMPLER_AUTO; +    } + +    if (resample_method == PA_RESAMPLER_FFMPEG && variable_rate) { +        pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'."); +        resample_method = PA_RESAMPLER_AUTO; +    } + +    if (resample_method == PA_RESAMPLER_COPY && (variable_rate || a->rate != b->rate)) { +        pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'."); +        resample_method = PA_RESAMPLER_AUTO; +    } + +    if (resample_method == PA_RESAMPLER_AUTO) +        resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;      r = pa_xnew(pa_resampler, 1); -    r->impl_data = NULL;      r->mempool = pool;      r->resample_method = resample_method;      r->impl_free = NULL; -    r->impl_update_input_rate = NULL; -    r->impl_run = NULL; +    r->impl_update_rates = NULL; +    r->impl_resample = NULL;      /* Fill sample specs */      r->i_ss = *a; @@ -116,25 +222,66 @@ pa_resampler* pa_resampler_new(      r->i_fz = pa_frame_size(a);      r->o_fz = pa_frame_size(b); -    /* Choose implementation */ -    if (a->channels != b->channels || -        a->format != b->format || -        !pa_channel_map_equal(&r->i_cm, &r->o_cm) || -        resample_method != PA_RESAMPLER_TRIVIAL) { +    pa_memchunk_reset(&r->buf1); +    pa_memchunk_reset(&r->buf2); +    pa_memchunk_reset(&r->buf3); +    pa_memchunk_reset(&r->buf4); + +    r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0; + +    calc_map_table(r); + +    pa_log_info("Using resampler '%s'", pa_resample_method_to_string(resample_method)); + +    if ((resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) || +        (resample_method == PA_RESAMPLER_FFMPEG)) +        r->work_format = PA_SAMPLE_S16NE; +    else if (resample_method == PA_RESAMPLER_TRIVIAL || resample_method == PA_RESAMPLER_COPY) { -        /* Use the libsamplerate based resampler for the complicated cases */ -        if (resample_method == PA_RESAMPLER_TRIVIAL) -            r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; +        if (r->map_required || a->format != b->format) { -        if (libsamplerate_init(r) < 0) +            if (a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE || +                b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE) +                r->work_format = PA_SAMPLE_FLOAT32NE; +            else +                r->work_format = PA_SAMPLE_S16NE; + +        } else +            r->work_format = a->format; + +    } else +        r->work_format = PA_SAMPLE_FLOAT32NE; + +    pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format)); + +    r->w_sz = sample_size(r->work_format); + +    if (r->i_ss.format == r->work_format) +        r->to_work_format_func = NULL; +    else if (r->work_format == PA_SAMPLE_FLOAT32NE) { +        if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) +            goto fail; +    } else { +        pa_assert(r->work_format == PA_SAMPLE_S16NE); +        if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))              goto fail; +    } +    if (r->o_ss.format == r->work_format) +        r->from_work_format_func = NULL; +    else if (r->work_format == PA_SAMPLE_FLOAT32NE) { +        if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) +            goto fail;      } else { -        /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */ -        if (trivial_init(r) < 0) +        pa_assert(r->work_format == PA_SAMPLE_S16NE); +        if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))              goto fail;      } +    /* initialize implementation */ +    if (init_table[resample_method](r) < 0) +        goto fail; +      return r;  fail: @@ -145,41 +292,85 @@ fail:  }  void pa_resampler_free(pa_resampler *r) { -    assert(r); +    pa_assert(r);      if (r->impl_free)          r->impl_free(r); +    if (r->buf1.memblock) +        pa_memblock_unref(r->buf1.memblock); +    if (r->buf2.memblock) +        pa_memblock_unref(r->buf2.memblock); +    if (r->buf3.memblock) +        pa_memblock_unref(r->buf3.memblock); +    if (r->buf4.memblock) +        pa_memblock_unref(r->buf4.memblock); +      pa_xfree(r);  }  void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) { -    assert(r); -    assert(rate > 0); +    pa_assert(r); +    pa_assert(rate > 0);      if (r->i_ss.rate == rate)          return;      r->i_ss.rate = rate; -    if (r->impl_update_input_rate) -        r->impl_update_input_rate(r, rate); +    r->impl_update_rates(r);  } -void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { -    assert(r && in && out && r->impl_run); +void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) { +    pa_assert(r); +    pa_assert(rate > 0); -    r->impl_run(r, in, out); +    if (r->o_ss.rate == rate) +        return; + +    r->o_ss.rate = rate; + +    r->impl_update_rates(r);  }  size_t pa_resampler_request(pa_resampler *r, size_t out_length) { -    assert(r); +    pa_assert(r);      return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;  } +size_t pa_resampler_max_block_size(pa_resampler *r) { +    size_t block_size_max; +    pa_sample_spec ss; +    size_t fs; + +    pa_assert(r); + +    block_size_max = pa_mempool_block_size_max(r->mempool); + +    /* We deduce the "largest" sample spec we're using during the +     * conversion */ +    ss = r->i_ss; +    if (r->o_ss.channels > ss.channels) +        ss.channels = r->o_ss.channels; + +    /* We silently assume that the format enum is ordered by size */ +    if (r->o_ss.format > ss.format) +        ss.format = r->o_ss.format; +    if (r->work_format > ss.format) +        ss.format = r->work_format; + +    if (r->o_ss.rate > ss.rate) +        ss.rate = r->o_ss.rate; + +    fs = pa_frame_size(&ss); + +    return (((block_size_max/fs + EXTRA_SAMPLES)*r->i_ss.rate)/ss.rate)*r->i_fz; +} +  pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { -    assert(r); +    pa_assert(r); +      return r->resample_method;  } @@ -189,7 +380,32 @@ static const char * const resample_methods[] = {      "src-sinc-fastest",      "src-zero-order-hold",      "src-linear", -    "trivial" +    "trivial", +    "speex-float-0", +    "speex-float-1", +    "speex-float-2", +    "speex-float-3", +    "speex-float-4", +    "speex-float-5", +    "speex-float-6", +    "speex-float-7", +    "speex-float-8", +    "speex-float-9", +    "speex-float-10", +    "speex-fixed-0", +    "speex-fixed-1", +    "speex-fixed-2", +    "speex-fixed-3", +    "speex-fixed-4", +    "speex-fixed-5", +    "speex-fixed-6", +    "speex-fixed-7", +    "speex-fixed-8", +    "speex-fixed-9", +    "speex-fixed-10", +    "ffmpeg", +    "auto", +    "copy"  };  const char *pa_resample_method_to_string(pa_resample_method_t m) { @@ -200,52 +416,43 @@ const char *pa_resample_method_to_string(pa_resample_method_t m) {      return resample_methods[m];  } -pa_resample_method_t pa_parse_resample_method(const char *string) { -    pa_resample_method_t m; +int pa_resample_method_supported(pa_resample_method_t m) { -    assert(string); +    if (m < 0 || m >= PA_RESAMPLER_MAX) +        return 0; -    for (m = 0; m < PA_RESAMPLER_MAX; m++) -        if (!strcmp(string, resample_methods[m])) -            return m; +#ifndef HAVE_LIBSAMPLERATE +    if (m <= PA_RESAMPLER_SRC_LINEAR) +        return 0; +#endif -    return PA_RESAMPLER_INVALID; +    return 1;  } +pa_resample_method_t pa_parse_resample_method(const char *string) { +    pa_resample_method_t m; -/*** libsamplerate based implementation ***/ - -static void libsamplerate_free(pa_resampler *r) { -    struct impl_libsamplerate *u; +    pa_assert(string); -    assert(r); -    assert(r->impl_data); +    for (m = 0; m < PA_RESAMPLER_MAX; m++) +        if (!strcmp(string, resample_methods[m])) +            return m; -    u = r->impl_data; +    if (!strcmp(string, "speex-fixed")) +        return PA_RESAMPLER_SPEEX_FIXED_BASE + 0; -    if (u->src_state) -        src_delete(u->src_state); +    if (!strcmp(string, "speex-float")) +        return PA_RESAMPLER_SPEEX_FLOAT_BASE + 0; -    if (u->buf1_block) -        pa_memblock_unref(u->buf1_block); -    if (u->buf2_block) -        pa_memblock_unref(u->buf2_block); -    if (u->buf3_block) -        pa_memblock_unref(u->buf3_block); -    if (u->buf4_block) -        pa_memblock_unref(u->buf4_block); -    pa_xfree(u); +    return PA_RESAMPLER_INVALID;  }  static void calc_map_table(pa_resampler *r) { -    struct impl_libsamplerate *u;      unsigned oc; -    assert(r); -    assert(r->impl_data); -    u = r->impl_data; +    pa_assert(r); -    if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels))) +    if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || !pa_channel_map_equal(&r->i_cm, &r->o_cm))))          return;      for (oc = 0; oc < r->o_ss.channels; oc++) { @@ -263,392 +470,590 @@ static void calc_map_table(pa_resampler *r) {                  (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||                  (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO)) -                u->map_table[oc][i++] = ic; +                r->map_table[oc][i++] = ic;          }          /* Add an end marker */          if (i < PA_CHANNELS_MAX) -            u->map_table[oc][i] = -1; +            r->map_table[oc][i] = -1;      }  } -static float * convert_to_float(pa_resampler *r, void *input, unsigned n_frames) { -    struct impl_libsamplerate *u; +static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {      unsigned n_samples; +    void *src, *dst; -    assert(r); -    assert(input); -    assert(r->impl_data); -    u = r->impl_data; +    pa_assert(r); +    pa_assert(input); +    pa_assert(input->memblock); -    /* Convert the incoming sample into floats and place them in buf1 */ +    /* Convert the incoming sample into the work sample format and place them in buf1 */ -    if (!u->to_float32ne_func) +    if (!r->to_work_format_func || !input->length)          return input; -    n_samples = n_frames * r->i_ss.channels; +    n_samples = (input->length / r->i_fz) * r->i_ss.channels; -    if (u->buf1_samples < n_samples) { -        if (u->buf1_block) -            pa_memblock_unref(u->buf1_block); +    r->buf1.index = 0; +    r->buf1.length = r->w_sz * n_samples; -        u->buf1_samples = n_samples; -        u->buf1_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); -        u->buf1 = u->buf1_block->data; +    if (!r->buf1.memblock || r->buf1_samples < n_samples) { +        if (r->buf1.memblock) +            pa_memblock_unref(r->buf1.memblock); + +        r->buf1_samples = n_samples; +        r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length);      } -    u->to_float32ne_func(n_samples, input, u->buf1); +    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; +    dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock); + +    r->to_work_format_func(n_samples, src, dst); + +    pa_memblock_release(input->memblock); +    pa_memblock_release(r->buf1.memblock); -    return u->buf1; +    return &r->buf1;  } -static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) { -    struct impl_libsamplerate *u; -    unsigned n_samples; +static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) { +    unsigned in_n_samples, out_n_samples, n_frames;      int i_skip, o_skip;      unsigned oc; +    void *src, *dst; -    assert(r); -    assert(input); -    assert(r->impl_data); -    u = r->impl_data; +    pa_assert(r); +    pa_assert(input); +    pa_assert(input->memblock);      /* Remap channels and place the result int buf2 */ -    if (!u->map_required) +    if (!r->map_required || !input->length)          return input; -    n_samples = n_frames * r->o_ss.channels; +    in_n_samples = input->length / r->w_sz; +    n_frames = in_n_samples / r->i_ss.channels; +    out_n_samples = n_frames * r->o_ss.channels; + +    r->buf2.index = 0; +    r->buf2.length = r->w_sz * out_n_samples; -    if (u->buf2_samples < n_samples) { -        if (u->buf2_block) -            pa_memblock_unref(u->buf2_block); +    if (!r->buf2.memblock || r->buf2_samples < out_n_samples) { +        if (r->buf2.memblock) +            pa_memblock_unref(r->buf2.memblock); -        u->buf2_samples = n_samples; -        u->buf2_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); -        u->buf2 = u->buf2_block->data; +        r->buf2_samples = out_n_samples; +        r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length);      } -    memset(u->buf2, 0, n_samples * sizeof(float)); +    src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); +    dst = pa_memblock_acquire(r->buf2.memblock); -    o_skip = sizeof(float) * r->o_ss.channels; -    i_skip = sizeof(float) * r->i_ss.channels; +    memset(dst, 0, r->buf2.length); -    for (oc = 0; oc < r->o_ss.channels; oc++) { -        unsigned i; -        static const float one = 1.0; - -        for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++) -            oil_vectoradd_f32( -                u->buf2 + oc, o_skip, -                u->buf2 + oc, o_skip, -                input + u->map_table[oc][i], i_skip, -                n_frames, -                &one, &one); +    o_skip = r->w_sz * r->o_ss.channels; +    i_skip = r->w_sz * r->i_ss.channels; + +    switch (r->work_format) { +        case PA_SAMPLE_FLOAT32NE: + +            for (oc = 0; oc < r->o_ss.channels; oc++) { +                unsigned i; +                static const float one = 1.0; + +                for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++) +                    oil_vectoradd_f32( +                            (float*) dst + oc, o_skip, +                            (float*) dst + oc, o_skip, +                            (float*) src + r->map_table[oc][i], i_skip, +                            n_frames, +                            &one, &one); +            } + +            break; + +        case PA_SAMPLE_S16NE: + +            for (oc = 0; oc < r->o_ss.channels; oc++) { +                unsigned i; +                static const int16_t one = 1; + +                for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++) +                    oil_vectoradd_s16( +                            (int16_t*) dst + oc, o_skip, +                            (int16_t*) dst + oc, o_skip, +                            (int16_t*) src + r->map_table[oc][i], i_skip, +                            n_frames, +                            &one, &one); +            } + +            break; + +        default: +            pa_assert_not_reached();      } -    return u->buf2; +    pa_memblock_release(input->memblock); +    pa_memblock_release(r->buf2.memblock); + +    r->buf2.length = out_n_samples * r->w_sz; + +    return &r->buf2;  } -static float *resample(pa_resampler *r, float *input, unsigned *n_frames) { -    struct impl_libsamplerate *u; -    SRC_DATA data; +static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) { +    unsigned in_n_frames, in_n_samples;      unsigned out_n_frames, out_n_samples; -    int ret; -    assert(r); -    assert(input); -    assert(n_frames); -    assert(r->impl_data); -    u = r->impl_data; +    pa_assert(r); +    pa_assert(input);      /* Resample the data and place the result in buf3 */ -    if (!u->src_state) +    if (!r->impl_resample || !input->length)          return input; -    out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024; +    in_n_samples = input->length / r->w_sz; +    in_n_frames = in_n_samples / r->o_ss.channels; + +    out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_SAMPLES;      out_n_samples = out_n_frames * r->o_ss.channels; -    if (u->buf3_samples < out_n_samples) { -        if (u->buf3_block) -            pa_memblock_unref(u->buf3_block); +    r->buf3.index = 0; +    r->buf3.length = r->w_sz * out_n_samples; + +    if (!r->buf3.memblock || r->buf3_samples < out_n_samples) { +        if (r->buf3.memblock) +            pa_memblock_unref(r->buf3.memblock); -        u->buf3_samples = out_n_samples; -        u->buf3_block = pa_memblock_new(r->mempool, sizeof(float) * out_n_samples); -        u->buf3 = u->buf3_block->data; +        r->buf3_samples = out_n_samples; +        r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length);      } -    data.data_in = input; -    data.input_frames = *n_frames; +    r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames); +    r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels; -    data.data_out = u->buf3; -    data.output_frames = out_n_frames; +    return &r->buf3; +} -    data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; -    data.end_of_input = 0; +static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) { +    unsigned n_samples, n_frames; +    void *src, *dst; + +    pa_assert(r); +    pa_assert(input); + +    /* Convert the data into the correct sample type and place the result in buf4 */ + +    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; + +    r->buf4.index = 0; +    r->buf4.length = r->o_fz * n_frames; + +    if (!r->buf4.memblock || r->buf4_samples < n_samples) { +        if (r->buf4.memblock) +            pa_memblock_unref(r->buf4.memblock); + +        r->buf4_samples = n_samples; +        r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length); +    } -    ret = src_process(u->src_state, &data); -    assert(ret == 0); -    assert((unsigned) data.input_frames_used == *n_frames); +    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; +    dst = pa_memblock_acquire(r->buf4.memblock); +    r->from_work_format_func(n_samples, src, dst); +    pa_memblock_release(input->memblock); +    pa_memblock_release(r->buf4.memblock); -    *n_frames = data.output_frames_gen; +    r->buf4.length = r->o_fz * n_frames; -    return u->buf3; +    return &r->buf4;  } -static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) { -    struct impl_libsamplerate *u; -    unsigned n_samples; +void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { +    pa_memchunk *buf; + +    pa_assert(r); +    pa_assert(in); +    pa_assert(out); +    pa_assert(in->length); +    pa_assert(in->memblock); +    pa_assert(in->length % r->i_fz == 0); + +    buf = (pa_memchunk*) in; +    buf = convert_to_work_format(r, buf); +    buf = remap_channels(r, buf); +    buf = resample(r, buf); + +    if (buf->length) { +        buf = convert_from_work_format(r, buf); +        *out = *buf; + +        if (buf == in) +            pa_memblock_ref(buf->memblock); +        else +            pa_memchunk_reset(buf); +    } else +        pa_memchunk_reset(out); +} -    assert(r); -    assert(input); -    assert(r->impl_data); -    u = r->impl_data; +/*** libsamplerate based implementation ***/ -    /* Convert the data into the correct sample type and place the result in buf4 */ +#ifdef HAVE_LIBSAMPLERATE +static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +    SRC_DATA data; -    if (!u->from_float32ne_func) -        return input; +    pa_assert(r); +    pa_assert(input); +    pa_assert(output); +    pa_assert(out_n_frames); -    n_samples = n_frames * r->o_ss.channels; +    memset(&data, 0, sizeof(data)); -    if (u->buf4_samples < n_samples) { -        if (u->buf4_block) -            pa_memblock_unref(u->buf4_block); +    data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); +    data.input_frames = in_n_frames; -        u->buf4_samples = n_samples; -        u->buf4_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); -        u->buf4 = u->buf4_block->data; -    } +    data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); +    data.output_frames = *out_n_frames; -    u->from_float32ne_func(n_samples, input, u->buf4); - -    return u->buf4; -} - -static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { -    struct impl_libsamplerate *u; -    float *buf; -    void *input, *output; -    unsigned n_frames; - -    assert(r); -    assert(in); -    assert(out); -    assert(in->length); -    assert(in->memblock); -    assert(in->length % r->i_fz == 0); -    assert(r->impl_data); - -    u = r->impl_data; - -    input = ((uint8_t*) in->memblock->data + in->index); -    n_frames = in->length / r->i_fz; -    assert(n_frames > 0); - -    buf = convert_to_float(r, input, n_frames); -    buf = remap_channels(r, buf, n_frames); -    buf = resample(r, buf, &n_frames); - -    if (n_frames) { -        output = convert_from_float(r, buf, n_frames); - -        if (output == input) { -            /* Mm, no adjustment has been necessary, so let's return the original block */ -            out->memblock = pa_memblock_ref(in->memblock); -            out->index = in->index; -            out->length = in->length; -        } else { -            out->length = n_frames * r->o_fz; -            out->index = 0; -            out->memblock = NULL; - -            if (output == u->buf1) { -                u->buf1 = NULL; -                u->buf1_samples = 0; -                out->memblock = u->buf1_block; -                u->buf1_block = NULL; -            } else if (output == u->buf2) { -                u->buf2 = NULL; -                u->buf2_samples = 0; -                out->memblock = u->buf2_block; -                u->buf2_block = NULL; -            } else if (output == u->buf3) { -                u->buf3 = NULL; -                u->buf3_samples = 0; -                out->memblock = u->buf3_block; -                u->buf3_block = NULL; -            } else if (output == u->buf4) { -                u->buf4 = NULL; -                u->buf4_samples = 0; -                out->memblock = u->buf4_block; -                u->buf4_block = NULL; -            } +    data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; +    data.end_of_input = 0; -            assert(out->memblock); -        } -    } else { -        out->memblock = NULL; -        out->index = out->length = 0; -    } +    pa_assert_se(src_process(r->src.state, &data) == 0); +    pa_assert((unsigned) data.input_frames_used == in_n_frames); + +    pa_memblock_release(input->memblock); +    pa_memblock_release(output->memblock); + +    *out_n_frames = data.output_frames_gen;  } -static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) { -    struct impl_libsamplerate *u; +static void libsamplerate_update_rates(pa_resampler *r) { +    pa_assert(r); -    assert(r); -    assert(rate > 0); -    assert(r->impl_data); -    u = r->impl_data; +    pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0); +} -    if (!u->src_state) { -        int err; -        u->src_state = src_new(r->resample_method, r->o_ss.channels, &err); -        assert(u->src_state); -    } else { -        int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate); -        assert(ret == 0); -    } +static void libsamplerate_free(pa_resampler *r) { +    pa_assert(r); + +    if (r->src.state) +        src_delete(r->src.state);  }  static int libsamplerate_init(pa_resampler *r) { -    struct impl_libsamplerate *u = NULL;      int err; -    r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1); +    pa_assert(r); -    u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL; -    u->buf1_block = u->buf2_block = u->buf3_block = u->buf4_block = NULL; -    u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0; +    if (!(r->src.state = src_new(r->resample_method, r->o_ss.channels, &err))) +        return -1; -    if (r->i_ss.format == PA_SAMPLE_FLOAT32NE) -        u->to_float32ne_func = NULL; -    else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) -        goto fail; +    r->impl_free = libsamplerate_free; +    r->impl_update_rates = libsamplerate_update_rates; +    r->impl_resample = libsamplerate_resample; -    if (r->o_ss.format == PA_SAMPLE_FLOAT32NE) -        u->from_float32ne_func = NULL; -    else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) -        goto fail; +    return 0; +} +#endif -    if (r->o_ss.rate == r->i_ss.rate) -        u->src_state = NULL; -    else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err))) -        goto fail; +/*** speex based implementation ***/ -    r->impl_free = libsamplerate_free; -    r->impl_update_input_rate = libsamplerate_update_input_rate; -    r->impl_run = libsamplerate_run; +static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +    float *in, *out; +    uint32_t inf = in_n_frames, outf = *out_n_frames; -    calc_map_table(r); +    pa_assert(r); +    pa_assert(input); +    pa_assert(output); +    pa_assert(out_n_frames); -    return 0; +    in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); +    out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); -fail: -    pa_xfree(u); -    return -1; +    pa_assert_se(paspfl_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0); + +    pa_memblock_release(input->memblock); +    pa_memblock_release(output->memblock); + +    pa_assert(inf == in_n_frames); +    *out_n_frames = outf;  } -/* Trivial implementation */ +static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +    int16_t *in, *out; +    uint32_t inf = in_n_frames, outf = *out_n_frames; -static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { -    size_t fz; -    unsigned  n_frames; -    struct impl_trivial *u; +    pa_assert(r); +    pa_assert(input); +    pa_assert(output); +    pa_assert(out_n_frames); + +    in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); +    out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); + +    pa_assert_se(paspfx_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0); + +    pa_memblock_release(input->memblock); +    pa_memblock_release(output->memblock); -    assert(r); -    assert(in); -    assert(out); -    assert(r->impl_data); +    pa_assert(inf == in_n_frames); +    *out_n_frames = outf; +} -    u = r->impl_data; +static void speex_update_rates(pa_resampler *r) { +    pa_assert(r); -    fz = r->i_fz; -    assert(fz == r->o_fz); +    if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) +        pa_assert_se(paspfx_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0); +    else { +        pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX); +        pa_assert_se(paspfl_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0); +    } +} -    n_frames = in->length/fz; +static void speex_free(pa_resampler *r) { +    pa_assert(r); -    if (r->i_ss.rate == r->o_ss.rate) { +    if (!r->speex.state) +        return; + +    if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) +        paspfx_resampler_destroy(r->speex.state); +    else { +        pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX); +        paspfl_resampler_destroy(r->speex.state); +    } +} -        /* In case there's no diefference in sample types, do nothing */ -        *out = *in; -        pa_memblock_ref(out->memblock); +static int speex_init(pa_resampler *r) { +    int q, err; -        u->o_counter += n_frames; +    pa_assert(r); + +    r->impl_free = speex_free; +    r->impl_update_rates = speex_update_rates; + +    if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) { +        q = r->resample_method - PA_RESAMPLER_SPEEX_FIXED_BASE; + +        pa_log_info("Choosing speex quality setting %i.", q); + +        if (!(r->speex.state = paspfx_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err))) +            return -1; + +        r->impl_resample = speex_resample_int;      } else { -        /* Do real resampling */ -        size_t l; -        unsigned o_index; +        pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX); +        q = r->resample_method - PA_RESAMPLER_SPEEX_FLOAT_BASE; + +        pa_log_info("Choosing speex quality setting %i.", q); -        /* The length of the new memory block rounded up */ -        l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz; +        if (!(r->speex.state = paspfl_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err))) +            return -1; -        out->index = 0; -        out->memblock = pa_memblock_new(r->mempool, l); +        r->impl_resample = speex_resample_float; +    } -        for (o_index = 0;; o_index++, u->o_counter++) { -            unsigned j; +    return 0; +} -            j = (u->o_counter * r->i_ss.rate / r->o_ss.rate); -            j = j > u->i_counter ? j - u->i_counter : 0; +/* Trivial implementation */ -            if (j >= n_frames) -                break; +static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +    size_t fz; +    unsigned o_index; +    void *src, *dst; -            assert(o_index*fz < out->memblock->length); +    pa_assert(r); +    pa_assert(input); +    pa_assert(output); +    pa_assert(out_n_frames); -            memcpy((uint8_t*) out->memblock->data + fz*o_index, -                   (uint8_t*) in->memblock->data + in->index + fz*j, fz); +    fz = r->w_sz * r->o_ss.channels; -        } +    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; +    dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index; + +    for (o_index = 0;; o_index++, r->trivial.o_counter++) { +        unsigned j; -        out->length = o_index*fz; +        j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate); +        j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0; + +        if (j >= in_n_frames) +            break; + +        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);      } -    u->i_counter += n_frames; +    pa_memblock_release(input->memblock); +    pa_memblock_release(output->memblock); + +    *out_n_frames = o_index; + +    r->trivial.i_counter += in_n_frames;      /* Normalize counters */ -    while (u->i_counter >= r->i_ss.rate) { -        u->i_counter -= r->i_ss.rate; -        assert(u->o_counter >= r->o_ss.rate); -        u->o_counter -= r->o_ss.rate; +    while (r->trivial.i_counter >= r->i_ss.rate) { +        pa_assert(r->trivial.o_counter >= r->o_ss.rate); + +        r->trivial.i_counter -= r->i_ss.rate; +        r->trivial.o_counter -= r->o_ss.rate;      }  } -static void trivial_free(pa_resampler *r) { -    assert(r); +static void trivial_update_rates(pa_resampler *r) { +    pa_assert(r); -    pa_xfree(r->impl_data); +    r->trivial.i_counter = 0; +    r->trivial.o_counter = 0;  } -static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) { -    struct impl_trivial *u; +static int trivial_init(pa_resampler*r) { +    pa_assert(r); + +    r->trivial.o_counter = r->trivial.i_counter = 0; -    assert(r); -    assert(rate > 0); -    assert(r->impl_data); +    r->impl_resample = trivial_resample; +    r->impl_update_rates = trivial_update_rates; +    r->impl_free = NULL; -    u = r->impl_data; -    u->i_counter = 0; -    u->o_counter = 0; +    return 0;  } -static int trivial_init(pa_resampler*r) { -    struct impl_trivial *u; +/*** ffmpeg based implementation ***/ + +static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +    unsigned used_frames = 0, c; + +    pa_assert(r); +    pa_assert(input); +    pa_assert(output); +    pa_assert(out_n_frames); + +    for (c = 0; c < r->o_ss.channels; c++) { +        unsigned u; +        pa_memblock *b, *w; +        int16_t *p, *t, *k, *q, *s; +        int consumed_frames; +        unsigned in, l; + +        /* Allocate a new block */ +        b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t)); +        p = pa_memblock_acquire(b); + +        /* Copy the remaining data into it */ +        l = 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); +            pa_memblock_release(r->ffmpeg.buf[c].memblock); +            pa_memblock_unref(r->ffmpeg.buf[c].memblock); +            pa_memchunk_reset(&r->ffmpeg.buf[c]); +        } + +        /* Now append the new data, splitting up channels */ +        t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c; +        k = (int16_t*) ((uint8_t*) p + l); +        for (u = 0; u < in_n_frames; u++) { +            *k = *t; +            t += r->o_ss.channels; +            k ++; +        } +        pa_memblock_release(input->memblock); + +        /* Calculate the resulting number of frames */ +        in = in_n_frames + l / 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); + +        pa_memblock_release(b); + +        /* Now store the remaining samples away */ +        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); +        } else +            pa_memblock_unref(b); + +        /* And place the results in the output buffer */ +        s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c; +        for (u = 0; u < used_frames; u++) { +            *s = *q; +            q++; +            s += r->o_ss.channels; +        } +        pa_memblock_release(output->memblock); +        pa_memblock_release(w); +        pa_memblock_unref(w); +    } + +    *out_n_frames = used_frames; +} + +static void ffmpeg_free(pa_resampler *r) { +    unsigned c; -    assert(r); -    assert(r->i_ss.format == r->o_ss.format); -    assert(r->i_ss.channels == r->o_ss.channels); +    pa_assert(r); -    r->impl_data = u = pa_xnew(struct impl_trivial, 1); -    u->o_counter = u->i_counter = 0; +    if (r->ffmpeg.state) +        av_resample_close(r->ffmpeg.state); -    r->impl_run = trivial_run; -    r->impl_free = trivial_free; -    r->impl_update_input_rate = trivial_update_input_rate; +    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++) +        if (r->ffmpeg.buf[c].memblock) +            pa_memblock_unref(r->ffmpeg.buf[c].memblock); +} + +static int ffmpeg_init(pa_resampler *r) { +    unsigned c; + +    pa_assert(r); + +    /* We could probably implement different quality levels by +     * adjusting the filter parameters here. However, ffmpeg +     * 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))) +        return -1; + +    r->impl_free = ffmpeg_free; +    r->impl_resample = ffmpeg_resample; + +    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++) +        pa_memchunk_reset(&r->ffmpeg.buf[c]);      return 0;  } +/*** copy (noop) implementation ***/ +static int copy_init(pa_resampler *r) { +    pa_assert(r); + +    pa_assert(r->o_ss.rate == r->i_ss.rate); + +    r->impl_free = NULL; +    r->impl_resample = NULL; +    r->impl_update_rates = NULL; + +    return 0; +} diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index c283593d..23e1acb7 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -24,8 +24,6 @@    USA.  ***/ -#include <samplerate.h> -  #include <pulse/sample.h>  #include <pulse/channelmap.h>  #include <pulsecore/memblock.h> @@ -35,12 +33,19 @@ typedef struct pa_resampler pa_resampler;  typedef enum pa_resample_method {      PA_RESAMPLER_INVALID                 = -1, -    PA_RESAMPLER_SRC_SINC_BEST_QUALITY   = SRC_SINC_BEST_QUALITY, -    PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY, -    PA_RESAMPLER_SRC_SINC_FASTEST        = SRC_SINC_FASTEST, -    PA_RESAMPLER_SRC_ZERO_ORDER_HOLD     = SRC_ZERO_ORDER_HOLD, -    PA_RESAMPLER_SRC_LINEAR              = SRC_LINEAR, +    PA_RESAMPLER_SRC_SINC_BEST_QUALITY   = 0, /* = SRC_SINC_BEST_QUALITY */ +    PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = 1, /* = SRC_SINC_MEDIUM_QUALITY */ +    PA_RESAMPLER_SRC_SINC_FASTEST        = 2, /* = SRC_SINC_FASTEST */ +    PA_RESAMPLER_SRC_ZERO_ORDER_HOLD     = 3, /* = SRC_ZERO_ORDER_HOLD */ +    PA_RESAMPLER_SRC_LINEAR              = 4, /* = SRC_LINEAR */      PA_RESAMPLER_TRIVIAL, +    PA_RESAMPLER_SPEEX_FLOAT_BASE, +    PA_RESAMPLER_SPEEX_FLOAT_MAX = PA_RESAMPLER_SPEEX_FLOAT_BASE + 10, +    PA_RESAMPLER_SPEEX_FIXED_BASE, +    PA_RESAMPLER_SPEEX_FIXED_MAX = PA_RESAMPLER_SPEEX_FIXED_BASE + 10, +    PA_RESAMPLER_FFMPEG, +    PA_RESAMPLER_AUTO, /* automatic select based on sample format */ +    PA_RESAMPLER_COPY,      PA_RESAMPLER_MAX  } pa_resample_method_t; @@ -50,19 +55,26 @@ pa_resampler* pa_resampler_new(          const pa_channel_map *am,          const pa_sample_spec *b,          const pa_channel_map *bm, -        pa_resample_method_t resample_method); +        pa_resample_method_t resample_method, +        int variable_rate);  void pa_resampler_free(pa_resampler *r);  /* Returns the size of an input memory block which is required to return the specified amount of output data */  size_t pa_resampler_request(pa_resampler *r, size_t out_length); +/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */ +size_t pa_resampler_max_block_size(pa_resampler *r); +  /* Pass the specified memory chunk to the resampler and return the newly resampled data */  void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);  /* Change the input rate of the resampler object */  void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate); +/* Change the output rate of the resampler object */ +void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate); +  /* Return the resampling method of the resampler object */  pa_resample_method_t pa_resampler_get_method(pa_resampler *r); @@ -72,4 +84,7 @@ pa_resample_method_t pa_parse_resample_method(const char *string);  /* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */  const char *pa_resample_method_to_string(pa_resample_method_t m); +/* Return 1 when the specified resampling method is supported */ +int pa_resample_method_supported(pa_resample_method_t m); +  #endif diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c new file mode 100644 index 00000000..07d776e4 --- /dev/null +++ b/src/pulsecore/rtclock.c @@ -0,0 +1,98 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <stddef.h> +#include <time.h> +#include <sys/time.h> + +#include <pulse/timeval.h> +#include <pulsecore/macro.h> + +#include "rtclock.h" + +pa_usec_t pa_rtclock_age(const struct timeval *tv) { +    struct timeval now; +    pa_assert(tv); + +    return pa_timeval_diff(pa_rtclock_get(&now), tv); +} + +struct timeval *pa_rtclock_get(struct timeval *tv) { +#ifdef HAVE_CLOCK_GETTIME +    struct timespec ts; + +#ifdef CLOCK_MONOTONIC +    /* No locking or atomic ops for no_monotonic here */ +    static pa_bool_t no_monotonic = FALSE; + +    if (!no_monotonic) +        if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) +            no_monotonic = TRUE; + +    if (no_monotonic) +#endif +        pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + +    pa_assert(tv); + +    tv->tv_sec = ts.tv_sec; +    tv->tv_usec = ts.tv_nsec / 1000; + +    return tv; + +#else /* HAVE_CLOCK_GETTIME */ + +    return pa_gettimeofday(tv); + +#endif +} + +pa_bool_t pa_rtclock_hrtimer(void) { +#ifdef HAVE_CLOCK_GETTIME +    struct timespec ts; + +#ifdef CLOCK_MONOTONIC +    if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) +        return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; +#endif + +    pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); +    return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; + +#else /* HAVE_CLOCK_GETTIME */ + +    return FALSE; + +#endif +} + +pa_usec_t pa_rtclock_usec(void) { +    struct timeval tv; + +    return pa_timeval_load(pa_rtclock_get(&tv)); +} diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h new file mode 100644 index 00000000..f0360af3 --- /dev/null +++ b/src/pulsecore/rtclock.h @@ -0,0 +1,43 @@ +#ifndef foopulsertclockhfoo +#define foopulsertclockhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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> + +struct timeval; + +/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */ + +struct timeval *pa_rtclock_get(struct timeval *ts); + +pa_usec_t pa_rtclock_usec(void); + +pa_usec_t pa_rtclock_age(const struct timeval *tv); +pa_bool_t pa_rtclock_hrtimer(void); + +/* timer with a resolution better than this are considered high-resolution */ +#define PA_HRTIMER_THRESHOLD_USEC 10 + +#endif diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c new file mode 100644 index 00000000..354c4c0e --- /dev/null +++ b/src/pulsecore/rtpoll.c @@ -0,0 +1,751 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <sys/types.h> +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <errno.h> + +#ifdef __linux__ +#include <sys/utsname.h> +#endif + +#ifdef HAVE_POLL_H +#include <poll.h> +#else +#include <pulsecore/poll.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/macro.h> +#include <pulsecore/llist.h> +#include <pulsecore/rtsig.h> +#include <pulsecore/flist.h> +#include <pulsecore/core-util.h> + +#include <pulsecore/winsock.h> + +#include "rtpoll.h" + +struct pa_rtpoll { +    struct pollfd *pollfd, *pollfd2; +    unsigned n_pollfd_alloc, n_pollfd_used; + +    pa_bool_t timer_enabled; +    struct timeval next_elapse; +    pa_usec_t period; + +    pa_bool_t scan_for_dead; +    pa_bool_t running, installed, rebuild_needed, quit; + +#ifdef HAVE_PPOLL +    int rtsig; +    sigset_t sigset_unblocked; +    timer_t timer; +#ifdef __linux__ +    pa_bool_t dont_use_ppoll; +#endif +#endif + +    PA_LLIST_HEAD(pa_rtpoll_item, items); +}; + +struct pa_rtpoll_item { +    pa_rtpoll *rtpoll; +    pa_bool_t dead; + +    pa_rtpoll_priority_t priority; + +    struct pollfd *pollfd; +    unsigned n_pollfd; + +    int (*work_cb)(pa_rtpoll_item *i); +    int (*before_cb)(pa_rtpoll_item *i); +    void (*after_cb)(pa_rtpoll_item *i); +    void *userdata; + +    PA_LLIST_FIELDS(pa_rtpoll_item); +}; + +PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); + +static void signal_handler_noop(int s) { } + +pa_rtpoll *pa_rtpoll_new(void) { +    pa_rtpoll *p; + +    p = pa_xnew(pa_rtpoll, 1); + +#ifdef HAVE_PPOLL + +#ifdef __linux__ +    /* ppoll is broken on Linux < 2.6.16 */ +    p->dont_use_ppoll = FALSE; + +    { +        struct utsname u; +        unsigned major, minor, micro; + +        pa_assert_se(uname(&u) == 0); + +        if (sscanf(u.release, "%u.%u.%u", &major, &minor, µ) != 3 || +            (major < 2) || +            (major == 2 && minor < 6) || +            (major == 2 && minor == 6 && micro < 16)) + +            p->dont_use_ppoll = TRUE; +    } + +#endif + +    p->rtsig = -1; +    sigemptyset(&p->sigset_unblocked); +    p->timer = (timer_t) -1; + +#endif + +    p->n_pollfd_alloc = 32; +    p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc); +    p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc); +    p->n_pollfd_used = 0; + +    p->period = 0; +    memset(&p->next_elapse, 0, sizeof(p->next_elapse)); +    p->timer_enabled = FALSE; + +    p->running = FALSE; +    p->installed = FALSE; +    p->scan_for_dead = FALSE; +    p->rebuild_needed = FALSE; +    p->quit = FALSE; + +    PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items); + +    return p; +} + +void pa_rtpoll_install(pa_rtpoll *p) { +    pa_assert(p); +    pa_assert(!p->installed); + +    p->installed = 1; + +#ifdef HAVE_PPOLL +    if (p->dont_use_ppoll) +        return; + +    if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) { +        pa_log_warn("Failed to reserve POSIX realtime signal."); +        return; +    } + +    pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig)); + +    { +        sigset_t ss; +        struct sigaction sa; + +        pa_assert_se(sigemptyset(&ss) == 0); +        pa_assert_se(sigaddset(&ss, p->rtsig) == 0); +        pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0); +        pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0); + +        memset(&sa, 0, sizeof(sa)); +        sa.sa_handler = signal_handler_noop; +        pa_assert_se(sigemptyset(&sa.sa_mask) == 0); + +        pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0); + +        /* We never reset the signal handler. Why should we? */ +    } + +#endif +} + +static void rtpoll_rebuild(pa_rtpoll *p) { + +    struct pollfd *e, *t; +    pa_rtpoll_item *i; +    int ra = 0; + +    pa_assert(p); + +    p->rebuild_needed = FALSE; + +    if (p->n_pollfd_used > p->n_pollfd_alloc) { +        /* Hmm, we have to allocate some more space */ +        p->n_pollfd_alloc = p->n_pollfd_used * 2; +        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd)); +        ra = 1; +    } + +    e = p->pollfd2; + +    for (i = p->items; i; i = i->next) { + +        if (i->n_pollfd > 0)  { +            size_t l = i->n_pollfd * sizeof(struct pollfd); + +            if (i->pollfd) +                memcpy(e, i->pollfd, l); +            else +                memset(e, 0, l); + +            i->pollfd = e; +        } else +            i->pollfd = NULL; + +        e += i->n_pollfd; +    } + +    pa_assert((unsigned) (e - p->pollfd2) == p->n_pollfd_used); +    t = p->pollfd; +    p->pollfd = p->pollfd2; +    p->pollfd2 = t; + +    if (ra) +        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd)); + +} + +static void rtpoll_item_destroy(pa_rtpoll_item *i) { +    pa_rtpoll *p; + +    pa_assert(i); + +    p = i->rtpoll; + +    PA_LLIST_REMOVE(pa_rtpoll_item, p->items, i); + +    p->n_pollfd_used -= i->n_pollfd; + +    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0) +        pa_xfree(i); + +    p->rebuild_needed = TRUE; +} + +void pa_rtpoll_free(pa_rtpoll *p) { +    pa_assert(p); + +    while (p->items) +        rtpoll_item_destroy(p->items); + +    pa_xfree(p->pollfd); +    pa_xfree(p->pollfd2); + +#ifdef HAVE_PPOLL +    if (p->timer != (timer_t) -1) +        timer_delete(p->timer); +#endif + +    pa_xfree(p); +} + +static void reset_revents(pa_rtpoll_item *i) { +    struct pollfd *f; +    unsigned n; + +    pa_assert(i); + +    if (!(f = pa_rtpoll_item_get_pollfd(i, &n))) +        return; + +    for (; n > 0; n--) +        f[n-1].revents = 0; +} + +static void reset_all_revents(pa_rtpoll *p) { +    pa_rtpoll_item *i; + +    pa_assert(p); + +    for (i = p->items; i; i = i->next) { + +        if (i->dead) +            continue; + +        reset_revents(i); +    } +} + +int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { +    pa_rtpoll_item *i; +    int r = 0; +    struct timeval timeout; + +    pa_assert(p); +    pa_assert(!p->running); +    pa_assert(p->installed); + +    p->running = TRUE; + +    /* First, let's do some work */ +    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) { +        int k; + +        if (i->dead) +            continue; + +        if (!i->work_cb) +            continue; + +        if (p->quit) +            goto finish; + +        if ((k = i->work_cb(i)) != 0) { +            if (k < 0) +                r = k; + +            goto finish; +        } +    } + +    /* Now let's prepare for entering the sleep */ +    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) { +        int k = 0; + +        if (i->dead) +            continue; + +        if (!i->before_cb) +            continue; + +        if (p->quit || (k = i->before_cb(i)) != 0) { + +            /* Hmm, this one doesn't let us enter the poll, so rewind everything */ + +            for (i = i->prev; i; i = i->prev) { + +                if (i->dead) +                    continue; + +                if (!i->after_cb) +                    continue; + +                i->after_cb(i); +            } + +            if (k < 0) +                r = k; + +            goto finish; +        } +    } + +    if (p->rebuild_needed) +        rtpoll_rebuild(p); + +    /* Calculate timeout */ +    if (!wait || p->quit) { +        timeout.tv_sec = 0; +        timeout.tv_usec = 0; +    } else if (p->timer_enabled) { +        struct timeval now; +        pa_rtclock_get(&now); + +        memset(&timeout, 0, sizeof(timeout)); +        if (pa_timeval_cmp(&p->next_elapse, &now) > 0) +            pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now)); +    } + +    /* OK, now let's sleep */ +#ifdef HAVE_PPOLL + +#ifdef __linux__ +    if (!p->dont_use_ppoll) +#endif +    { +        struct timespec ts; +        ts.tv_sec = timeout.tv_sec; +        ts.tv_nsec = timeout.tv_usec * 1000; +        r = ppoll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked); +    } +#ifdef __linux__ +    else +#endif + +#endif +        r = poll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1); + +    if (r < 0) { +        if (errno == EAGAIN || errno == EINTR) +            r = 0; +        else +            pa_log_error("poll(): %s", pa_cstrerror(errno)); + +        reset_all_revents(p); +    } + +    if (p->timer_enabled) { +        if (p->period > 0) { +            struct timeval now; +            pa_rtclock_get(&now); + +            pa_timeval_add(&p->next_elapse, p->period); + +            /* Guarantee that the next timeout will happen in the future */ +            if (pa_timeval_cmp(&p->next_elapse, &now) < 0) +                pa_timeval_add(&p->next_elapse, (pa_timeval_diff(&now, &p->next_elapse) / p->period + 1) * p->period); + +        } else +            p->timer_enabled = FALSE; +    } + +    /* Let's tell everyone that we left the sleep */ +    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) { + +        if (i->dead) +            continue; + +        if (!i->after_cb) +            continue; + +        i->after_cb(i); +    } + +finish: + +    p->running = FALSE; + +    if (p->scan_for_dead) { +        pa_rtpoll_item *n; + +        p->scan_for_dead = FALSE; + +        for (i = p->items; i; i = n) { +            n = i->next; + +            if (i->dead) +                rtpoll_item_destroy(i); +        } +    } + +    return r < 0 ? r : !p->quit; +} + +static void update_timer(pa_rtpoll *p) { +    pa_assert(p); + +#ifdef HAVE_PPOLL + +#ifdef __linux__ +    if (!p->dont_use_ppoll) { +#endif + +        if (p->timer == (timer_t) -1) { +            struct sigevent se; + +            memset(&se, 0, sizeof(se)); +            se.sigev_notify = SIGEV_SIGNAL; +            se.sigev_signo = p->rtsig; + +            if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0) +                if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) { +                    pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno)); +                    p->timer = (timer_t) -1; +                } +        } + +        if (p->timer != (timer_t) -1) { +            struct itimerspec its; +            memset(&its, 0, sizeof(its)); + +            if (p->timer_enabled) { +                its.it_value.tv_sec = p->next_elapse.tv_sec; +                its.it_value.tv_nsec = p->next_elapse.tv_usec*1000; + +                /* Make sure that 0,0 is not understood as +                 * "disarming" */ +                if (its.it_value.tv_sec == 0) +                    its.it_value.tv_nsec = 1; + +                if (p->period > 0) { +                    struct timeval tv; +                    pa_timeval_store(&tv, p->period); +                    its.it_interval.tv_sec = tv.tv_sec; +                    its.it_interval.tv_nsec = tv.tv_usec*1000; +                } +            } + +            pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); +        } + +#ifdef __linux__ +    } +#endif + +#endif +} + +void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) { +    pa_assert(p); +    pa_assert(ts); + +    p->next_elapse = *ts; +    p->period = 0; +    p->timer_enabled = TRUE; + +    update_timer(p); +} + +void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) { +    pa_assert(p); + +    p->period = usec; +    pa_rtclock_get(&p->next_elapse); +    pa_timeval_add(&p->next_elapse, usec); +    p->timer_enabled = TRUE; + +    update_timer(p); +} + +void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { +    pa_assert(p); + +    p->period = 0; +    pa_rtclock_get(&p->next_elapse); +    pa_timeval_add(&p->next_elapse, usec); +    p->timer_enabled = TRUE; + +    update_timer(p); +} + +void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) { +    pa_assert(p); + +    p->period = 0; +    memset(&p->next_elapse, 0, sizeof(p->next_elapse)); +    p->timer_enabled = FALSE; + +    update_timer(p); +} + +pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) { +    pa_rtpoll_item *i, *j, *l = NULL; + +    pa_assert(p); + +    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +        i = pa_xnew(pa_rtpoll_item, 1); + +    i->rtpoll = p; +    i->dead = FALSE; +    i->n_pollfd = n_fds; +    i->pollfd = NULL; +    i->priority = prio; + +    i->userdata = NULL; +    i->before_cb = NULL; +    i->after_cb = NULL; +    i->work_cb = NULL; + +    for (j = p->items; j; j = j->next) { +        if (prio <= j->priority) +            break; + +        l = j; +    } + +    PA_LLIST_INSERT_AFTER(pa_rtpoll_item, p->items, j ? j->prev : l, i); + +    if (n_fds > 0) { +        p->rebuild_needed = 1; +        p->n_pollfd_used += n_fds; +    } + +    return i; +} + +void pa_rtpoll_item_free(pa_rtpoll_item *i) { +    pa_assert(i); + +    if (i->rtpoll->running) { +        i->dead = TRUE; +        i->rtpoll->scan_for_dead = TRUE; +        return; +    } + +    rtpoll_item_destroy(i); +} + +struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) { +    pa_assert(i); + +    if (i->n_pollfd > 0) +        if (i->rtpoll->rebuild_needed) +            rtpoll_rebuild(i->rtpoll); + +    if (n_fds) +        *n_fds = i->n_pollfd; + +    return i->pollfd; +} + +void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) { +    pa_assert(i); +    pa_assert(i->priority < PA_RTPOLL_NEVER); + +    i->before_cb = before_cb; +} + +void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) { +    pa_assert(i); +    pa_assert(i->priority < PA_RTPOLL_NEVER); + +    i->after_cb = after_cb; +} + +void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) { +    pa_assert(i); +    pa_assert(i->priority < PA_RTPOLL_NEVER); + +    i->work_cb = work_cb; +} + +void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) { +    pa_assert(i); + +    i->userdata = userdata; +} + +void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) { +    pa_assert(i); + +    return i->userdata; +} + +static int fdsem_before(pa_rtpoll_item *i) { + +    if (pa_fdsem_before_poll(i->userdata) < 0) +        return 1; /* 1 means immediate restart of the loop */ + +    return 0; +} + +static void fdsem_after(pa_rtpoll_item *i) { +    pa_assert(i); + +    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); +    pa_fdsem_after_poll(i->userdata); +} + +pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) { +    pa_rtpoll_item *i; +    struct pollfd *pollfd; + +    pa_assert(p); +    pa_assert(f); + +    i = pa_rtpoll_item_new(p, prio, 1); + +    pollfd = pa_rtpoll_item_get_pollfd(i, NULL); + +    pollfd->fd = pa_fdsem_get(f); +    pollfd->events = POLLIN; + +    i->before_cb = fdsem_before; +    i->after_cb = fdsem_after; +    i->userdata = f; + +    return i; +} + +static int asyncmsgq_before(pa_rtpoll_item *i) { +    pa_assert(i); + +    if (pa_asyncmsgq_before_poll(i->userdata) < 0) +        return 1; /* 1 means immediate restart of the loop */ + +    return 0; +} + +static void asyncmsgq_after(pa_rtpoll_item *i) { +    pa_assert(i); + +    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); +    pa_asyncmsgq_after_poll(i->userdata); +} + +static int asyncmsgq_work(pa_rtpoll_item *i) { +    pa_msgobject *object; +    int code; +    void *data; +    pa_memchunk chunk; +    int64_t offset; + +    pa_assert(i); + +    if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) { +        int ret; + +        if (!object && code == PA_MESSAGE_SHUTDOWN) { +            pa_asyncmsgq_done(i->userdata, 0); +            pa_rtpoll_quit(i->rtpoll); +            return 1; +        } + +        ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk); +        pa_asyncmsgq_done(i->userdata, ret); +        return 1; +    } + +    return 0; +} + +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) { +    pa_rtpoll_item *i; +    struct pollfd *pollfd; + +    pa_assert(p); +    pa_assert(q); + +    i = pa_rtpoll_item_new(p, prio, 1); + +    pollfd = pa_rtpoll_item_get_pollfd(i, NULL); +    pollfd->fd = pa_asyncmsgq_get_fd(q); +    pollfd->events = POLLIN; + +    i->before_cb = asyncmsgq_before; +    i->after_cb = asyncmsgq_after; +    i->work_cb = asyncmsgq_work; +    i->userdata = q; + +    return i; +} + +void pa_rtpoll_quit(pa_rtpoll *p) { +    pa_assert(p); + +    p->quit = TRUE; +} diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h new file mode 100644 index 00000000..02f5c7c2 --- /dev/null +++ b/src/pulsecore/rtpoll.h @@ -0,0 +1,116 @@ +#ifndef foopulsertpollhfoo +#define foopulsertpollhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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 <sys/types.h> +#include <limits.h> + +#include <pulse/sample.h> +#include <pulsecore/asyncmsgq.h> +#include <pulsecore/fdsem.h> +#include <pulsecore/macro.h> + +/* An implementation of a "real-time" poll loop. Basically, this is + * yet another wrapper around poll(). However it has certain + * advantages over pa_mainloop and suchlike: + * + * 1) It uses timer_create() and POSIX real time signals to guarantee + * optimal high-resolution timing. Starting with Linux 2.6.21 hrtimers + * are available, and since right now only nanosleep() and the POSIX + * clock and timer interfaces have been ported to hrtimers (and not + * ppoll/pselect!) we have to combine ppoll() with timer_create(). The + * fact that POSIX timers and POSIX rt signals are used internally is + * completely hidden. + * + * 2) It allows raw access to the pollfd data to users + * + * 3) It allows arbitrary functions to be run before entering the + * actual poll() and after it. + * + * Only a single interval timer is supported..*/ + +typedef struct pa_rtpoll pa_rtpoll; +typedef struct pa_rtpoll_item pa_rtpoll_item; + +typedef enum pa_rtpoll_priority { +    PA_RTPOLL_EARLY  = -100,          /* For veeery important stuff, like handling control messages */ +    PA_RTPOLL_NORMAL = 0,             /* For normal stuff */ +    PA_RTPOLL_LATE   = +100,          /* For housekeeping */ +    PA_RTPOLL_NEVER  = INT_MAX,       /* For stuff that doesn't register any callbacks, but only fds to listen on */ +} pa_rtpoll_priority_t; + +pa_rtpoll *pa_rtpoll_new(void); +void pa_rtpoll_free(pa_rtpoll *p); + +/* Install the rtpoll in the current thread */ +void pa_rtpoll_install(pa_rtpoll *p); + +/* Sleep on the rtpoll until the time event, or any of the fd events + * is triggered. If "wait" is 0 we don't sleep but only update the + * struct pollfd. Returns negative on error, positive if the loop + * should continue to run, 0 when the loop should be terminated + * cleanly. */ +int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait); + +void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts); +void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec); +void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec); +void pa_rtpoll_set_timer_disabled(pa_rtpoll *p); + +/* A new fd wakeup item for pa_rtpoll */ +pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds); +void pa_rtpoll_item_free(pa_rtpoll_item *i); + +/* Please note that this pointer might change on every call and when + * pa_rtpoll_run() is called. Hence: call this immediately before + * using the pointer and don't save the result anywhere */ +struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds); + +/* Set the callback that shall be called when there's time to do some work: If the + * callback returns a value > 0, the poll is skipped and the next + * iteraton of the loop will start immediately. */ +void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)); + +/* Set the callback that shall be called immediately before entering + * the sleeping poll: If the callback returns a value > 0, the poll is + * skipped and the next iteraton of the loop will start + * immediately.. */ +void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)); + +/* Set the callback that shall be called immediately after having + * entered the sleeping poll */ +void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)); + +void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata); +void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i); + +pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s); +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q); + +/* Requests the loop to exit. Will cause the next iteration of + * pa_rtpoll_run() to return 0 */ +void pa_rtpoll_quit(pa_rtpoll *p); + +#endif diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c new file mode 100644 index 00000000..bfc49c88 --- /dev/null +++ b/src/pulsecore/rtsig.c @@ -0,0 +1,133 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <signal.h> + +#include <pulsecore/macro.h> +#include <pulsecore/flist.h> +#include <pulsecore/once.h> +#include <pulsecore/thread.h> +#include <pulsecore/core-util.h> + +#include "rtsig.h" + +#ifdef SIGRTMIN + +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_TLS_DECLARE(rtsig_tls, _free_rtsig); + +static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1); + +static int rtsig_start = -1, rtsig_end = -1; + +int pa_rtsig_get(void) { +    void *p; +    int sig; + +    if ((p = pa_flist_pop(PA_STATIC_FLIST_GET(rtsig_flist)))) +        return PA_PTR_TO_INT(p); + +    sig = pa_atomic_dec(&rtsig_current); + +    pa_assert(sig <= SIGRTMAX); +    pa_assert(sig <= rtsig_end); + +    if (sig < rtsig_start) { +        pa_atomic_inc(&rtsig_current); +        return -1; +    } + +    return sig; +} + +int pa_rtsig_get_for_thread(void) { +    int sig; +    void *p; + +    if ((p = PA_STATIC_TLS_GET(rtsig_tls))) +        return PA_PTR_TO_INT(p); + +    if ((sig = pa_rtsig_get()) < 0) +        return -1; + +    PA_STATIC_TLS_SET(rtsig_tls, PA_INT_TO_PTR(sig)); +    return sig; +} + +void pa_rtsig_put(int sig) { +    pa_assert(sig >= rtsig_start); +    pa_assert(sig <= rtsig_end); + +    pa_assert_se(pa_flist_push(PA_STATIC_FLIST_GET(rtsig_flist), PA_INT_TO_PTR(sig)) >= 0); +} + +void pa_rtsig_configure(int start, int end) { +    int s; +    sigset_t ss; + +    pa_assert(pa_atomic_load(&rtsig_current) == -1); + +    pa_assert(SIGRTMIN <= start); +    pa_assert(start <= end); +    pa_assert(end <= SIGRTMAX); + +    rtsig_start = start; +    rtsig_end = end; + +    sigemptyset(&ss); + +    for (s = rtsig_start; s <= rtsig_end; s++) +        pa_assert_se(sigaddset(&ss, s) == 0); + +    pa_assert(pthread_sigmask(SIG_BLOCK, &ss, NULL) == 0); + +    /* We allocate starting from the end */ +    pa_atomic_store(&rtsig_current, rtsig_end); +} + +#else /* SIGRTMIN */ + +int pa_rtsig_get(void) { +    return -1; +} + +int pa_rtsig_get_for_thread(void) { +    return -1; +} + +void pa_rtsig_put(int sig) { +} + +void pa_rtsig_configure(int start, int end) { +} + +#endif /* SIGRTMIN */ diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h new file mode 100644 index 00000000..7830d272 --- /dev/null +++ b/src/pulsecore/rtsig.h @@ -0,0 +1,41 @@ +#ifndef foopulsertsighfoo +#define foopulsertsighfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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. +***/ + +/* Return the next unused POSIX Realtime signals */ +int pa_rtsig_get(void); + +/* If not called before in the current thread, return the next unused + * rtsig, and install it in a TLS region and give it up automatically + * when the thread shuts down */ +int pa_rtsig_get_for_thread(void); + +/* Give an rtsig back. */ +void pa_rtsig_put(int sig); + +/* Block all RT signals */ +void pa_rtsig_configure(int start, int end); + +#endif diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index a9971408..21771302 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -28,53 +28,71 @@  #include <stdio.h>  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <liboil/liboilfuncs.h> +#include <liboil/liboil.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "sample-util.h"  #include "endianmacros.h" -#define PA_SILENCE_MAX (1024*1024*1) +#define PA_SILENCE_MAX (PA_PAGE_SIZE*16)  pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) {      size_t fs; -    assert(pool); -    assert(spec); +    pa_assert(pool); +    pa_assert(spec); -    if (length == 0) +    if (length <= 0)          length = pa_bytes_per_second(spec)/20; /* 50 ms */      if (length > PA_SILENCE_MAX)          length = PA_SILENCE_MAX;      fs = pa_frame_size(spec); -    length = ((PA_SILENCE_MAX+fs-1) / fs) * fs; + +    length = (length+fs-1)/fs;      if (length <= 0) -        length = fs; +        length = 1; + +    length *= fs;      return pa_silence_memblock(pa_memblock_new(pool, length), spec);  }  pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { -    assert(b && b->data && spec); -    pa_silence_memory(b->data, b->length, spec); +    void *data; + +    pa_assert(b); +    pa_assert(spec); + +    data = pa_memblock_acquire(b); +    pa_silence_memory(data, pa_memblock_get_length(b), spec); +    pa_memblock_release(b);      return b;  }  void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { -    assert(c && c->memblock && c->memblock->data && spec && c->length); +    void *data; -    pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec); +    pa_assert(c); +    pa_assert(c->memblock); +    pa_assert(spec); + +    data = pa_memblock_acquire(c->memblock); +    pa_silence_memory((uint8_t*) data+c->index, c->length, spec); +    pa_memblock_release(c->memblock);  }  void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {      uint8_t c = 0; -    assert(p && length && spec); +    pa_assert(p); +    pa_assert(length > 0); +    pa_assert(spec);      switch (spec->format) {          case PA_SAMPLE_U8: @@ -87,37 +105,51 @@ void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {              c = 0;              break;          case PA_SAMPLE_ALAW: +            c = 0xd5; +            break;          case PA_SAMPLE_ULAW: -            c = 80; +            c = 0xff;              break;          default: -            assert(0); +            pa_assert_not_reached();      }      memset(p, c, length);  }  size_t pa_mix( -    const pa_mix_info streams[], -    unsigned nstreams, -    void *data, -    size_t length, -    const pa_sample_spec *spec, -    const pa_cvolume *volume, -    int mute) { +        pa_mix_info streams[], +        unsigned nstreams, +        void *data, +        size_t length, +        const pa_sample_spec *spec, +        const pa_cvolume *volume, +        int mute) { + +    pa_cvolume full_volume; +    size_t d = 0; +    unsigned k; + +    pa_assert(streams); +    pa_assert(data); +    pa_assert(length); +    pa_assert(spec); -    assert(streams && data && length && spec); +    if (!volume) +        volume = pa_cvolume_reset(&full_volume, spec->channels); + +    for (k = 0; k < nstreams; k++) +        streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock);      switch (spec->format) {          case PA_SAMPLE_S16NE:{ -            size_t d;              unsigned channel = 0;              for (d = 0;; d += sizeof(int16_t)) {                  int32_t sum = 0;                  if (d >= length) -                    return d; +                    goto finish;                  if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {                      unsigned i; @@ -127,12 +159,12 @@ size_t pa_mix(                          pa_volume_t cvolume = streams[i].volume.values[channel];                          if (d >= streams[i].chunk.length) -                            return d; +                            goto finish;                          if (cvolume == PA_VOLUME_MUTED)                              v = 0;                          else { -                            v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); +                            v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));                              if (cvolume != PA_VOLUME_NORM)                                  v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -144,28 +176,27 @@ size_t pa_mix(                      if (volume->values[channel] != PA_VOLUME_NORM)                          sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel])); -                    if (sum < -0x8000) sum = -0x8000; -                    if (sum > 0x7FFF) sum = 0x7FFF; - +                    sum = CLAMP(sum, -0x8000, 0x7FFF);                  } -                *((int16_t*) data) = sum; +                *((int16_t*) data) = (int16_t) sum;                  data = (uint8_t*) data + sizeof(int16_t);                  if (++channel >= spec->channels)                      channel = 0;              } + +            break;          }          case PA_SAMPLE_S16RE:{ -            size_t d;              unsigned channel = 0;              for (d = 0;; d += sizeof(int16_t)) {                  int32_t sum = 0;                  if (d >= length) -                    return d; +                    goto finish;                  if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {                      unsigned i; @@ -175,12 +206,12 @@ size_t pa_mix(                          pa_volume_t cvolume = streams[i].volume.values[channel];                          if (d >= streams[i].chunk.length) -                            return d; +                            goto finish;                          if (cvolume == PA_VOLUME_MUTED)                              v = 0;                          else { -                            v = INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d))); +                            v = PA_INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)));                              if (cvolume != PA_VOLUME_NORM)                                  v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -192,28 +223,27 @@ size_t pa_mix(                      if (volume->values[channel] != PA_VOLUME_NORM)                          sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel])); -                    if (sum < -0x8000) sum = -0x8000; -                    if (sum > 0x7FFF) sum = 0x7FFF; - +                    sum = CLAMP(sum, -0x8000, 0x7FFF);                  } -                *((int16_t*) data) = INT16_SWAP(sum); +                *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);                  data = (uint8_t*) data + sizeof(int16_t);                  if (++channel >= spec->channels)                      channel = 0;              } + +            break;          }          case PA_SAMPLE_U8: { -            size_t d;              unsigned channel = 0;              for (d = 0;; d ++) {                  int32_t sum = 0;                  if (d >= length) -                    return d; +                    goto finish;                  if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {                      unsigned i; @@ -223,12 +253,12 @@ size_t pa_mix(                          pa_volume_t cvolume = streams[i].volume.values[channel];                          if (d >= streams[i].chunk.length) -                            return d; +                            goto finish;                          if (cvolume == PA_VOLUME_MUTED)                              v = 0;                          else { -                            v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80; +                            v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80;                              if (cvolume != PA_VOLUME_NORM)                                  v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -240,9 +270,7 @@ size_t pa_mix(                      if (volume->values[channel] != PA_VOLUME_NORM)                          sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel])); -                    if (sum < -0x80) sum = -0x80; -                    if (sum > 0x7F) sum = 0x7F; - +                    sum = CLAMP(sum, -0x80, 0x7F);                  }                  *((uint8_t*) data) = (uint8_t) (sum + 0x80); @@ -251,17 +279,18 @@ size_t pa_mix(                  if (++channel >= spec->channels)                      channel = 0;              } + +            break;          }          case PA_SAMPLE_FLOAT32NE: { -            size_t d;              unsigned channel = 0;              for (d = 0;; d += sizeof(float)) {                  float sum = 0;                  if (d >= length) -                    return d; +                    goto finish;                  if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {                      unsigned i; @@ -271,12 +300,12 @@ size_t pa_mix(                          pa_volume_t cvolume = streams[i].volume.values[channel];                          if (d >= streams[i].chunk.length) -                            return d; +                            goto finish;                          if (cvolume == PA_VOLUME_MUTED)                              v = 0;                          else { -                            v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); +                            v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));                              if (cvolume != PA_VOLUME_NORM)                                  v *= pa_sw_volume_to_linear(cvolume); @@ -295,18 +324,35 @@ size_t pa_mix(                  if (++channel >= spec->channels)                      channel = 0;              } + +            break;          }          default:              pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));              abort();      } + +finish: + +    for (k = 0; k < nstreams; k++) +        pa_memblock_release(streams[k].chunk.memblock); + +    return d;  } -void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) { -    assert(c && spec && (c->length % pa_frame_size(spec) == 0)); -    assert(volume); +void pa_volume_memchunk( +        pa_memchunk*c, +        const pa_sample_spec *spec, +        const pa_cvolume *volume) { + +    void *ptr; + +    pa_assert(c); +    pa_assert(spec); +    pa_assert(c->length % pa_frame_size(spec) == 0); +    pa_assert(volume);      if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))          return; @@ -316,24 +362,25 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol          return;      } +    ptr = pa_memblock_acquire(c->memblock); +      switch (spec->format) { +          case PA_SAMPLE_S16NE: {              int16_t *d;              size_t n;              unsigned channel; -            double linear[PA_CHANNELS_MAX]; +            int32_t linear[PA_CHANNELS_MAX];              for (channel = 0; channel < spec->channels; channel++) -                linear[channel] = pa_sw_volume_to_linear(volume->values[channel]); - -            for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { -                int32_t t = (int32_t)(*d); +                linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000); -                t = (int32_t) (t * linear[channel]); - -                if (t < -0x8000) t = -0x8000; -                if (t > 0x7FFF) t = 0x7FFF; +            for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { +                int32_t t; +                t = (int32_t)(*d); +                t = (t * linear[channel]) / 0x10000; +                t = CLAMP(t, -0x8000, 0x7FFF);                  *d = (int16_t) t;                  if (++channel >= spec->channels) @@ -346,20 +393,18 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol              int16_t *d;              size_t n;              unsigned channel; -            double linear[PA_CHANNELS_MAX]; +            int32_t linear[PA_CHANNELS_MAX];              for (channel = 0; channel < spec->channels; channel++) -                linear[channel] = pa_sw_volume_to_linear(volume->values[channel]); - -            for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { -                int32_t t = (int32_t)(INT16_SWAP(*d)); +                linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000); -                t = (int32_t) (t * linear[channel]); +            for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { +                int32_t t; -                if (t < -0x8000) t = -0x8000; -                if (t > 0x7FFF) t = 0x7FFF; - -                *d = INT16_SWAP((int16_t) t); +                t = (int32_t)(PA_INT16_SWAP(*d)); +                t = (t * linear[channel]) / 0x10000; +                t = CLAMP(t, -0x8000, 0x7FFF); +                *d = PA_INT16_SWAP((int16_t) t);                  if (++channel >= spec->channels)                      channel = 0; @@ -371,16 +416,18 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol          case PA_SAMPLE_U8: {              uint8_t *d;              size_t n; -            unsigned channel = 0; - -            for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) { -                int32_t t = (int32_t) *d - 0x80; +            unsigned channel; +            int32_t linear[PA_CHANNELS_MAX]; -                t = (int32_t) (t * pa_sw_volume_to_linear(volume->values[channel])); +            for (channel = 0; channel < spec->channels; channel++) +                linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000); -                if (t < -0x80) t = -0x80; -                if (t > 0x7F) t = 0x7F; +            for (channel = 0, d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) { +                int32_t t; +                t = (int32_t) *d - 0x80; +                t = (t * linear[channel]) / 0x10000; +                t = CLAMP(t, -0x80, 0x7F);                  *d = (uint8_t) (t + 0x80);                  if (++channel >= spec->channels) @@ -395,7 +442,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol              unsigned n;              unsigned channel; -            d = (float*) ((uint8_t*) c->memblock->data + c->index); +            d = (float*) ((uint8_t*) ptr + c->index);              skip = spec->channels * sizeof(float);              n = c->length/sizeof(float)/spec->channels; @@ -406,7 +453,6 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol                      continue;                  v = (float) pa_sw_volume_to_linear(volume->values[channel]); -                  t = d + channel;                  oil_scalarmult_f32(t, skip, t, skip, &v, n);              } @@ -414,9 +460,85 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol          }          default: -            pa_log_error("ERROR: Unable to change volume of format %s.", -                pa_sample_format_to_string(spec->format)); -            abort(); +            pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format)); +            /* If we cannot change the volume, we just don't do it */      } + +    pa_memblock_release(c->memblock); +} + +size_t pa_frame_align(size_t l, const pa_sample_spec *ss) { +    size_t fs; + +    pa_assert(ss); + +    fs = pa_frame_size(ss); + +    return (l/fs) * fs;  } +int pa_frame_aligned(size_t l, const pa_sample_spec *ss) { +    size_t fs; + +    pa_assert(ss); + +    fs = pa_frame_size(ss); + +    return l % fs == 0; +} + +void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) { +    unsigned c; +    size_t fs; + +    pa_assert(src); +    pa_assert(channels > 0); +    pa_assert(dst); +    pa_assert(ss > 0); +    pa_assert(n > 0); + +    fs = ss * channels; + +    for (c = 0; c < channels; c++) { +        unsigned j; +        void *d; +        const void *s; + +        s = src[c]; +        d = (uint8_t*) dst + c * ss; + +        for (j = 0; j < n; j ++) { +            oil_memcpy(d, s, ss); +            s = (uint8_t*) s + ss; +            d = (uint8_t*) d + fs; +        } +    } +} + +void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) { +    size_t fs; +    unsigned c; + +    pa_assert(src); +    pa_assert(dst); +    pa_assert(channels > 0); +    pa_assert(ss > 0); +    pa_assert(n > 0); + +    fs = ss * channels; + +    for (c = 0; c < channels; c++) { +        unsigned j; +        const void *s; +        void *d; + +        s = (uint8_t*) src + c * ss; +        d = dst[c]; + +        for (j = 0; j < n; j ++) { +            oil_memcpy(d, s, ss); +            s = (uint8_t*) s + fs; +            d = (uint8_t*) d + ss; +        } +    } +} diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 3ff065ab..0a39d5ca 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -39,10 +39,11 @@ typedef struct pa_mix_info {      pa_memchunk chunk;      pa_cvolume volume;      void *userdata; +    void *internal; /* Used internally by pa_mix(), should not be initialised when calling pa_mix() */  } pa_mix_info;  size_t pa_mix( -    const pa_mix_info channels[], +    pa_mix_info channels[],      unsigned nchannels,      void *data,      size_t length, @@ -55,4 +56,11 @@ void pa_volume_memchunk(      const pa_sample_spec *spec,      const pa_cvolume *volume); +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; + +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); +  #endif diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c index c530e79b..f74d0282 100644 --- a/src/pulsecore/sconv-s16be.c +++ b/src/pulsecore/sconv-s16be.c @@ -27,12 +27,15 @@  #include "endianmacros.h" -#define INT16_FROM INT16_FROM_BE -#define INT16_TO INT16_TO_BE +#define INT16_FROM PA_INT16_FROM_BE +#define INT16_TO PA_INT16_TO_BE  #define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne  #define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne +#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re +#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re +  #ifdef WORDS_BIGENDIAN  #define SWAP_WORDS 0  #else diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h index 6b736f69..ad034489 100644 --- a/src/pulsecore/sconv-s16be.h +++ b/src/pulsecore/sconv-s16be.h @@ -24,7 +24,18 @@    USA.  ***/ -void pa_sconv_s16be_to_float32ne(unsigned n, const void *a, float *b); -void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b); +#include <inttypes.h> + +void pa_sconv_s16be_to_float32ne(unsigned n, const int16_t *a, float *b); +void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b); +void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b); +void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b); + +#ifdef WORDS_BIGENDIAN +#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne +#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne +#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re +#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re +#endif  #endif diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c index 5f45ef66..6925052c 100644 --- a/src/pulsecore/sconv-s16le.c +++ b/src/pulsecore/sconv-s16le.c @@ -25,12 +25,12 @@  #include <config.h>  #endif -#include <assert.h>  #include <inttypes.h>  #include <liboil/liboilfuncs.h>  #include <pulsecore/sconv.h> +#include <pulsecore/macro.h>  #include <pulsecore/log.h>  #include "endianmacros.h" @@ -38,11 +38,11 @@  #include "sconv-s16le.h"  #ifndef INT16_FROM -#define INT16_FROM INT16_FROM_LE +#define INT16_FROM PA_INT16_FROM_LE  #endif  #ifndef INT16_TO -#define INT16_TO INT16_TO_LE +#define INT16_TO PA_INT16_TO_LE  #endif  #ifndef SWAP_WORDS @@ -53,32 +53,28 @@  #endif  #endif -void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) { -    const int16_t *ca = a; - -    assert(a); -    assert(b); +void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) { +    pa_assert(a); +    pa_assert(b);  #if SWAP_WORDS == 1      for (; n > 0; n--) { -        int16_t s = *(ca++); +        int16_t s = *(a++);          *(b++) = ((float) INT16_FROM(s))/0x7FFF;      }  #else  {      static const double add = 0, factor = 1.0/0x7FFF; -    oil_scaleconv_f32_s16(b, ca, n, &add, &factor); +    oil_scaleconv_f32_s16(b, a, n, &add, &factor);  }  #endif  } -void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) { -    int16_t *cb = b; - -    assert(a); -    assert(b); +void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) { +    pa_assert(a); +    pa_assert(b);  #if SWAP_WORDS == 1 @@ -86,20 +82,43 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) {          int16_t s;          float v = *(a++); -        if (v > 1) -            v = 1; - -        if (v < -1) -            v = -1; - +        v = CLAMP(v, -1, 1);          s = (int16_t) (v * 0x7FFF); -        *(cb++) = INT16_TO(s); +        *(b++) = INT16_TO(s);      }  #else  {      static const double add = 0, factor = 0x7FFF; -    oil_scaleconv_s16_f32(cb, a, n, &add, &factor); +    oil_scaleconv_s16_f32(b, a, n, &add, &factor);  }  #endif  } + +void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) { +    pa_assert(a); +    pa_assert(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); +        *(b++) = k; +    } +} + +void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--) { +        int16_t s; +        float v = *(a++); +        uint32_t *j = (uint32_t*) &v; +        *j = PA_UINT32_SWAP(*j); +        v = CLAMP(v, -1, 1); +        s = (int16_t) (v * 0x7FFF); +        *(b++) = INT16_TO(s); +    } +} diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h index c4e4911a..4203315a 100644 --- a/src/pulsecore/sconv-s16le.h +++ b/src/pulsecore/sconv-s16le.h @@ -24,7 +24,18 @@    USA.  ***/ -void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b); -void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b); +#include <inttypes.h> + +void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b); +void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b); +void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b); +void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b); + +#ifndef WORDS_BIGENDIAN +#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re +#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re +#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne +#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne +#endif  #endif diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c index d15cec84..7f5da63f 100644 --- a/src/pulsecore/sconv.c +++ b/src/pulsecore/sconv.c @@ -28,12 +28,12 @@  #include <stdio.h>  #include <stdlib.h> -#include <assert.h>  #include <liboil/liboilfuncs.h>  #include <liboil/liboil.h>  #include <pulsecore/g711.h> +#include <pulsecore/macro.h>  #include "endianmacros.h"  #include "sconv-s16le.h" @@ -41,152 +41,223 @@  #include "sconv.h" -static void u8_to_float32ne(unsigned n, const void *a, float *b) { -    const uint8_t *ca = a; -    static const double add = -128.0/127.0, factor = 1.0/127.0; +/* u8 */ +static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) { +    static const double add = -1, factor = 1.0/128.0; -    assert(a); -    assert(b); +    pa_assert(a); +    pa_assert(b); -    oil_scaleconv_f32_u8(b, ca, n, &add, &factor); +    oil_scaleconv_f32_u8(b, a, n, &add, &factor);  } -static void u8_from_float32ne(unsigned n, const float *a, void *b) { -    uint8_t *cb = b; -    static const double add = 128.0, factor = 127.0; +static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) { +    static const double add = 128, factor = 127.0; -    assert(a); -    assert(b); +    pa_assert(a); +    pa_assert(b); -    oil_scaleconv_u8_f32(cb, a, n, &add, &factor); +    oil_scaleconv_u8_f32(b, a, n, &add, &factor);  } -static void float32ne_to_float32ne(unsigned n, const void *a, float *b) { -    assert(a); -    assert(b); +static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) { +    static const int16_t add = -0x80, factor = 0x100; -    oil_memcpy(b, a, sizeof(float) * n); +    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); +} + +static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { + +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) +        *b = (uint8_t) (*a / 0x100 + 0x80);  } -static void float32ne_from_float32ne(unsigned n, const float *a, void *b) { -    assert(a); -    assert(b); +/* float32 */ + +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);  } -static void float32re_to_float32ne(unsigned n, const void *a, float *b) { -    assert(a); -    assert(b); +static void float32re_to_float32ne(unsigned n, const float *a, float *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) +        *((uint32_t *) b) = PA_UINT32_SWAP(*((uint32_t *) a)); +} + +/* s16 */ -    while (n-- > 0) -        ((uint32_t *)b)[n] = UINT32_SWAP (((uint32_t *)a)[n]); +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);  } -static void float32re_from_float32ne(unsigned n, const float *a, void *b) { -    assert(a); -    assert(b); +static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) { +    pa_assert(a); +    pa_assert(b); -    while (n-- > 0) -        ((uint32_t *)b)[n] = UINT32_SWAP (((uint32_t *)a)[n]); +    for (; n > 0; n--, a++, b++) +        *b = PA_UINT16_SWAP(*a);  } -static void ulaw_to_float32ne(unsigned n, const void *a, float *b) { -    const uint8_t *ca = a; +/* ulaw */ -    assert(a); -    assert(b); +static void ulaw_to_float32ne(unsigned n, const uint8_t *a, float *b) { +    pa_assert(a); +    pa_assert(b);      for (; n > 0; n--) -        *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF; +        *(b++) = (float) st_ulaw2linear16(*(a++)) / 0x8000;  } -static void ulaw_from_float32ne(unsigned n, const float *a, void *b) { -    uint8_t *cb = b; - -    assert(a); -    assert(b); +static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) { +    pa_assert(a); +    pa_assert(b);      for (; n > 0; n--) {          float v = *(a++); +        v = CLAMP(v, -1, 1); +        v *= 0x1FFF; +        *(b++) = st_14linear2ulaw((int16_t) v); +    } +} + +static void ulaw_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) +        *b = st_ulaw2linear16(*a); +} + +static void ulaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) +        *b = st_14linear2ulaw(*a >> 2); +} -        if (v > 1) -            v = 1; +/* alaw */ -        if (v < -1) -            v = -1; +static void alaw_to_float32ne(unsigned n, const uint8_t *a, float *b) { +    pa_assert(a); +    pa_assert(b); -        *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF)); +    for (; n > 0; n--, a++, b++) +        *b = (float) st_alaw2linear16(*a) / 0x8000; +} + +static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) { +        float v = *a; +        v = CLAMP(v, -1, 1); +        v *= 0xFFF; +        *b = st_13linear2alaw((int16_t) v);      }  } -static void alaw_to_float32ne(unsigned n, const void *a, float *b) { -    const uint8_t *ca = a; +static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) { +    pa_assert(a); +    pa_assert(b); -    assert(a); -    assert(b); +    for (; n > 0; n--, a++, b++) +        *b = st_alaw2linear16(*a); +} -    for (; n > 0; n--) -        *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF; +static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { +    pa_assert(a); +    pa_assert(b); + +    for (; n > 0; n--, a++, b++) +        *b = st_13linear2alaw(*a >> 3);  } -static void alaw_from_float32ne(unsigned n, const float *a, void *b) { -    uint8_t *cb = b; +pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) { -    assert(a); -    assert(b); +    static const pa_convert_func_t table[] = { +        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_float32ne, +        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_float32ne, +        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_float32ne, +        [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_to_float32ne, +        [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_to_float32ne, +        [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne, +        [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne, +    }; -    for (; n > 0; n--) { -        float v = *(a++); +    pa_assert(f >= 0); +    pa_assert(f < PA_SAMPLE_MAX); -        if (v > 1) -            v = 1; +    return table[f]; +} -        if (v < -1) -            v = -1; +pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) { -        *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF)); -    } +    static const pa_convert_func_t table[] = { +        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_float32ne, +        [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_from_float32ne, +        [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_from_float32ne, +        [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne, +        [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne, +        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_float32ne, +        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_float32ne +    }; + +    pa_assert(f >= 0); +    pa_assert(f < PA_SAMPLE_MAX); + +    return table[f];  } -pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) { -    switch(f) { -        case PA_SAMPLE_U8: -            return u8_to_float32ne; -        case PA_SAMPLE_S16LE: -            return pa_sconv_s16le_to_float32ne; -        case PA_SAMPLE_S16BE: -            return pa_sconv_s16be_to_float32ne; -        case PA_SAMPLE_FLOAT32NE: -            return float32ne_to_float32ne; -        case PA_SAMPLE_FLOAT32RE: -            return float32re_to_float32ne; -        case PA_SAMPLE_ALAW: -            return alaw_to_float32ne; -        case PA_SAMPLE_ULAW: -            return ulaw_to_float32ne; -        default: -            return NULL; -    } +pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) { + +    static const pa_convert_func_t table[] = { +        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_s16ne, +        [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne, +        [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne, +        [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne, +        [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne, +        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_s16ne, +        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_s16ne +    }; + +    pa_assert(f >= 0); +    pa_assert(f < PA_SAMPLE_MAX); + +    return table[f];  } -pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) { -    switch(f) { -        case PA_SAMPLE_U8: -            return u8_from_float32ne; -        case PA_SAMPLE_S16LE: -            return pa_sconv_s16le_from_float32ne; -        case PA_SAMPLE_S16BE: -            return pa_sconv_s16be_from_float32ne; -        case PA_SAMPLE_FLOAT32NE: -            return float32ne_from_float32ne; -        case PA_SAMPLE_FLOAT32RE: -            return float32re_from_float32ne; -        case PA_SAMPLE_ALAW: -            return alaw_from_float32ne; -        case PA_SAMPLE_ULAW: -            return ulaw_from_float32ne; -        default: -            return NULL; -    } +pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) { + +    static const pa_convert_func_t table[] = { +        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_s16ne, +        [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne, +        [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne, +        [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne, +        [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne, +        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_s16ne, +        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_s16ne, +    }; + +    pa_assert(f >= 0); +    pa_assert(f < PA_SAMPLE_MAX); + +    return table[f];  } diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h index 1e97aad9..901f50a3 100644 --- a/src/pulsecore/sconv.h +++ b/src/pulsecore/sconv.h @@ -27,10 +27,12 @@  #include <pulse/sample.h> -typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, float *b); -typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b); +typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b); -pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f); -pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f); +pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) PA_GCC_PURE; +pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) PA_GCC_PURE; + +pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE; +pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;  #endif diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c new file mode 100644 index 00000000..750c2afc --- /dev/null +++ b/src/pulsecore/semaphore-posix.c @@ -0,0 +1,69 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <errno.h> +#include <pthread.h> +#include <semaphore.h> + +#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> + +#include "semaphore.h" + +struct pa_semaphore { +    sem_t sem; +}; + +pa_semaphore* pa_semaphore_new(unsigned value) { +    pa_semaphore *s; + +    s = pa_xnew(pa_semaphore, 1); +    pa_assert_se(sem_init(&s->sem, 0, value) == 0); +    return s; +} + +void pa_semaphore_free(pa_semaphore *s) { +    pa_assert(s); +    pa_assert_se(sem_destroy(&s->sem) == 0); +    pa_xfree(s); +} + +void pa_semaphore_post(pa_semaphore *s) { +    pa_assert(s); +    pa_assert_se(sem_post(&s->sem) == 0); +} + +void pa_semaphore_wait(pa_semaphore *s) { +    int ret; +    pa_assert(s); + +    do { +        ret = sem_wait(&s->sem); +    } while (ret < 0 && errno == EINTR); + +    pa_assert(ret == 0); +} diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c new file mode 100644 index 00000000..f6576348 --- /dev/null +++ b/src/pulsecore/semaphore-win32.c @@ -0,0 +1,65 @@ +/* $Id: mutex-win32.c 1426 2007-02-13 15:35:19Z ossman $ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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 <windows.h> + +#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> + +#include "semaphore.h" + +struct pa_semaphore +{ +    HANDLE sema; +}; + +pa_semaphore* pa_semaphore_new(unsigned value) { +    pa_semaphore *s; + +    s = pa_xnew(pa_semaphore, 1); + +    s->sema = CreateSemaphore(NULL, value, 32767, NULL); +    pa_assert(s->sema != NULL); + +    return s; +} + +void pa_semaphore_free(pa_semaphore *s) { +    pa_assert(s); +    CloseHandle(s->sema); +    pa_xfree(s); +} + +void pa_semaphore_post(pa_semaphore *s) { +    pa_assert(s); +    ReleaseSemaphore(s->sema, 1, NULL); +} + +void pa_semaphore_wait(pa_semaphore *s) { +    pa_assert(s); +    WaitForSingleObject(s->sema, INFINITE); +} diff --git a/src/pulsecore/anotify.h b/src/pulsecore/semaphore.h index b3f75b7e..c394e0f2 100644 --- a/src/pulsecore/anotify.h +++ b/src/pulsecore/semaphore.h @@ -1,5 +1,5 @@ -#ifndef foopulseanotifyhfoo -#define foopulseanotifyhfoo +#ifndef foopulsesemaphorehfoo +#define foopulsesemaphorehfoo  /* $Id$ */ @@ -24,17 +24,12 @@    USA.  ***/ -/* Asynchronous thread-safe notification of mainloops */ +typedef struct pa_semaphore pa_semaphore; +pa_semaphore* pa_semaphore_new(unsigned value); +void pa_semaphore_free(pa_semaphore *m); -#include <inttypes.h> -#include <pulse/mainloop-api.h> - -typedef struct pa_anotify pa_anotify; -typedef void (*pa_anotify_cb_t)(uint8_t event, void *userdata); - -pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata); -void pa_anotify_free(pa_anotify *a); -int pa_anotify_signal(pa_anotify *a, uint8_t event); +void pa_semaphore_post(pa_semaphore *m); +void pa_semaphore_wait(pa_semaphore *m);  #endif diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 444d4010..6882e7f8 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -1,32 +1,31 @@  /* $Id$ */  /*** -  This file is part of PulseAudio. +    This file is part of PulseAudio. -  Copyright 2006 Lennart Poettering -  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB +    Copyright 2006 Lennart Poettering +    Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB -  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 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. +    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. +    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 <assert.h>  #include <stdlib.h>  #include <unistd.h>  #include <fcntl.h> @@ -34,15 +33,22 @@  #include <errno.h>  #include <string.h>  #include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <signal.h>  #ifdef HAVE_SYS_MMAN_H  #include <sys/mman.h>  #endif +#include <pulse/xmalloc.h> +  #include <pulsecore/core-error.h>  #include <pulsecore/log.h>  #include <pulsecore/random.h> -#include <pulse/xmalloc.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> +#include <pulsecore/atomic.h>  #include "shm.h" @@ -50,10 +56,31 @@  #define MADV_REMOVE 9  #endif -#define MAX_SHM_SIZE (1024*1024*20) +#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20)) + +#ifdef __linux__ +/* On Linux we know that the shared memory blocks are files in + * /dev/shm. We can use that information to list all blocks and + * cleanup unused ones */ +#define SHM_PATH "/dev/shm/" +#else +#undef SHM_PATH +#endif + +#define SHM_MARKER ((int) 0xbeefcafe) + +/* We now put this SHM marker at the end of each segment. It's optional to not require a reboot when upgrading, though */ +struct shm_marker { +    pa_atomic_t marker; /* 0xbeefcafe */ +    pa_atomic_t pid; +    void *_reserverd1; +    void *_reserverd2; +    void *_reserverd3; +    void *_reserverd4; +};  static char *segment_name(char *fn, size_t l, unsigned id) { -    snprintf(fn, l, "/pulse-shm-%u", id); +    pa_snprintf(fn, l, "/pulse-shm-%u", id);      return fn;  } @@ -61,10 +88,17 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {      char fn[32];      int fd = -1; -    assert(m); -    assert(size > 0); -    assert(size < MAX_SHM_SIZE); -    assert(mode >= 0600); +    pa_assert(m); +    pa_assert(size > 0); +    pa_assert(size < MAX_SHM_SIZE); +    pa_assert(mode >= 0600); + +    /* Each time we create a new SHM area, let's first drop all stale +     * ones */ +    pa_shm_cleanup(); + +    /* Round up to make it aligned */ +    size = PA_ALIGN(size);      if (!shared) {          m->id = 0; @@ -79,7 +113,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {          {              int r; -            if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) { +            if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {                  pa_log("posix_memalign() failed: %s", pa_cstrerror(r));                  goto fail;              } @@ -92,6 +126,8 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {      } else {  #ifdef HAVE_SHM_OPEN +        struct shm_marker *marker; +          pa_random(&m->id, sizeof(m->id));          segment_name(fn, sizeof(fn), m->id); @@ -100,7 +136,9 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {              goto fail;          } -        if (ftruncate(fd, m->size = size) < 0) { +        m->size = size + PA_ALIGN(sizeof(struct shm_marker)); + +        if (ftruncate(fd, m->size) < 0) {              pa_log("ftruncate() failed: %s", pa_cstrerror(errno));              goto fail;          } @@ -110,10 +148,16 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {              goto fail;          } -        close(fd); +        /* We store our PID at the end of the shm block, so that we +         * can check for dead shm segments later */ +        marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker))); +        pa_atomic_store(&marker->pid, (int) getpid()); +        pa_atomic_store(&marker->marker, SHM_MARKER); + +        pa_assert_se(close(fd) == 0);          m->do_unlink = 1;  #else -		return -1; +        return -1;  #endif      } @@ -126,7 +170,7 @@ fail:  #ifdef HAVE_SHM_OPEN      if (fd >= 0) {          shm_unlink(fn); -        close(fd); +        pa_close(fd);      }  #endif @@ -134,76 +178,70 @@ fail:  }  void pa_shm_free(pa_shm *m) { -    assert(m); -    assert(m->ptr); -    assert(m->size > 0); +    pa_assert(m); +    pa_assert(m->ptr); +    pa_assert(m->size > 0);  #ifdef MAP_FAILED -	assert(m->ptr != MAP_FAILED); +    pa_assert(m->ptr != MAP_FAILED);  #endif -	if (!m->shared) { +    if (!m->shared) {  #ifdef MAP_ANONYMOUS -	    if (munmap(m->ptr, m->size) < 0) -	        pa_log("munmap() failed: %s", pa_cstrerror(errno)); +        if (munmap(m->ptr, m->size) < 0) +            pa_log("munmap() failed: %s", pa_cstrerror(errno));  #elif defined(HAVE_POSIX_MEMALIGN)          free(m->ptr);  #else          pa_xfree(m->ptr);  #endif -	} else { +    } else {  #ifdef HAVE_SHM_OPEN -	    if (munmap(m->ptr, m->size) < 0) -	        pa_log("munmap() failed: %s", pa_cstrerror(errno)); +        if (munmap(m->ptr, m->size) < 0) +            pa_log("munmap() failed: %s", pa_cstrerror(errno)); -	    if (m->do_unlink) { -		    char fn[32]; +        if (m->do_unlink) { +            char fn[32]; -                    segment_name(fn, sizeof(fn), m->id); +            segment_name(fn, sizeof(fn), m->id); -                    if (shm_unlink(fn) < 0) -                        pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno)); -	    } +            if (shm_unlink(fn) < 0) +                pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno)); +        }  #else -		/* We shouldn't be here without shm support */ -		assert(0); +        /* We shouldn't be here without shm support */ +        pa_assert_not_reached();  #endif -	} +    }      memset(m, 0, sizeof(*m));  }  void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {      void *ptr; +    size_t o, ps; -    assert(m); -    assert(m->ptr); -    assert(m->size > 0); -    assert(offset+size <= m->size); +    pa_assert(m); +    pa_assert(m->ptr); +    pa_assert(m->size > 0); +    pa_assert(offset+size <= m->size);  #ifdef MAP_FAILED -	assert(m->ptr != MAP_FAILED); +    pa_assert(m->ptr != MAP_FAILED);  #endif      /* You're welcome to implement this as NOOP on systems that don't       * support it */ +    /* Align this to multiples of the page size */      ptr = (uint8_t*) m->ptr + offset; - -#ifdef __linux__ -{ -    /* On Linux ptr must be page aligned */ -    long psz = sysconf(_SC_PAGESIZE); -    unsigned o; - -    o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz); +    o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);      if (o > 0) { -        ptr = (uint8_t*) ptr + (psz - o); -        size -= psz - o; +        ps = PA_PAGE_SIZE; +        ptr = (uint8_t*) ptr + (ps - o); +        size -= ps - o;      } -} -#endif  #ifdef MADV_REMOVE      if (madvise(ptr, size, MADV_REMOVE) >= 0) @@ -216,7 +254,9 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {  #endif  #ifdef MADV_DONTNEED -    madvise(ptr, size, MADV_DONTNEED); +    pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0); +#elif defined(POSIX_MADV_DONTNEED) +    pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);  #endif  } @@ -227,12 +267,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {      int fd = -1;      struct stat st; -    assert(m); +    pa_assert(m);      segment_name(fn, sizeof(fn), m->id = id);      if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) { -        pa_log("shm_open() failed: %s", pa_cstrerror(errno)); +        if (errno != EACCES) +            pa_log("shm_open() failed: %s", pa_cstrerror(errno));          goto fail;      } @@ -241,7 +282,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {          goto fail;      } -    if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) { +    if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker)) || PA_ALIGN(st.st_size) != st.st_size) {          pa_log("Invalid shared memory segment size");          goto fail;      } @@ -256,13 +297,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {      m->do_unlink = 0;      m->shared = 1; -    close(fd); +    pa_assert_se(pa_close(fd) == 0);      return 0;  fail:      if (fd >= 0) -        close(fd); +        pa_close(fd);      return -1;  } @@ -270,7 +311,71 @@ fail:  #else /* HAVE_SHM_OPEN */  int pa_shm_attach_ro(pa_shm *m, unsigned id) { -	return -1; +    return -1;  }  #endif /* HAVE_SHM_OPEN */ + +int pa_shm_cleanup(void) { + +#ifdef SHM_PATH +    DIR *d; +    struct dirent *de; + +    if (!(d = opendir(SHM_PATH))) { +        pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno)); +        return -1; +    } + +    while ((de = readdir(d))) { +        pa_shm seg; +        unsigned id; +        pid_t pid; +        char fn[128]; +        struct shm_marker *m; + +        if (strncmp(de->d_name, "pulse-shm-", 10)) +            continue; + +        if (pa_atou(de->d_name + 10, &id) < 0) +            continue; + +        if (pa_shm_attach_ro(&seg, id) < 0) +            continue; + +        if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) { +            pa_shm_free(&seg); +            continue; +        } + +        m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker))); + +        if (pa_atomic_load(&m->marker) != SHM_MARKER) { +            pa_shm_free(&seg); +            continue; +        } + +        if (!(pid = (pid_t) pa_atomic_load(&m->pid))) { +            pa_shm_free(&seg); +            continue; +        } + +        if (kill(pid, 0) == 0 || errno != ESRCH) { +            pa_shm_free(&seg); +            continue; +        } + +        pa_shm_free(&seg); + +        /* Ok, the owner of this shms segment is dead, so, let's remove the segment */ +        segment_name(fn, sizeof(fn), id); + +        if (shm_unlink(fn) < 0 && errno != EACCES) +            pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno)); +    } + +    closedir(d); +#endif + +    return 0; +} diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h index e695a2a1..270591de 100644 --- a/src/pulsecore/shm.h +++ b/src/pulsecore/shm.h @@ -41,4 +41,6 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size);  void pa_shm_free(pa_shm *m); +int pa_shm_cleanup(void); +  #endif diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 3ddd7435..6f654b61 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -42,45 +41,51 @@  #include "sink-input.h" -#define CONVERT_BUFFER_LENGTH 4096 -#define MOVE_BUFFER_LENGTH (1024*1024) -#define SILENCE_BUFFER_LENGTH (64*1024) +#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE) +#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12) +#define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256) -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ -    return NULL; \ -} while (0) +static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject); + +static void sink_input_free(pa_object *o);  pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) { -    assert(data); +    pa_assert(data);      memset(data, 0, sizeof(*data));      data->resample_method = PA_RESAMPLER_INVALID; +      return data;  }  void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) { -    assert(data); +    pa_assert(data);      if ((data->channel_map_is_set = !!map))          data->channel_map = *map;  }  void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { -    assert(data); +    pa_assert(data);      if ((data->volume_is_set = !!volume))          data->volume = *volume;  }  void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) { -    assert(data); +    pa_assert(data);      if ((data->sample_spec_is_set = !!spec))          data->sample_spec = *spec;  } +void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { +    pa_assert(data); + +    data->muted_is_set = TRUE; +    data->muted = !!mute; +} +  pa_sink_input* pa_sink_input_new(          pa_core *core,          pa_sink_input_new_data *data, @@ -88,46 +93,52 @@ pa_sink_input* pa_sink_input_new(      pa_sink_input *i;      pa_resampler *resampler = NULL; -    int r;      char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; -    assert(core); -    assert(data); +    pa_assert(core); +    pa_assert(data); -    if (!(flags & PA_SINK_INPUT_NO_HOOKS)) -        if (pa_hook_fire(&core->hook_sink_input_new, data) < 0) -            return NULL; +    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0) +        return NULL; -    CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver)); -    CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name)); +    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); +    pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name));      if (!data->sink)          data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1); -    CHECK_VALIDITY_RETURN_NULL(data->sink); -    CHECK_VALIDITY_RETURN_NULL(data->sink->state == PA_SINK_RUNNING); +    pa_return_null_if_fail(data->sink); +    pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED); +    pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));      if (!data->sample_spec_is_set)          data->sample_spec = data->sink->sample_spec; -    CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec)); +    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); -    if (!data->channel_map_is_set) -        pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); +    if (!data->channel_map_is_set) { +        if (data->sink->channel_map.channels == data->sample_spec.channels) +            data->channel_map = data->sink->channel_map; +        else +            pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); +    } -    CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map)); -    CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels); +    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);      if (!data->volume_is_set)          pa_cvolume_reset(&data->volume, data->sample_spec.channels); -    CHECK_VALIDITY_RETURN_NULL(pa_cvolume_valid(&data->volume)); -    CHECK_VALIDITY_RETURN_NULL(data->volume.channels == 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); + +    if (!data->muted_is_set) +        data->muted = 0;      if (data->resample_method == PA_RESAMPLER_INVALID)          data->resample_method = core->resample_method; -    CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX); +    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);      if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {          pa_log_warn("Failed to create sink input: too many inputs per sink."); @@ -136,20 +147,27 @@ pa_sink_input* pa_sink_input_new(      if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||          !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) || -        !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) +        !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {          if (!(resampler = pa_resampler_new(                        core->mempool,                        &data->sample_spec, &data->channel_map,                        &data->sink->sample_spec, &data->sink->channel_map, -                      data->resample_method))) { +                      data->resample_method, +                      !!(flags & PA_SINK_INPUT_VARIABLE_RATE)))) {              pa_log_warn("Unsupported resampling operation.");              return NULL;          } -    i = pa_xnew(pa_sink_input, 1); -    i->ref = 1; -    i->state = PA_SINK_INPUT_DRAINED; +        data->resample_method = pa_resampler_get_method(resampler); +    } + +    i = pa_msgobject_new(pa_sink_input); +    i->parent.parent.free = sink_input_free; +    i->parent.process_msg = pa_sink_input_process_msg; + +    i->core = core; +    i->state = PA_SINK_INPUT_INIT;      i->flags = flags;      i->name = pa_xstrdup(data->name);      i->driver = pa_xstrdup(data->driver); @@ -157,105 +175,203 @@ pa_sink_input* pa_sink_input_new(      i->sink = data->sink;      i->client = data->client; +    i->resample_method = data->resample_method;      i->sample_spec = data->sample_spec;      i->channel_map = data->channel_map; +      i->volume = data->volume; +    i->muted = data->muted; + +    if (data->sync_base) { +        i->sync_next = data->sync_base->sync_next; +        i->sync_prev = data->sync_base; + +        if (data->sync_base->sync_next) +            data->sync_base->sync_next->sync_prev = i; +        data->sync_base->sync_next = i; +    } else +        i->sync_next = i->sync_prev = NULL;      i->peek = NULL;      i->drop = NULL;      i->kill = NULL;      i->get_latency = NULL; -    i->underrun = NULL; +    i->attach = NULL; +    i->detach = NULL; +    i->suspend = NULL;      i->userdata = NULL; -    i->move_silence = 0; - -    pa_memchunk_reset(&i->resampled_chunk); -    i->resampler = resampler; -    i->resample_method = data->resample_method; -    i->silence_memblock = NULL; - -    r = pa_idxset_put(core->sink_inputs, i, &i->index); -    assert(r == 0); -    r = pa_idxset_put(i->sink->inputs, i, NULL); -    assert(r == 0); - -    pa_log_info("created %u \"%s\" on %s with sample spec %s", +    i->thread_info.state = i->state; +    pa_atomic_store(&i->thread_info.drained, 1); +    i->thread_info.sample_spec = i->sample_spec; +    i->thread_info.silence_memblock = NULL; +    i->thread_info.move_silence = 0; +    pa_memchunk_reset(&i->thread_info.resampled_chunk); +    i->thread_info.resampler = resampler; +    i->thread_info.volume = i->volume; +    i->thread_info.muted = i->muted; +    i->thread_info.attached = FALSE; + +    pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); +    pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); + +    pa_log_info("Created input %u \"%s\" on %s with sample spec %s",                  i->index,                  i->name,                  i->sink->name,                  pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec)); -    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); - -    /* We do not call pa_sink_notify() here, because the virtual -     * functions have not yet been initialized */ +    /* Don't forget to call pa_sink_input_put! */      return i;  } -void pa_sink_input_disconnect(pa_sink_input *i) { -    assert(i); -    assert(i->state != PA_SINK_INPUT_DISCONNECTED); -    assert(i->sink); -    assert(i->sink->core); +static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { +    pa_assert(i); + +    if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) +        pa_assert_se(i->sink->n_corked -- >= 1); +    else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED) +        i->sink->n_corked++; + +    pa_sink_update_status(i->sink); +} + +static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { +    pa_sink_input *ssync; +    pa_assert(i); + +    if (state == PA_SINK_INPUT_DRAINED) +        state = PA_SINK_INPUT_RUNNING; + +    if (i->state == state) +        return 0; + +    if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) +        return -1; + +    update_n_corked(i, state); +    i->state = state; + +    for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) { +        update_n_corked(ssync, state); +        ssync->state = state; +    } +    for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) { +        update_n_corked(ssync, state); +        ssync->state = state; +    } + +    if (state != PA_SINK_INPUT_UNLINKED) +        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); + +    return 0; +} + +void pa_sink_input_unlink(pa_sink_input *i) { +    pa_bool_t linked; +    pa_assert(i); + +    /* See pa_sink_unlink() for a couple of comments how this function +     * works */ + +    pa_sink_input_ref(i); + +    linked = PA_SINK_INPUT_LINKED(i->state); + +    if (linked) +        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); + +    if (i->sync_prev) +        i->sync_prev->sync_next = i->sync_next; +    if (i->sync_next) +        i->sync_next->sync_prev = i->sync_prev; + +    i->sync_prev = i->sync_next = NULL;      pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); -    pa_idxset_remove_by_data(i->sink->inputs, i, NULL); +    if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) +        pa_sink_input_unref(i); -    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); -    i->sink = NULL; +    if (linked) { +        pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); +        sink_input_set_state(i, PA_SINK_INPUT_UNLINKED); +        pa_sink_update_status(i->sink); +    } else +        i->state = PA_SINK_INPUT_UNLINKED;      i->peek = NULL;      i->drop = NULL;      i->kill = NULL;      i->get_latency = NULL; -    i->underrun = NULL; +    i->attach = NULL; +    i->detach = NULL; +    i->suspend = NULL; + +    if (linked) { +        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); +        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); +    } -    i->state = PA_SINK_INPUT_DISCONNECTED; +    i->sink = NULL; +    pa_sink_input_unref(i);  } -static void sink_input_free(pa_sink_input* i) { -    assert(i); +static void sink_input_free(pa_object *o) { +    pa_sink_input* i = PA_SINK_INPUT(o); -    if (i->state != PA_SINK_INPUT_DISCONNECTED) -        pa_sink_input_disconnect(i); +    pa_assert(i); +    pa_assert(pa_sink_input_refcnt(i) == 0); -    pa_log_info("freed %u \"%s\"", i->index, i->name); +    if (PA_SINK_INPUT_LINKED(i->state)) +        pa_sink_input_unlink(i); -    if (i->resampled_chunk.memblock) -        pa_memblock_unref(i->resampled_chunk.memblock); +    pa_log_info("Freeing output %u \"%s\"", i->index, i->name); -    if (i->resampler) -        pa_resampler_free(i->resampler); +    pa_assert(!i->thread_info.attached); -    if (i->silence_memblock) -        pa_memblock_unref(i->silence_memblock); +    if (i->thread_info.resampled_chunk.memblock) +        pa_memblock_unref(i->thread_info.resampled_chunk.memblock); + +    if (i->thread_info.resampler) +        pa_resampler_free(i->thread_info.resampler); + +    if (i->thread_info.silence_memblock) +        pa_memblock_unref(i->thread_info.silence_memblock);      pa_xfree(i->name);      pa_xfree(i->driver);      pa_xfree(i);  } -void pa_sink_input_unref(pa_sink_input *i) { -    assert(i); -    assert(i->ref >= 1); +void pa_sink_input_put(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); -    if (!(--i->ref)) -        sink_input_free(i); -} +    pa_assert(i->state == PA_SINK_INPUT_INIT); +    pa_assert(i->peek); +    pa_assert(i->drop); -pa_sink_input* pa_sink_input_ref(pa_sink_input *i) { -    assert(i); -    assert(i->ref >= 1); +    i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; +    i->thread_info.volume = i->volume; +    i->thread_info.muted = i->muted; -    i->ref++; -    return i; +    if (i->state == PA_SINK_INPUT_CORKED) +        i->sink->n_corked++; + +    pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); +    pa_sink_update_status(i->sink); + +    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); +    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); + +    /* Please note that if you change something here, you have to +       change something in pa_sink_input_move() with the ghost stream +       registration too. */  }  void pa_sink_input_kill(pa_sink_input*i) { -    assert(i); -    assert(i->ref >= 1); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state));      if (i->kill)          i->kill(i); @@ -264,108 +380,127 @@ void pa_sink_input_kill(pa_sink_input*i) {  pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {      pa_usec_t r = 0; -    assert(i); -    assert(i->ref >= 1); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); + +    if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) +        r = 0;      if (i->get_latency)          r += i->get_latency(i); -    if (i->resampled_chunk.memblock) -        r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sink->sample_spec); - -    if (i->move_silence) -        r += pa_bytes_to_usec(i->move_silence, &i->sink->sample_spec); -      return r;  } -int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) { +/* Called from thread context */ +int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume) {      int ret = -1;      int do_volume_adj_here;      int volume_is_norm; +    size_t block_size_max; -    assert(i); -    assert(i->ref >= 1); -    assert(chunk); -    assert(volume); - -    pa_sink_input_ref(i); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); +    pa_assert(pa_frame_aligned(length, &i->sink->sample_spec)); +    pa_assert(chunk); +    pa_assert(volume); -    if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED) +    if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)          goto finish; -    assert(i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED); +    pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED); -    if (i->move_silence > 0) { +    /* Default buffer size */ +    if (length <= 0) +        length = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec); + +    /* Make sure the buffer fits in the mempool tile */ +    block_size_max = pa_mempool_block_size_max(i->sink->core->mempool); +    if (length > block_size_max) +        length = pa_frame_align(block_size_max, &i->sink->sample_spec); + +    if (i->thread_info.move_silence > 0) { +        size_t l;          /* We have just been moved and shall play some silence for a           * while until the old sink has drained its playback buffer */ -        if (!i->silence_memblock) -            i->silence_memblock = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, SILENCE_BUFFER_LENGTH); +        if (!i->thread_info.silence_memblock) +            i->thread_info.silence_memblock = pa_silence_memblock_new( +                    i->sink->core->mempool, +                    &i->sink->sample_spec, +                    pa_frame_align(SILENCE_BUFFER_LENGTH, &i->sink->sample_spec)); -        chunk->memblock = pa_memblock_ref(i->silence_memblock); +        chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock);          chunk->index = 0; -        chunk->length = i->move_silence < chunk->memblock->length ? i->move_silence : chunk->memblock->length; +        l = pa_memblock_get_length(chunk->memblock); +        chunk->length = i->thread_info.move_silence < l ? i->thread_info.move_silence : l;          ret = 0;          do_volume_adj_here = 1;          goto finish;      } -    if (!i->resampler) { -        do_volume_adj_here = 0; -        ret = i->peek(i, chunk); +    if (!i->thread_info.resampler) { +        do_volume_adj_here = 0; /* FIXME??? */ +        ret = i->peek(i, length, chunk);          goto finish;      }      do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); -    volume_is_norm = pa_cvolume_is_norm(&i->volume); +    volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted; -    while (!i->resampled_chunk.memblock) { +    while (!i->thread_info.resampled_chunk.memblock) {          pa_memchunk tchunk; -        size_t l; +        size_t l, rmbs; -        if ((ret = i->peek(i, &tchunk)) < 0) -            goto finish; +        l = pa_resampler_request(i->thread_info.resampler, length); -        assert(tchunk.length); +        if (l <= 0) +            l = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec); -        l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); +        rmbs = pa_resampler_max_block_size(i->thread_info.resampler); +        if (l > rmbs) +            l = rmbs; -        if (l > tchunk.length) -            l = tchunk.length; +        if ((ret = i->peek(i, l, &tchunk)) < 0) +            goto finish; + +        pa_assert(tchunk.length > 0); + +        if (tchunk.length > l) +            tchunk.length = l; -        i->drop(i, &tchunk, l); -        tchunk.length = l; +        i->drop(i, tchunk.length);          /* It might be necessary to adjust the volume here */          if (do_volume_adj_here && !volume_is_norm) {              pa_memchunk_make_writable(&tchunk, 0); -            pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume); + +            if (i->thread_info.muted) +                pa_silence_memchunk(&tchunk, &i->thread_info.sample_spec); +            else +                pa_volume_memchunk(&tchunk, &i->thread_info.sample_spec, &i->thread_info.volume);          } -        pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); +        pa_resampler_run(i->thread_info.resampler, &tchunk, &i->thread_info.resampled_chunk);          pa_memblock_unref(tchunk.memblock);      } -    assert(i->resampled_chunk.memblock); -    assert(i->resampled_chunk.length); +    pa_assert(i->thread_info.resampled_chunk.memblock); +    pa_assert(i->thread_info.resampled_chunk.length > 0); -    *chunk = i->resampled_chunk; -    pa_memblock_ref(i->resampled_chunk.memblock); +    *chunk = i->thread_info.resampled_chunk; +    pa_memblock_ref(i->thread_info.resampled_chunk.memblock);      ret = 0;  finish: -    if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING && i->underrun) -        i->underrun(i); -      if (ret >= 0) -        i->state = PA_SINK_INPUT_RUNNING; -    else if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING) -        i->state = PA_SINK_INPUT_DRAINED; +        pa_atomic_store(&i->thread_info.drained, 0); +    else if (ret < 0) +        pa_atomic_store(&i->thread_info.drained, 1);      if (ret >= 0) {          /* Let's see if we had to apply the volume adjustment @@ -374,120 +509,179 @@ finish:          if (do_volume_adj_here)              /* We had different channel maps, so we already did the adjustment */              pa_cvolume_reset(volume, i->sink->sample_spec.channels); -        else +        else if (i->thread_info.muted)              /* We've both the same channel map, so let's have the sink do the adjustment for us*/ -            *volume = i->volume; +            pa_cvolume_mute(volume, i->sink->sample_spec.channels); +        else +            *volume = i->thread_info.volume;      } -    pa_sink_input_unref(i); -      return ret;  } -void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { -    assert(i); -    assert(i->ref >= 1); -    assert(length > 0); +/* Called from thread context */ +void pa_sink_input_drop(pa_sink_input *i, size_t length) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); +    pa_assert(pa_frame_aligned(length, &i->sink->sample_spec)); +    pa_assert(length > 0); -    if (i->move_silence > 0) { +    if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED) +        return; -        if (chunk) { +    if (i->thread_info.move_silence > 0) { -            if (chunk->memblock != i->silence_memblock || -                chunk->index != 0 || -                (chunk->memblock && (chunk->length != (i->silence_memblock->length < i->move_silence ? i->silence_memblock->length : i->move_silence)))) -                return; +        if (i->thread_info.move_silence >= length) { +            i->thread_info.move_silence -= length; +            length = 0; +        } else { +            length -= i->thread_info.move_silence; +            i->thread_info.move_silence = 0; +        } +        if (i->thread_info.move_silence <= 0) { +            if (i->thread_info.silence_memblock) { +                pa_memblock_unref(i->thread_info.silence_memblock); +                i->thread_info.silence_memblock = NULL; +            }          } -        assert(i->move_silence >= length); +        if (length <= 0) +            return; +    } + +    if (i->thread_info.resampled_chunk.memblock) { +        size_t l = length; + +        if (l > i->thread_info.resampled_chunk.length) +            l = i->thread_info.resampled_chunk.length; -        i->move_silence -= length; +        i->thread_info.resampled_chunk.index += l; +        i->thread_info.resampled_chunk.length -= l; -        if (i->move_silence <= 0) { -            assert(i->silence_memblock); -            pa_memblock_unref(i->silence_memblock); -            i->silence_memblock = NULL; +        if (i->thread_info.resampled_chunk.length <= 0) { +            pa_memblock_unref(i->thread_info.resampled_chunk.memblock); +            pa_memchunk_reset(&i->thread_info.resampled_chunk);          } -        return; +        length -= l;      } -    if (!i->resampler) { -        if (i->drop) -            i->drop(i, chunk, length); -        return; -    } +    if (length > 0) { + +        if (i->thread_info.resampler) { +            /* So, we have a resampler. To avoid discontinuities we +             * have to actually read all data that could be read and +             * pass it through the resampler. */ + +            while (length > 0) { +                pa_memchunk chunk; +                pa_cvolume volume; + +                if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) { +                    size_t l; + +                    pa_memblock_unref(chunk.memblock); + +                    l = chunk.length; +                    if (l > length) +                        l = length; + +                    pa_sink_input_drop(i, l); +                    length -= l; -    assert(i->resampled_chunk.memblock); -    assert(i->resampled_chunk.length >= length); +                } else { +                    size_t l; -    i->resampled_chunk.index += length; -    i->resampled_chunk.length -= length; +                    l = pa_resampler_request(i->thread_info.resampler, length); + +                    /* Hmmm, peeking failed, so let's at least drop +                     * the right amount of data */ + +                    if (l > 0) +                        if (i->drop) +                            i->drop(i, l); + +                    break; +                } +            } -    if (i->resampled_chunk.length <= 0) { -        pa_memblock_unref(i->resampled_chunk.memblock); -        i->resampled_chunk.memblock = NULL; -        i->resampled_chunk.index = i->resampled_chunk.length = 0; +        } else { + +            /* We have no resampler, hence let's just drop the data */ + +            if (i->drop) +                i->drop(i, length); +        }      }  }  void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { -    assert(i); -    assert(i->ref >= 1); -    assert(i->sink); -    assert(i->sink->core); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state));      if (pa_cvolume_equal(&i->volume, volume))          return;      i->volume = *volume; + +    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree);      pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);  } -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) { -    assert(i); -    assert(i->ref >= 1); +const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state));      return &i->volume;  } -void pa_sink_input_cork(pa_sink_input *i, int b) { -    int n; +void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { +    pa_assert(i); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); -    assert(i); -    assert(i->ref >= 1); +    if (!i->muted == !mute) +        return; -    assert(i->state != PA_SINK_INPUT_DISCONNECTED); +    i->muted = mute; -    n = i->state == PA_SINK_INPUT_CORKED && !b; +    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); +    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +} + +int pa_sink_input_get_mute(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); + +    return !!i->muted; +} -    if (b) -        i->state = PA_SINK_INPUT_CORKED; -    else if (i->state == PA_SINK_INPUT_CORKED) -        i->state = PA_SINK_INPUT_DRAINED; +void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); -    if (n) -        pa_sink_notify(i->sink); +    sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);  } -void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { -    assert(i); -    assert(i->resampler); -    assert(i->ref >= 1); +int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); +    pa_return_val_if_fail(i->thread_info.resampler, -1);      if (i->sample_spec.rate == rate) -        return; +        return 0;      i->sample_spec.rate = rate; -    pa_resampler_set_input_rate(i->resampler, rate); + +    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);      pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    return 0;  }  void pa_sink_input_set_name(pa_sink_input *i, const char *name) { -    assert(i); -    assert(i->ref >= 1); +    pa_sink_input_assert_ref(i);      if (!i->name && !name)          return; @@ -498,47 +692,56 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {      pa_xfree(i->name);      i->name = pa_xstrdup(name); -    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    if (PA_SINK_INPUT_LINKED(i->state)) { +        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED], i); +        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    }  }  pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { -    assert(i); -    assert(i->ref >= 1); +    pa_sink_input_assert_ref(i); -    if (!i->resampler) -        return i->resample_method; - -    return pa_resampler_get_method(i->resampler); +    return i->resample_method;  }  int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { -    pa_resampler *new_resampler = NULL; -    pa_memblockq *buffer = NULL; +    pa_resampler *new_resampler;      pa_sink *origin; +    pa_usec_t silence_usec = 0; +    pa_sink_input_move_info info; -    assert(i); -    assert(dest); +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->state)); +    pa_sink_assert_ref(dest);      origin = i->sink;      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."); +        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;      } -    if (i->resampler && +    if (i->thread_info.resampler &&          pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&          pa_channel_map_equal(&origin->channel_map, &dest->channel_map))          /* Try to reuse the old resampler if possible */ -        new_resampler = i->resampler; +        new_resampler = i->thread_info.resampler;      else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) || -        !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) || -        !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) { +             !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) || +             !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {          /* Okey, we need a new resampler for the new sink */ @@ -546,20 +749,26 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {                        dest->core->mempool,                        &i->sample_spec, &i->channel_map,                        &dest->sample_spec, &dest->channel_map, -                      i->resample_method))) { +                      i->resample_method, +                      !!(i->flags & PA_SINK_INPUT_VARIABLE_RATE)))) {              pa_log_warn("Unsupported resampling operation.");              return -1;          } -    } +    } else +        new_resampler = NULL; + +    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], i); + +    memset(&info, 0, sizeof(info)); +    info.sink_input = i;      if (!immediately) {          pa_usec_t old_latency, new_latency; -        pa_usec_t silence_usec = 0; - -        buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);          /* Let's do a little bit of Voodoo for compensating latency -         * differences */ +         * differences. We assume that the accuracy for our +         * estimations is still good enough, even though we do these +         * operations non-atomic. */          old_latency = pa_sink_get_latency(origin);          new_latency = pa_sink_get_latency(dest); @@ -576,8 +785,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {              silence_usec = old_latency - new_latency;          } else { -            size_t l; -            int volume_is_norm;              /* The latency of new sink is larger than the latency of               * the old sink. Therefore we have to precompute a little @@ -585,87 +792,164 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {               * sink, until we can play the first sample on the new               * sink.*/ -            l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); +            info.buffer_bytes = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); +        } -            volume_is_norm = pa_cvolume_is_norm(&i->volume); +        /* Okey, let's move it */ -            while (l > 0) { -                pa_memchunk chunk; -                pa_cvolume volume; -                size_t n; +        if (info.buffer_bytes > 0) { -                if (pa_sink_input_peek(i, &chunk, &volume) < 0) -                    break; +            info.ghost_sink_input = pa_memblockq_sink_input_new( +                    origin, +                    "Ghost Stream", +                    &origin->sample_spec, +                    &origin->channel_map, +                    NULL, +                    NULL); -                n = chunk.length > l ? l : chunk.length; -                pa_sink_input_drop(i, &chunk, n); -                chunk.length = n; +            info.ghost_sink_input->thread_info.state = info.ghost_sink_input->state = PA_SINK_INPUT_RUNNING; +            info.ghost_sink_input->thread_info.volume = info.ghost_sink_input->volume; +            info.ghost_sink_input->thread_info.muted = info.ghost_sink_input->muted; -                if (!volume_is_norm) { -                    pa_memchunk_make_writable(&chunk, 0); -                    pa_volume_memchunk(&chunk, &origin->sample_spec, &volume); -                } - -                if (pa_memblockq_push(buffer, &chunk) < 0) { -                    pa_memblock_unref(chunk.memblock); -                    break; -                } - -                pa_memblock_unref(chunk.memblock); -                l -= n; -            } +            info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);          } +    } -        if (i->resampled_chunk.memblock) { - -            /* There is still some data left in the already resampled -             * memory block. Hence, let's output it on the old sink -             * and sleep so long on the new sink */ +    pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, &info, 0, NULL); -            pa_memblockq_push(buffer, &i->resampled_chunk); -            silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec); -        } +    if (info.ghost_sink_input) { +        /* Basically, do what pa_sink_input_put() does ...*/ -        /* Calculate the new sleeping time */ -        i->move_silence = pa_usec_to_bytes( -                pa_bytes_to_usec(i->move_silence, &i->sample_spec) + -                silence_usec, -                &i->sample_spec); +        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, info.ghost_sink_input->index); +        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], info.ghost_sink_input); +        pa_sink_input_unref(info.ghost_sink_input);      } -    /* Okey, let's move it */      pa_idxset_remove_by_data(origin->inputs, i, NULL);      pa_idxset_put(dest->inputs, i, NULL);      i->sink = dest; +    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) { +        pa_assert_se(origin->n_corked-- >= 1); +        dest->n_corked++; +    } +      /* Replace resampler */ -    if (new_resampler != i->resampler) { -        if (i->resampler) -            pa_resampler_free(i->resampler); -        i->resampler = new_resampler; +    if (new_resampler != i->thread_info.resampler) { +        if (i->thread_info.resampler) +            pa_resampler_free(i->thread_info.resampler); +        i->thread_info.resampler = new_resampler;          /* if the resampler changed, the silence memblock is           * probably invalid now, too */ -        if (i->silence_memblock) { -            pa_memblock_unref(i->silence_memblock); -            i->silence_memblock = NULL; +        if (i->thread_info.silence_memblock) { +            pa_memblock_unref(i->thread_info.silence_memblock); +            i->thread_info.silence_memblock = NULL;          }      }      /* Dump already resampled data */ -    if (i->resampled_chunk.memblock) { -        pa_memblock_unref(i->resampled_chunk.memblock); -        i->resampled_chunk.memblock = NULL; -        i->resampled_chunk.index = i->resampled_chunk.length = 0; +    if (i->thread_info.resampled_chunk.memblock) { +        /* Hmm, this data has already been added to the ghost queue, presumably, hence let's sleep a little bit longer */ +        silence_usec += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &origin->sample_spec); +        pa_memblock_unref(i->thread_info.resampled_chunk.memblock); +        pa_memchunk_reset(&i->thread_info.resampled_chunk);      } +    /* Calculate the new sleeping time */ +    if (immediately) +        i->thread_info.move_silence = 0; +    else +        i->thread_info.move_silence = pa_usec_to_bytes( +                pa_bytes_to_usec(i->thread_info.move_silence, &origin->sample_spec) + +                silence_usec, +                &dest->sample_spec); + +    pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); + +    pa_sink_update_status(origin); +    pa_sink_update_status(dest); + +    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i); + +    pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name); +      /* Notify everyone */      pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -    pa_sink_notify(i->sink); - -    /* Ok, no let's feed the precomputed buffer to the old sink */ -    if (buffer) -        pa_play_memblockq(origin, "Ghost Stream", &origin->sample_spec, &origin->channel_map, buffer, NULL);      return 0;  } + +/* Called from thread context */ +int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_sink_input *i = PA_SINK_INPUT(o); + +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + +    switch (code) { +        case PA_SINK_INPUT_MESSAGE_SET_VOLUME: +            i->thread_info.volume = *((pa_cvolume*) userdata); +            return 0; + +        case PA_SINK_INPUT_MESSAGE_SET_MUTE: +            i->thread_info.muted = PA_PTR_TO_UINT(userdata); +            return 0; + +        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { +            pa_usec_t *r = userdata; + +            if (i->thread_info.resampled_chunk.memblock) +                *r += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &i->sink->sample_spec); + +            if (i->thread_info.move_silence) +                *r += pa_bytes_to_usec(i->thread_info.move_silence, &i->sink->sample_spec); + +            return 0; +        } + +        case PA_SINK_INPUT_MESSAGE_SET_RATE: + +            i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); +            pa_resampler_set_input_rate(i->thread_info.resampler, PA_PTR_TO_UINT(userdata)); + +            return 0; + +        case PA_SINK_INPUT_MESSAGE_SET_STATE: { +            pa_sink_input *ssync; + +            if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && +                (i->thread_info.state != PA_SINK_INPUT_DRAINED) && (i->thread_info.state != PA_SINK_INPUT_RUNNING)) +                pa_atomic_store(&i->thread_info.drained, 1); + +            i->thread_info.state = PA_PTR_TO_UINT(userdata); + +            for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) { +                if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && +                    (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING)) +                    pa_atomic_store(&ssync->thread_info.drained, 1); +                ssync->thread_info.state = PA_PTR_TO_UINT(userdata); +            } + +            for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) { +                if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && +                    (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING)) +                    pa_atomic_store(&ssync->thread_info.drained, 1); +                ssync->thread_info.state = PA_PTR_TO_UINT(userdata); +            } + +            return 0; +        } +    } + +    return -1; +} + +pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED) +        return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING; + +    return i->state; +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 51d9ec78..3f8e2039 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -1,5 +1,5 @@ -#ifndef foosinkinputhfoo -#define foosinkinputhfoo +#ifndef foopulsesinkinputhfoo +#define foopulsesinkinputhfoo  /* $Id$ */ @@ -39,20 +39,32 @@ typedef struct pa_sink_input pa_sink_input;  #include <pulsecore/core.h>  typedef enum pa_sink_input_state { -    PA_SINK_INPUT_RUNNING,      /*< The stream is alive and kicking */ +    PA_SINK_INPUT_INIT,         /*< The stream is not active yet, because pa_sink_put() has not been called yet */      PA_SINK_INPUT_DRAINED,      /*< The stream stopped playing because there was no data to play */ +    PA_SINK_INPUT_RUNNING,      /*< The stream is alive and kicking */      PA_SINK_INPUT_CORKED,       /*< The stream was corked on user request */ -    PA_SINK_INPUT_DISCONNECTED  /*< The stream is dead */ +    PA_SINK_INPUT_UNLINKED      /*< The stream is dead */  } pa_sink_input_state_t; +static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) { +    return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED; +} +  typedef enum pa_sink_input_flags {      PA_SINK_INPUT_VARIABLE_RATE = 1, -    PA_SINK_INPUT_NO_HOOKS = 2 +    PA_SINK_INPUT_DONT_MOVE = 2, +    PA_SINK_INPUT_START_CORKED = 4  } pa_sink_input_flags_t;  struct pa_sink_input { -    int ref; +    pa_msgobject parent; +      uint32_t index; +    pa_core *core; + +    /* Please note that this state should only be read with +     * pa_sink_input_get_state(). That function will transparently +     * merge the thread_info.drained value in. */      pa_sink_input_state_t state;      pa_sink_input_flags_t flags; @@ -64,27 +76,87 @@ struct pa_sink_input {      pa_sample_spec sample_spec;      pa_channel_map channel_map; -    pa_cvolume volume; -    /* Some silence to play before the actual data. This is used to -     * compensate for latency differences when moving a sink input -     * "hot" between sinks. */ -    size_t move_silence; +    pa_sink_input *sync_prev, *sync_next; -    int (*peek) (pa_sink_input *i, pa_memchunk *chunk); -    void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length); +    pa_cvolume volume; +    pa_bool_t muted; + +    /* Returns the chunk of audio data (but doesn't drop it +     * yet!). Returns -1 on failure. Called from IO thread context. If +     * data needs to be generated from scratch then please in the +     * specified length. This is an optimization only. If less data is +     * available, it's fine to return a smaller block. If more data is +     * already ready, it is better to return the full block.*/ +    int (*peek) (pa_sink_input *i, size_t length, pa_memchunk *chunk); + +    /* Drops the specified number of bytes, usually called right after +     * peek(), but not necessarily. Called from IO thread context. */ +    void (*drop) (pa_sink_input *i, size_t length); + +    /* If non-NULL this function is called when the input is first +     * connected to a sink or when the rtpoll/asyncmsgq fields +     * change. You usually don't need to implement this function +     * unless you rewrite a sink that is piggy-backed onto +     * another. Called from IO thread context */ +    void (*attach) (pa_sink_input *i);           /* may be NULL */ + +    /* If non-NULL this function is called when the output is +     * disconnected from its sink. Called from IO thread context */ +    void (*detach) (pa_sink_input *i);           /* may be NULL */ + +    /* If non-NULL called whenever the the sink this input is attached +     * to suspends or resumes. Called from main context */ +    void (*suspend) (pa_sink_input *i, int b);   /* may be NULL */ + +    /* Supposed to unlink and destroy this stream. Called from main +     * context. */      void (*kill) (pa_sink_input *i);             /* may be NULL */ + +    /* Return the current latency (i.e. length of bufferd audio) of +    this stream. Called from main context. If NULL a +    PA_SINK_INPUT_MESSAGE_GET_LATENCY message is sent to the IO thread +    instead. */      pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ -    void (*underrun) (pa_sink_input *i);         /* may be NULL */ -    void *userdata; +    pa_resample_method_t resample_method; -    pa_memchunk resampled_chunk; -    pa_resampler *resampler;                     /* may be NULL */ +    struct { +        pa_sink_input_state_t state; +        pa_atomic_t drained; -    pa_resample_method_t resample_method; +        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ + +        pa_sample_spec sample_spec; + +        pa_memchunk resampled_chunk; +        pa_resampler *resampler;                     /* may be NULL */ + +        /* Some silence to play before the actual data. This is used to +         * compensate for latency differences when moving a sink input +         * "hot" between sinks. */ +        size_t move_silence; +        pa_memblock *silence_memblock;               /* may be NULL */ + +        pa_sink_input *sync_prev, *sync_next; + +        pa_cvolume volume; +        pa_bool_t muted; +    } thread_info; + +    void *userdata; +}; + +PA_DECLARE_CLASS(pa_sink_input); +#define PA_SINK_INPUT(o) pa_sink_input_cast(o) -    pa_memblock *silence_memblock;               /* may be NULL */ +enum { +    PA_SINK_INPUT_MESSAGE_SET_VOLUME, +    PA_SINK_INPUT_MESSAGE_SET_MUTE, +    PA_SINK_INPUT_MESSAGE_GET_LATENCY, +    PA_SINK_INPUT_MESSAGE_SET_RATE, +    PA_SINK_INPUT_MESSAGE_SET_STATE, +    PA_SINK_INPUT_MESSAGE_MAX  };  typedef struct pa_sink_input_new_data { @@ -95,50 +167,71 @@ typedef struct pa_sink_input_new_data {      pa_sink *sink;      pa_sample_spec sample_spec; -    int sample_spec_is_set; +    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    int channel_map_is_set; +    pa_bool_t channel_map_is_set; +      pa_cvolume volume; -    int volume_is_set; +    pa_bool_t volume_is_set; +    pa_bool_t muted; +    pa_bool_t muted_is_set;      pa_resample_method_t resample_method; + +    pa_sink_input *sync_base;  } pa_sink_input_new_data;  pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);  void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);  void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);  void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); + +/* To be called by the implementing module only */  pa_sink_input* pa_sink_input_new(          pa_core *core,          pa_sink_input_new_data *data,          pa_sink_input_flags_t flags); -void pa_sink_input_unref(pa_sink_input* i); -pa_sink_input* pa_sink_input_ref(pa_sink_input* i); +void pa_sink_input_put(pa_sink_input *i); +void pa_sink_input_unlink(pa_sink_input* i); -/* To be called by the implementing module only */ -void pa_sink_input_disconnect(pa_sink_input* i); +void pa_sink_input_set_name(pa_sink_input *i, const char *name); -/* External code may request disconnection with this funcion */ +/* Callable by everyone */ + +/* External code may request disconnection with this function */  void pa_sink_input_kill(pa_sink_input*i);  pa_usec_t pa_sink_input_get_latency(pa_sink_input *i); -int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume); -void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length); -  void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i); +const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); +void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute); +int pa_sink_input_get_mute(pa_sink_input *i); -void pa_sink_input_cork(pa_sink_input *i, int b); +void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b); -void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); - -void pa_sink_input_set_name(pa_sink_input *i, const char *name); +int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);  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, int immediately); +pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); + +/* To be used exclusively by the sink driver thread */ + +int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume); +void pa_sink_input_drop(pa_sink_input *i, size_t length); +int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); + +typedef struct pa_sink_input_move_info { +    pa_sink_input *sink_input; +    pa_sink_input *ghost_sink_input; +    pa_memblockq *buffer; +    size_t buffer_bytes; +} pa_sink_input_move_info; +  #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 9588c2c3..dccb34cc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -27,7 +27,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <stdio.h> @@ -41,16 +40,18 @@  #include <pulsecore/sample-util.h>  #include <pulsecore/core-subscribe.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/play-memblockq.h>  #include "sink.h"  #define MAX_MIX_CHANNELS 32 +#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE) +#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12) -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ -    return NULL; \ -} while (0) +static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject); + +static void sink_free(pa_object *s);  pa_sink* pa_sink_new(          pa_core *core, @@ -63,68 +64,71 @@ pa_sink* pa_sink_new(      pa_sink *s;      char *n = NULL;      char st[256]; -    int r;      pa_channel_map tmap; -    assert(core); -    assert(name); -    assert(spec); +    pa_assert(core); +    pa_assert(name); +    pa_assert(spec); -    CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); +    pa_return_null_if_fail(pa_sample_spec_valid(spec));      if (!map)          map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); -    CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); -    CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); -    CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); -    CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); +    pa_return_null_if_fail(map && pa_channel_map_valid(map)); +    pa_return_null_if_fail(map->channels == spec->channels); +    pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); +    pa_return_null_if_fail(name && pa_utf8_valid(name) && *name); -    s = pa_xnew(pa_sink, 1); +    s = pa_msgobject_new(pa_sink);      if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {          pa_xfree(s);          return NULL;      } -    s->ref = 1; +    s->parent.parent.free = sink_free; +    s->parent.process_msg = pa_sink_process_msg; +      s->core = core; -    s->state = PA_SINK_RUNNING; +    s->state = PA_SINK_INIT; +    s->flags = 0;      s->name = pa_xstrdup(name);      s->description = NULL;      s->driver = pa_xstrdup(driver); -    s->owner = NULL; +    s->module = NULL;      s->sample_spec = *spec;      s->channel_map = *map;      s->inputs = pa_idxset_new(NULL, NULL); +    s->n_corked = 0; -    pa_cvolume_reset(&s->sw_volume, spec->channels); -    pa_cvolume_reset(&s->hw_volume, spec->channels); -    s->sw_muted = 0; -    s->hw_muted = 0; - -    s->is_hardware = 0; +    pa_cvolume_reset(&s->volume, spec->channels); +    s->muted = FALSE; +    s->refresh_volume = s->refresh_mute = FALSE;      s->get_latency = NULL; -    s->notify = NULL; -    s->set_hw_volume = NULL; -    s->get_hw_volume = NULL; -    s->set_hw_mute = NULL; -    s->get_hw_mute = NULL; +    s->set_volume = NULL; +    s->get_volume = NULL; +    s->set_mute = NULL; +    s->get_mute = NULL; +    s->set_state = NULL;      s->userdata = NULL; -    r = pa_idxset_put(core->sinks, s, &s->index); -    assert(s->index != PA_IDXSET_INVALID && r >= 0); +    s->asyncmsgq = NULL; +    s->rtpoll = NULL; +    s->silence = NULL; + +    pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);      pa_sample_spec_snprint(st, sizeof(st), spec); -    pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); +    pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);      n = pa_sprintf_malloc("%s.monitor", name);      if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) -        pa_log_warn("failed to create monitor source."); +        pa_log_warn("Failed to create monitor source.");      else {          char *d;          s->monitor_source->monitor_of = s; @@ -135,51 +139,124 @@ pa_sink* pa_sink_new(      pa_xfree(n); -    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); +    s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); +    s->thread_info.soft_volume = s->volume; +    s->thread_info.soft_muted = s->muted; +    s->thread_info.state = s->state;      return s;  } -void pa_sink_disconnect(pa_sink* s) { +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +    int ret; + +    pa_assert(s); + +    if (s->state == state) +        return 0; + +    if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || +        (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) { +        pa_sink_input *i; +        uint32_t idx; + +        /* We're suspending or resuming, tell everyone about it */ + +        for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) +            if (i->suspend) +                i->suspend(i, state == PA_SINK_SUSPENDED); +    } + +    if (s->set_state) +        if ((ret = s->set_state(s, state)) < 0) +            return -1; + +    if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) +        return -1; + +    s->state = state; + +    if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */ +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s); +    return 0; +} + +void pa_sink_put(pa_sink* s) { +    pa_sink_assert_ref(s); + +    pa_assert(s->state == PA_SINK_INIT); +    pa_assert(s->asyncmsgq); +    pa_assert(s->rtpoll); + +    pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); + +    pa_source_put(s->monitor_source); + +    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); +    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], s); +} + +void pa_sink_unlink(pa_sink* s) { +    pa_bool_t linked;      pa_sink_input *i, *j = NULL; -    assert(s); -    assert(s->state == PA_SINK_RUNNING); +    pa_assert(s); + +    /* Please note that pa_sink_unlink() does more than simply +     * reversing pa_sink_put(). It also undoes the registrations +     * already done in pa_sink_new()! */ -    s->state = PA_SINK_DISCONNECTED; -    pa_namereg_unregister(s->core, s->name); +    /* All operations here shall be idempotent, i.e. pa_sink_unlink() +     * may be called multiple times on the same sink without bad +     * effects. */ -    pa_hook_fire(&s->core->hook_sink_disconnect, s); +    linked = PA_SINK_LINKED(s->state); + +    if (linked) +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); + +    if (s->state != PA_SINK_UNLINKED) +        pa_namereg_unregister(s->core, s->name); +    pa_idxset_remove_by_data(s->core->sinks, s, NULL);      while ((i = pa_idxset_first(s->inputs, NULL))) { -        assert(i != j); +        pa_assert(i != j);          pa_sink_input_kill(i);          j = i;      } -    if (s->monitor_source) -        pa_source_disconnect(s->monitor_source); - -    pa_idxset_remove_by_data(s->core->sinks, s, NULL); +    if (linked) +        sink_set_state(s, PA_SINK_UNLINKED); +    else +        s->state = PA_SINK_UNLINKED;      s->get_latency = NULL; -    s->notify = NULL; -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; -    s->set_hw_mute = NULL; -    s->get_hw_mute = NULL; +    s->get_volume = NULL; +    s->set_volume = NULL; +    s->set_mute = NULL; +    s->get_mute = NULL; +    s->set_state = NULL; -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +    if (s->monitor_source) +        pa_source_unlink(s->monitor_source); + +    if (linked) { +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s); +    }  } -static void sink_free(pa_sink *s) { -    assert(s); -    assert(!s->ref); +static void sink_free(pa_object *o) { +    pa_sink *s = PA_SINK(o); +    pa_sink_input *i; + +    pa_assert(s); +    pa_assert(pa_sink_refcnt(s) == 0); -    if (s->state != PA_SINK_DISCONNECTED) -        pa_sink_disconnect(s); +    if (PA_SINK_LINKED(s->state)) +        pa_sink_unlink(s); -    pa_log_info("freed %u \"%s\"", s->index, s->name); +    pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);      if (s->monitor_source) {          pa_source_unref(s->monitor_source); @@ -188,108 +265,192 @@ static void sink_free(pa_sink *s) {      pa_idxset_free(s->inputs, NULL, NULL); +    while ((i = pa_hashmap_steal_first(s->thread_info.inputs))) +        pa_sink_input_unref(i); + +    pa_hashmap_free(s->thread_info.inputs, NULL, NULL); + +    if (s->silence) +        pa_memblock_unref(s->silence); +      pa_xfree(s->name);      pa_xfree(s->description);      pa_xfree(s->driver);      pa_xfree(s);  } -void pa_sink_unref(pa_sink*s) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { +    pa_sink_assert_ref(s); +    pa_assert(q); + +    s->asyncmsgq = q; -    if (!(--s->ref)) -        sink_free(s); +    if (s->monitor_source) +        pa_source_set_asyncmsgq(s->monitor_source, q);  } -pa_sink* pa_sink_ref(pa_sink *s) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { +    pa_sink_assert_ref(s); +    pa_assert(p); -    s->ref++; -    return s; +    s->rtpoll = p; +    if (s->monitor_source) +        pa_source_set_rtpoll(s->monitor_source, p); +} + +int pa_sink_update_status(pa_sink*s) { +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    if (s->state == PA_SINK_SUSPENDED) +        return 0; + +    return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); +} + +int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    if (suspend) +        return sink_set_state(s, PA_SINK_SUSPENDED); +    else +        return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);  } -void pa_sink_notify(pa_sink*s) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_ping(pa_sink *s) { +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); -    if (s->notify) -        s->notify(s); +    pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL);  } -static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { -    uint32_t idx = PA_IDXSET_INVALID; +static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) {      pa_sink_input *i;      unsigned n = 0; +    void *state = NULL; -    assert(s); -    assert(s->ref >= 1); -    assert(info); +    pa_sink_assert_ref(s); +    pa_assert(info); -    for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { -        /* Increase ref counter, to make sure that this input doesn't -         * vanish while we still need it */ -        pa_sink_input_ref(i); +    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) { +        pa_sink_input_assert_ref(i); -        if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { -            pa_sink_input_unref(i); +        if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0)              continue; -        } -        info->userdata = i; +        info->userdata = pa_sink_input_ref(i); -        assert(info->chunk.memblock); -        assert(info->chunk.memblock->data); -        assert(info->chunk.length); +        pa_assert(info->chunk.memblock); +        pa_assert(info->chunk.length > 0);          info++; -        maxinfo--;          n++; +        maxinfo--;      }      return n;  } -static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { -    assert(s); -    assert(s->ref >= 1); -    assert(info); +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) { +    pa_sink_input *i; +    void *state = NULL; +    unsigned p = 0; +    unsigned n_unreffed = 0; + +    pa_sink_assert_ref(s); + +    /* We optimize for the case where the order of the inputs has not changed */ + +    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { +        unsigned j; +        pa_mix_info* m; + +        pa_sink_input_assert_ref(i); -    for (; maxinfo > 0; maxinfo--, info++) { -        pa_sink_input *i = info->userdata; +        m = NULL; -        assert(i); -        assert(info->chunk.memblock); +        /* Let's try to find the matching entry info the pa_mix_info array */ +        for (j = 0; j < n; j ++) { + +            if (info[p].userdata == i) { +                m = info + p; +                break; +            } + +            p++; +            if (p >= n) +                p = 0; +        }          /* Drop read data */ -        pa_sink_input_drop(i, &info->chunk, length); -        pa_memblock_unref(info->chunk.memblock); +        pa_sink_input_drop(i, length); -        /* Decrease ref counter */ -        pa_sink_input_unref(i); -        info->userdata = NULL; +        if (m) { +            pa_sink_input_unref(m->userdata); +            m->userdata = NULL; +            if (m->chunk.memblock) +                pa_memblock_unref(m->chunk.memblock); +            pa_memchunk_reset(&m->chunk); + +            n_unreffed += 1; +        } +    } + +    /* Now drop references to entries that are included in the +     * pa_mix_info array but don't exist anymore */ + +    if (n_unreffed < n) { +        for (; n > 0; info++, n--) { +            if (info->userdata) +                pa_sink_input_unref(info->userdata); +            if (info->chunk.memblock) +                pa_memblock_unref(info->chunk.memblock); +        }      }  } -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { +void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {      pa_mix_info info[MAX_MIX_CHANNELS];      unsigned n; -    int r = -1; +    size_t block_size_max; -    assert(s); -    assert(s->ref >= 1); -    assert(length); -    assert(result); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_OPENED(s->thread_info.state)); +    pa_assert(pa_frame_aligned(length, &s->sample_spec)); +    pa_assert(result);      pa_sink_ref(s); -    n = fill_mix_info(s, info, MAX_MIX_CHANNELS); +    if (length <= 0) +        length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec); + +    block_size_max = pa_mempool_block_size_max(s->core->mempool); +    if (length > block_size_max) +        length = pa_frame_align(block_size_max, &s->sample_spec); + +    pa_assert(length > 0); + +    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, length, info, MAX_MIX_CHANNELS) : 0; + +    if (n == 0) { + +        if (length > SILENCE_BUFFER_LENGTH) +            length = pa_frame_align(SILENCE_BUFFER_LENGTH, &s->sample_spec); -    if (n <= 0) -        goto finish; +        pa_assert(length > 0); -    if (n == 1) { +        if (!s->silence || pa_memblock_get_length(s->silence) < length) { +            if (s->silence) +                pa_memblock_unref(s->silence); +            s->silence = pa_silence_memblock_new(s->core->mempool, &s->sample_spec, length); +        } + +        result->memblock = pa_memblock_ref(s->silence); +        result->length = length; +        result->index = 0; + +    } else if (n == 1) {          pa_cvolume volume;          *result = info[0].chunk; @@ -298,105 +459,112 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {          if (result->length > length)              result->length = length; -        pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); +        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); -        if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { +        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {              pa_memchunk_make_writable(result, 0); -            if (s->sw_muted) +            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))                  pa_silence_memchunk(result, &s->sample_spec);              else                  pa_volume_memchunk(result, &s->sample_spec, &volume);          }      } else { +        void *ptr;          result->memblock = pa_memblock_new(s->core->mempool, length); -        assert(result->memblock); -/*          pa_log("mixing %i", n);  */ +        ptr = pa_memblock_acquire(result->memblock); +        result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted); +        pa_memblock_release(result->memblock); -        result->length = pa_mix(info, n, result->memblock->data, length, -            &s->sample_spec, &s->sw_volume, s->sw_muted);          result->index = 0;      } -    inputs_drop(s, info, n, result->length); +    if (s->thread_info.state == PA_SINK_RUNNING) +        inputs_drop(s, info, n, result->length); -    if (s->monitor_source) +    if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))          pa_source_post(s->monitor_source, result); -    r = 0; - -finish:      pa_sink_unref(s); - -    return r;  } -int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { +void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {      pa_mix_info info[MAX_MIX_CHANNELS];      unsigned n; -    int r = -1; -    assert(s); -    assert(s->ref >= 1); -    assert(target); -    assert(target->memblock); -    assert(target->length); -    assert(target->memblock->data); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_OPENED(s->thread_info.state)); +    pa_assert(target); +    pa_assert(target->memblock); +    pa_assert(target->length > 0); +    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));      pa_sink_ref(s); -    n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - -    if (n <= 0) -        goto finish; - -    if (n == 1) { -        pa_cvolume volume; +    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0; +    if (n == 0) { +        pa_silence_memchunk(target, &s->sample_spec); +    } else if (n == 1) {          if (target->length > info[0].chunk.length)              target->length = info[0].chunk.length; -        memcpy((uint8_t*) target->memblock->data + target->index, -               (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, -               target->length); +        if (s->thread_info.soft_muted) +            pa_silence_memchunk(target, &s->sample_spec); +        else { +            void *src, *ptr; +            pa_cvolume volume; -        pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); +            ptr = pa_memblock_acquire(target->memblock); +            src = pa_memblock_acquire(info[0].chunk.memblock); + +            memcpy((uint8_t*) ptr + target->index, +                   (uint8_t*) src + info[0].chunk.index, +                   target->length); + +            pa_memblock_release(target->memblock); +            pa_memblock_release(info[0].chunk.memblock); + +            pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); + +            if (!pa_cvolume_is_norm(&volume)) +                pa_volume_memchunk(target, &s->sample_spec, &volume); +        } + +    } else { +        void *ptr; + +        ptr = pa_memblock_acquire(target->memblock); -        if (s->sw_muted) -            pa_silence_memchunk(target, &s->sample_spec); -        else if (!pa_cvolume_is_norm(&volume)) -            pa_volume_memchunk(target, &s->sample_spec, &volume); -    } else          target->length = pa_mix(info, n, -                                (uint8_t*) target->memblock->data + target->index, +                                (uint8_t*) ptr + target->index,                                  target->length,                                  &s->sample_spec, -                                &s->sw_volume, -                                s->sw_muted); +                                &s->thread_info.soft_volume, +                                s->thread_info.soft_muted); -    inputs_drop(s, info, n, target->length); +        pa_memblock_release(target->memblock); +    } -    if (s->monitor_source) -        pa_source_post(s->monitor_source, target); +    if (s->thread_info.state == PA_SINK_RUNNING) +        inputs_drop(s, info, n, target->length); -    r = 0; +    if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) +        pa_source_post(s->monitor_source, target); -finish:      pa_sink_unref(s); - -    return r;  }  void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {      pa_memchunk chunk;      size_t l, d; -    assert(s); -    assert(s->ref >= 1); -    assert(target); -    assert(target->memblock); -    assert(target->length); -    assert(target->memblock->data); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_OPENED(s->thread_info.state)); +    pa_assert(target); +    pa_assert(target->memblock); +    pa_assert(target->length > 0); +    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));      pa_sink_ref(s); @@ -407,140 +575,177 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {          chunk.index += d;          chunk.length -= d; -        if (pa_sink_render_into(s, &chunk) < 0) -            break; +        pa_sink_render_into(s, &chunk);          d += chunk.length;          l -= chunk.length;      } -    if (l > 0) { -        chunk = *target; -        chunk.index += d; -        chunk.length -= d; -        pa_silence_memchunk(&chunk, &s->sample_spec); -    } -      pa_sink_unref(s);  }  void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { -    assert(s); -    assert(s->ref >= 1); -    assert(length); -    assert(result); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_OPENED(s->thread_info.state)); +    pa_assert(length > 0); +    pa_assert(pa_frame_aligned(length, &s->sample_spec)); +    pa_assert(result);      /*** This needs optimization ***/ -    result->memblock = pa_memblock_new(s->core->mempool, result->length = length);      result->index = 0; +    result->length = length; +    result->memblock = pa_memblock_new(s->core->mempool, length);      pa_sink_render_into_full(s, result);  } +void pa_sink_skip(pa_sink *s, size_t length) { +    pa_sink_input *i; +    void *state = NULL; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_OPENED(s->thread_info.state)); +    pa_assert(length > 0); +    pa_assert(pa_frame_aligned(length, &s->sample_spec)); + +    if (pa_source_used_by(s->monitor_source)) { +        pa_memchunk chunk; + +        /* If something is connected to our monitor source, we have to +         * pass valid data to it */ + +        while (length > 0) { +            pa_sink_render(s, length, &chunk); +            pa_memblock_unref(chunk.memblock); + +            pa_assert(chunk.length <= length); +            length -= chunk.length; +        } + +    } else { +        /* Ok, noone cares about the rendered data, so let's not even render it */ + +        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { +            pa_sink_input_assert_ref(i); +            pa_sink_input_drop(i, length); +        } +    } +} +  pa_usec_t pa_sink_get_latency(pa_sink *s) { -    assert(s); -    assert(s->ref >= 1); +    pa_usec_t usec = 0; -    if (!s->get_latency) +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    if (!PA_SINK_OPENED(s->state)) +        return 0; + +    if (s->get_latency) +        return s->get_latency(s); + +    if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)          return 0; -    return s->get_latency(s); +    return usec;  } -void pa_sink_set_owner(pa_sink *s, pa_module *m) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { +    int changed; -    if (s->owner == m) -        return; +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); +    pa_assert(volume); -    s->owner = m; +    changed = !pa_cvolume_equal(volume, &s->volume); +    s->volume = *volume; -    if (s->monitor_source) -        pa_source_set_owner(s->monitor_source, m); +    if (s->set_volume && s->set_volume(s) < 0) +        s->set_volume = NULL; -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    if (!s->set_volume) +        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); + +    if (changed) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  } -void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { -    pa_cvolume *v; +const pa_cvolume *pa_sink_get_volume(pa_sink *s) { +    struct pa_cvolume old_volume; -    assert(s); -    assert(s->ref >= 1); -    assert(volume); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); -    if (m == PA_MIXER_HARDWARE && s->set_hw_volume) -        v = &s->hw_volume; -    else -        v = &s->sw_volume; +    old_volume = s->volume; -    if (pa_cvolume_equal(v, volume)) -        return; +    if (s->get_volume && s->get_volume(s) < 0) +        s->get_volume = NULL; -    *v = *volume; +    if (!s->get_volume && s->refresh_volume) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); -    if (v == &s->hw_volume) -        if (s->set_hw_volume(s) < 0) -            s->sw_volume =  *volume; +    if (!pa_cvolume_equal(&old_volume, &s->volume)) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    return &s->volume;  } -const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { +    int changed; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); -    if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { +    changed = s->muted != mute; +    s->muted = mute; -        if (s->get_hw_volume) -            s->get_hw_volume(s); +    if (s->set_mute && s->set_mute(s) < 0) +        s->set_mute = NULL; -        return &s->hw_volume; -    } else -        return &s->sw_volume; +    if (!s->set_mute) +        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); + +    if (changed) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  } -void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) { -    int *t; +pa_bool_t pa_sink_get_mute(pa_sink *s) { +    pa_bool_t old_muted; -    assert(s); -    assert(s->ref >= 1); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); -    if (m == PA_MIXER_HARDWARE && s->set_hw_mute) -        t = &s->hw_muted; -    else -        t = &s->sw_muted; +    old_muted = s->muted; -    if (!!*t == !!mute) -        return; +    if (s->get_mute && s->get_mute(s) < 0) +        s->get_mute = NULL; -    *t = !!mute; +    if (!s->get_mute && s->refresh_mute) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); -    if (t == &s->hw_muted) -        if (s->set_hw_mute(s) < 0) -            s->sw_muted = !!mute; +    if (old_muted != s->muted) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    return s->muted;  } -int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { -    assert(s); -    assert(s->ref >= 1); +void pa_sink_set_module(pa_sink *s, pa_module *m) { +    pa_sink_assert_ref(s); -    if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { +    if (s->module == m) +        return; + +    s->module = m; -        if (s->get_hw_mute) -            s->get_hw_mute(s); +    if (s->monitor_source) +        pa_source_set_module(s->monitor_source, m); -        return s->hw_muted; -    } else -        return s->sw_muted; +    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  }  void pa_sink_set_description(pa_sink *s, const char *description) { -    assert(s); -    assert(s->ref >= 1); +    pa_sink_assert_ref(s);      if (!description && !s->description)          return; @@ -559,19 +764,298 @@ void pa_sink_set_description(pa_sink *s, const char *description) {          pa_xfree(n);      } -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    if (PA_SINK_LINKED(s->state)) { +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], s); +    }  } -unsigned pa_sink_used_by(pa_sink *s) { +unsigned pa_sink_linked_by(pa_sink *s) {      unsigned ret; -    assert(s); -    assert(s->ref >= 1); +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state));      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()! */ +      if (s->monitor_source) -        ret += pa_source_used_by(s->monitor_source); +        ret += pa_source_linked_by(s->monitor_source);      return ret;  } + +unsigned pa_sink_used_by(pa_sink *s) { +    unsigned ret; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    ret = pa_idxset_size(s->inputs); +    pa_assert(ret >= s->n_corked); +    ret -= s->n_corked; + +    /* Streams connected to our monitor source do not matter for +     * pa_sink_used_by()!.*/ + +    return ret; +} + +int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_sink *s = PA_SINK(o); +    pa_sink_assert_ref(s); +    pa_assert(s->thread_info.state != PA_SINK_UNLINKED); + +    switch ((pa_sink_message_t) code) { + +        case PA_SINK_MESSAGE_ADD_INPUT: { +            pa_sink_input *i = PA_SINK_INPUT(userdata); +            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); + +            /* Since the caller sleeps in pa_sink_input_put(), we can +             * safely access data outside of thread_info even though +             * it is mutable */ + +            if ((i->thread_info.sync_prev = i->sync_prev)) { +                pa_assert(i->sink == i->thread_info.sync_prev->sink); +                pa_assert(i->sync_prev->sync_next == i); +                i->thread_info.sync_prev->thread_info.sync_next = i; +            } + +            if ((i->thread_info.sync_next = i->sync_next)) { +                pa_assert(i->sink == i->thread_info.sync_next->sink); +                pa_assert(i->sync_next->sync_prev == i); +                i->thread_info.sync_next->thread_info.sync_prev = i; +            } + +            pa_assert(!i->thread_info.attached); +            i->thread_info.attached = TRUE; + +            if (i->attach) +                i->attach(i); + +            /* If you change anything here, make sure to change the +             * ghost sink input handling a few lines down at +             * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + +            return 0; +        } + +        case PA_SINK_MESSAGE_REMOVE_INPUT: { +            pa_sink_input *i = PA_SINK_INPUT(userdata); + +            /* If you change anything here, make sure to change the +             * sink input handling a few lines down at +             * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + +            if (i->detach) +                i->detach(i); + +            pa_assert(i->thread_info.attached); +            i->thread_info.attached = FALSE; + +            /* Since the caller sleeps in pa_sink_input_unlink(), +             * we can safely access data outside of thread_info even +             * though it is mutable */ + +            pa_assert(!i->thread_info.sync_prev); +            pa_assert(!i->thread_info.sync_next); + +            if (i->thread_info.sync_prev) { +                i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next; +                i->thread_info.sync_prev = NULL; +            } + +            if (i->thread_info.sync_next) { +                i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev; +                i->thread_info.sync_next = NULL; +            } + +            if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index))) +                pa_sink_input_unref(i); + +            return 0; +        } + +        case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { +            pa_sink_input_move_info *info = userdata; +            int volume_is_norm; + +            /* We don't support moving synchronized streams. */ +            pa_assert(!info->sink_input->sync_prev); +            pa_assert(!info->sink_input->sync_next); +            pa_assert(!info->sink_input->thread_info.sync_next); +            pa_assert(!info->sink_input->thread_info.sync_prev); + +            if (info->sink_input->detach) +                info->sink_input->detach(info->sink_input); + +            pa_assert(info->sink_input->thread_info.attached); +            info->sink_input->thread_info.attached = FALSE; + +            if (info->ghost_sink_input) { +                pa_assert(info->buffer_bytes > 0); +                pa_assert(info->buffer); + +                volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume); + +                pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes); + +                while (info->buffer_bytes > 0) { +                    pa_memchunk memchunk; +                    pa_cvolume volume; +                    size_t n; + +                    if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0) +                        break; + +                    n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length; +                    pa_sink_input_drop(info->sink_input, n); +                    memchunk.length = n; + +                    if (!volume_is_norm) { +                        pa_memchunk_make_writable(&memchunk, 0); +                        pa_volume_memchunk(&memchunk, &s->sample_spec, &volume); +                    } + +                    if (pa_memblockq_push(info->buffer, &memchunk) < 0) { +                        pa_memblock_unref(memchunk.memblock); +                        break; +                    } + +                    pa_memblock_unref(memchunk.memblock); +                    info->buffer_bytes -= n; +                } + +                /* Add the remaining already resampled chunk to the buffer */ +                if (info->sink_input->thread_info.resampled_chunk.memblock) +                    pa_memblockq_push(info->buffer, &info->sink_input->thread_info.resampled_chunk); + +                pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer); + +                pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer)); +            } + +            /* Let's remove the sink input ...*/ +            if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(info->sink_input->index))) +                pa_sink_input_unref(info->sink_input); + +            /* .. and add the ghost sink input instead */ +            if (info->ghost_sink_input) { +                pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input)); +                info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL; + +                pa_assert(!info->ghost_sink_input->thread_info.attached); +                info->ghost_sink_input->thread_info.attached = TRUE; + +                if (info->ghost_sink_input->attach) +                    info->ghost_sink_input->attach(info->ghost_sink_input); +            } + +            return 0; +        } + +        case PA_SINK_MESSAGE_SET_VOLUME: +            s->thread_info.soft_volume = *((pa_cvolume*) userdata); +            return 0; + +        case PA_SINK_MESSAGE_SET_MUTE: +            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); +            return 0; + +        case PA_SINK_MESSAGE_GET_VOLUME: +            *((pa_cvolume*) userdata) = s->thread_info.soft_volume; +            return 0; + +        case PA_SINK_MESSAGE_GET_MUTE: +            *((pa_bool_t*) userdata) = s->thread_info.soft_muted; +            return 0; + +        case PA_SINK_MESSAGE_PING: +            return 0; + +        case PA_SINK_MESSAGE_SET_STATE: + +            s->thread_info.state = PA_PTR_TO_UINT(userdata); +            return 0; + +        case PA_SINK_MESSAGE_DETACH: + +            /* We're detaching all our input streams so that the +             * asyncmsgq and rtpoll fields can be changed without +             * problems */ +            pa_sink_detach_within_thread(s); +            break; + +        case PA_SINK_MESSAGE_ATTACH: + +            /* Reattach all streams */ +            pa_sink_attach_within_thread(s); +            break; + +        case PA_SINK_MESSAGE_GET_LATENCY: +        case PA_SINK_MESSAGE_MAX: +            ; +    } + +    return -1; +} + +int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { +    pa_sink *sink; +    uint32_t idx; +    int ret = 0; + +    pa_core_assert_ref(c); + +    for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) +        ret -= pa_sink_suspend(sink, suspend) < 0; + +    return ret; +} + +void pa_sink_detach(pa_sink *s) { +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL); +} + +void pa_sink_attach(pa_sink *s) { +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->state)); + +    pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL); +} + +void pa_sink_detach_within_thread(pa_sink *s) { +    pa_sink_input *i; +    void *state = NULL; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->thread_info.state)); + +    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) +        if (i->detach) +            i->detach(i); + +    if (s->monitor_source) +        pa_source_detach_within_thread(s->monitor_source); +} + +void pa_sink_attach_within_thread(pa_sink *s) { +    pa_sink_input *i; +    void *state = NULL; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_LINKED(s->thread_info.state)); + +    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) +        if (i->attach) +            i->attach(i); + +    if (s->monitor_source) +        pa_source_attach_within_thread(s->monitor_source); +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index ef73f67d..e9969309 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -1,5 +1,5 @@ -#ifndef foosinkhfoo -#define foosinkhfoo +#ifndef foopulsesinkhfoo +#define foopulsesinkhfoo  /* $Id$ */ @@ -25,87 +25,164 @@    USA.  ***/ -#include <inttypes.h> -  typedef struct pa_sink pa_sink; +#include <inttypes.h> +  #include <pulse/sample.h>  #include <pulse/channelmap.h>  #include <pulse/volume.h> +  #include <pulsecore/core-def.h>  #include <pulsecore/core.h>  #include <pulsecore/idxset.h>  #include <pulsecore/source.h>  #include <pulsecore/module.h> +#include <pulsecore/refcnt.h> +#include <pulsecore/msgobject.h> +#include <pulsecore/rtpoll.h>  #define PA_MAX_INPUTS_PER_SINK 32  typedef enum pa_sink_state { +    PA_SINK_INIT,      PA_SINK_RUNNING, -    PA_SINK_DISCONNECTED +    PA_SINK_SUSPENDED, +    PA_SINK_IDLE, +    PA_SINK_UNLINKED  } pa_sink_state_t; +static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) { +    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; +} + +static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) { +    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; +} +  struct pa_sink { -    int ref; +    pa_msgobject parent; +      uint32_t index;      pa_core *core;      pa_sink_state_t state; +    pa_sink_flags_t flags;      char *name;      char *description, *driver;            /* may be NULL */ -    int is_hardware; -    pa_module *owner;                      /* may be NULL */ +    pa_module *module;                      /* may be NULL */      pa_sample_spec sample_spec;      pa_channel_map channel_map;      pa_idxset *inputs; -    pa_source *monitor_source;             /* may be NULL */ - -    pa_cvolume hw_volume, sw_volume; -    int hw_muted, sw_muted; - -    void (*notify)(pa_sink*sink);          /* may be NULL */ -    pa_usec_t (*get_latency)(pa_sink *s);  /* dito */ -    int (*set_hw_volume)(pa_sink *s);      /* dito */ -    int (*get_hw_volume)(pa_sink *s);      /* dito */ -    int (*set_hw_mute)(pa_sink *s);        /* dito */ -    int (*get_hw_mute)(pa_sink *s);        /* dito */ +    unsigned n_corked; +    pa_source *monitor_source; + +    pa_cvolume volume; +    pa_bool_t muted; +    pa_bool_t refresh_volume; +    pa_bool_t refresh_mute; + +    int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ +    int (*set_volume)(pa_sink *s);           /* dito */ +    int (*get_volume)(pa_sink *s);           /* dito */ +    int (*get_mute)(pa_sink *s);             /* dito */ +    int (*set_mute)(pa_sink *s);             /* dito */ +    pa_usec_t (*get_latency)(pa_sink *s);    /* dito */ + +    pa_asyncmsgq *asyncmsgq; +    pa_rtpoll *rtpoll; + +    /* Contains copies of the above data so that the real-time worker +     * thread can work without access locking */ +    struct { +        pa_sink_state_t state; +        pa_hashmap *inputs; +        pa_cvolume soft_volume; +        pa_bool_t soft_muted; +    } thread_info; + +    pa_memblock *silence;      void *userdata;  }; +PA_DECLARE_CLASS(pa_sink); +#define PA_SINK(s) (pa_sink_cast(s)) + +typedef enum pa_sink_message { +    PA_SINK_MESSAGE_ADD_INPUT, +    PA_SINK_MESSAGE_REMOVE_INPUT, +    PA_SINK_MESSAGE_GET_VOLUME, +    PA_SINK_MESSAGE_SET_VOLUME, +    PA_SINK_MESSAGE_GET_MUTE, +    PA_SINK_MESSAGE_SET_MUTE, +    PA_SINK_MESSAGE_GET_LATENCY, +    PA_SINK_MESSAGE_SET_STATE, +    PA_SINK_MESSAGE_PING, +    PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, +    PA_SINK_MESSAGE_ATTACH, +    PA_SINK_MESSAGE_DETACH, +    PA_SINK_MESSAGE_MAX +} pa_sink_message_t; + +/* To be called exclusively by the sink driver, from main context */ +  pa_sink* pa_sink_new( -    pa_core *core, -    const char *driver, -    const char *name, -    int namereg_fail, -    const pa_sample_spec *spec, -    const pa_channel_map *map); - -void pa_sink_disconnect(pa_sink* s); -void pa_sink_unref(pa_sink*s); -pa_sink* pa_sink_ref(pa_sink *s); - -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); -void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); -int pa_sink_render_into(pa_sink*s, pa_memchunk *target); -void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); +        pa_core *core, +        const char *driver, +        const char *name, +        int namereg_fail, +        const pa_sample_spec *spec, +        const pa_channel_map *map); + +void pa_sink_put(pa_sink *s); +void pa_sink_unlink(pa_sink* s); + +void pa_sink_set_module(pa_sink *sink, pa_module *m); +void pa_sink_set_description(pa_sink *s, const char *description); +void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q); +void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p); + +void pa_sink_detach(pa_sink *s); +void pa_sink_attach(pa_sink *s); + +/* May be called by everyone, from main context */  pa_usec_t pa_sink_get_latency(pa_sink *s); -void pa_sink_notify(pa_sink*s); +int pa_sink_update_status(pa_sink*s); +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_owner(pa_sink *sink, pa_module *m); +/* Sends a ping message to the sink thread, to make it wake up and + * check for data to process even if there is no real message is + * sent */ +void pa_sink_ping(pa_sink *s); -void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume); -const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m); -void pa_sink_set_mute(pa_sink *sink, pa_mixer_t m, int mute); -int pa_sink_get_mute(pa_sink *sink, pa_mixer_t m); +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_mute(pa_sink *sink, pa_bool_t mute); +pa_bool_t pa_sink_get_mute(pa_sink *sink); -void pa_sink_set_description(pa_sink *s, const char *description); +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 */ +#define pa_sink_get_state(s) ((s)->state) + +/* To be called exclusively by the sink driver, from IO context */ + +void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); +void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); +void pa_sink_render_into(pa_sink*s, pa_memchunk *target); +void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); + +void pa_sink_skip(pa_sink *s, size_t length); + +int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); -unsigned pa_sink_used_by(pa_sink *s); +void pa_sink_attach_within_thread(pa_sink *s); +void pa_sink_detach_within_thread(pa_sink *s);  #endif diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c index d3d7538e..8d4c6fa7 100644 --- a/src/pulsecore/sioman.c +++ b/src/pulsecore/sioman.c @@ -25,21 +25,17 @@  #include <config.h>  #endif -#include <assert.h> +#include <pulsecore/macro.h> +#include <pulsecore/atomic.h>  #include "sioman.h" -static int stdio_inuse = 0; +static pa_atomic_t stdio_inuse = PA_ATOMIC_INIT(0);  int pa_stdio_acquire(void) { -    if (stdio_inuse) -        return -1; - -    stdio_inuse = 1; -    return 0; +    return pa_atomic_cmpxchg(&stdio_inuse, 0, 1) ? 0 : -1;  }  void pa_stdio_release(void) { -    assert(stdio_inuse); -    stdio_inuse = 0; +    pa_assert_se(pa_atomic_cmpxchg(&stdio_inuse, 1, 0));  } diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c index b99c8025..5b5bc5ca 100644 --- a/src/pulsecore/socket-client.c +++ b/src/pulsecore/socket-client.c @@ -1,25 +1,25 @@  /* $Id$ */  /*** -  This file is part of PulseAudio. +    This file is part of PulseAudio. -  Copyright 2004-2006 Lennart Poettering -  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB +    Copyright 2004-2006 Lennart Poettering +    Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB -  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 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. +    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. +    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 @@ -32,7 +32,6 @@  #include <stdio.h>  #include <errno.h>  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #ifdef HAVE_SYS_SOCKET_H @@ -55,23 +54,24 @@  #include <asyncns.h>  #endif -#include "winsock.h" -  #include <pulse/timeval.h>  #include <pulse/xmalloc.h> +#include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/socket-util.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h>  #include <pulsecore/parseaddr.h> +#include <pulsecore/macro.h> +#include <pulsecore/refcnt.h>  #include "socket-client.h"  #define CONNECT_TIMEOUT 5  struct pa_socket_client { -    int ref; +    PA_REFCNT_DECLARE;      pa_mainloop_api *mainloop;      int fd;      pa_io_event *io_event; @@ -89,10 +89,10 @@ struct pa_socket_client {  static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {      pa_socket_client *c; -    assert(m); +    pa_assert(m); -    c = pa_xmalloc(sizeof(pa_socket_client)); -    c->ref = 1; +    c = pa_xnew(pa_socket_client, 1); +    PA_REFCNT_INIT(c);      c->mainloop = m;      c->fd = -1;      c->io_event = NULL; @@ -112,7 +112,7 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {  }  static void free_events(pa_socket_client *c) { -    assert(c); +    pa_assert(c);      if (c->io_event) {          c->mainloop->io_free(c->io_event); @@ -134,7 +134,10 @@ static void do_call(pa_socket_client *c) {      pa_iochannel *io = NULL;      int error;      socklen_t lerror; -    assert(c && c->callback); + +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(c->callback);      pa_socket_client_ref(c); @@ -153,13 +156,13 @@ static void do_call(pa_socket_client *c) {      }      if (error != 0) { -        pa_log_debug("connect(): %s", pa_cstrerror(errno)); +        pa_log_debug("connect(): %s", pa_cstrerror(error));          errno = error;          goto finish;      }      io = pa_iochannel_new(c->mainloop, c->fd, c->fd); -    assert(io); +    pa_assert(io);  finish:      if (!io && c->fd >= 0) @@ -168,7 +171,7 @@ finish:      free_events(c); -    assert(c->callback); +    pa_assert(c->callback);      c->callback(c, io, c->userdata);      pa_socket_client_unref(c); @@ -176,21 +179,36 @@ finish:  static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {      pa_socket_client *c = userdata; -    assert(m && c && c->defer_event == e); + +    pa_assert(m); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(c->defer_event == e); +      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) {      pa_socket_client *c = userdata; -    assert(m && c && c->io_event == e && fd >= 0); + +    pa_assert(m); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(c->io_event == e); +    pa_assert(fd >= 0); +      do_call(c);  }  static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {      int r; -    assert(c && sa && len); -    pa_make_nonblock_fd(c->fd); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(sa); +    pa_assert(len > 0); + +    pa_make_fd_nonblock(c->fd);      if ((r = connect(c->fd, sa, len)) < 0) {  #ifdef OS_IS_WIN32 @@ -203,19 +221,18 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t              return -1;          } -        c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c); -        assert(c->io_event); -    } else { -        c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c); -        assert(c->defer_event); -    } +        pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c)); +    } else +        pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c));      return 0;  }  pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {      struct sockaddr_in sa; -    assert(m && port > 0); + +    pa_assert(m); +    pa_assert(port > 0);      memset(&sa, 0, sizeof(sa));      sa.sin_family = AF_INET; @@ -229,7 +246,9 @@ pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address  pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {      struct sockaddr_un sa; -    assert(m && filename); + +    pa_assert(m); +    pa_assert(filename);      memset(&sa, 0, sizeof(sa));      sa.sun_family = AF_UNIX; @@ -248,9 +267,9 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file  #endif /* HAVE_SYS_UN_H */  static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) { -    assert(c); -    assert(sa); -    assert(salen); +    pa_assert(c); +    pa_assert(sa); +    pa_assert(salen);      switch (sa->sa_family) {          case AF_UNIX: @@ -274,11 +293,11 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size          return -1;      } -    pa_fd_set_cloexec(c->fd, 1); +    pa_make_fd_cloexec(c->fd);      if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) -        pa_socket_tcp_low_delay(c->fd); +        pa_make_tcp_socket_low_delay(c->fd);      else -        pa_socket_low_delay(c->fd); +        pa_make_socket_low_delay(c->fd);      if (do_connect(c, sa, salen) < 0)          return -1; @@ -288,9 +307,12 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size  pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {      pa_socket_client *c; -    assert(m && sa); -    c = pa_socket_client_new(m); -    assert(c); + +    pa_assert(m); +    pa_assert(sa); +    pa_assert(salen > 0); + +    pa_assert_se(c = pa_socket_client_new(m));      if (sockaddr_prepare(c, sa, salen) < 0)          goto fail; @@ -300,12 +322,11 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct  fail:      pa_socket_client_unref(c);      return NULL; -  }  static void socket_client_free(pa_socket_client *c) { -    assert(c && c->mainloop); - +    pa_assert(c); +    pa_assert(c->mainloop);      free_events(c); @@ -325,20 +346,25 @@ static void socket_client_free(pa_socket_client *c) {  }  void pa_socket_client_unref(pa_socket_client *c) { -    assert(c && c->ref >= 1); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); -    if (!(--(c->ref))) +    if (PA_REFCNT_DEC(c) <= 0)          socket_client_free(c);  }  pa_socket_client* pa_socket_client_ref(pa_socket_client *c) { -    assert(c && c->ref >= 1); -    c->ref++; +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    PA_REFCNT_INC(c);      return c;  }  void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) { -    assert(c); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +      c->callback = on_connection;      c->userdata = userdata;  } @@ -346,6 +372,10 @@ void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa  pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {      struct sockaddr_in6 sa; +    pa_assert(m); +    pa_assert(address); +    pa_assert(port > 0); +      memset(&sa, 0, sizeof(sa));      sa.sin6_family = AF_INET6;      sa.sin6_port = htons(port); @@ -360,7 +390,12 @@ static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED      pa_socket_client *c = userdata;      struct addrinfo *res = NULL;      int ret; -    assert(m && c && c->asyncns_io_event == e && fd >= 0); + +    pa_assert(m); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +    pa_assert(c->asyncns_io_event == e); +    pa_assert(fd >= 0);      if (asyncns_wait(c->asyncns, 0) < 0)          goto fail; @@ -397,10 +432,11 @@ fail:  static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {      pa_socket_client *c = userdata; -    assert(m); -    assert(e); -    assert(tv); -    assert(c); + +    pa_assert(m); +    pa_assert(e); +    pa_assert(tv); +    pa_assert(c);      if (c->fd >= 0) {          pa_close(c->fd); @@ -413,8 +449,8 @@ static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeva  static void start_timeout(pa_socket_client *c) {      struct timeval tv; -    assert(c); -    assert(!c->timeout_event); +    pa_assert(c); +    pa_assert(!c->timeout_event);      pa_gettimeofday(&tv);      pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000); @@ -424,7 +460,9 @@ static void start_timeout(pa_socket_client *c) {  pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {      pa_socket_client *c = NULL;      pa_parsed_address a; -    assert(m && name); + +    pa_assert(m); +    pa_assert(name);      if (pa_parse_address(name, &a) < 0)          return NULL; @@ -435,7 +473,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam      switch (a.type) {          case PA_PARSED_ADDRESS_UNIX:              if ((c = pa_socket_client_new_unix(m, a.path_or_host))) -            	start_timeout(c); +                start_timeout(c);              break;          case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */ @@ -445,7 +483,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam              struct addrinfo hints;              char port[12]; -            snprintf(port, sizeof(port), "%u", (unsigned) a.port); +            pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);              memset(&hints, 0, sizeof(hints));              hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC); @@ -462,7 +500,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam                  c->asyncns = asyncns;                  c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);                  c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints); -                assert(c->asyncns_query); +                pa_assert(c->asyncns_query);                  start_timeout(c);              }  #else /* HAVE_LIBASYNCNS */ @@ -479,7 +517,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam                  if (res->ai_addr) {                      if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))                          start_timeout(c); -				} +                }                  freeaddrinfo(res);  #else /* HAVE_GETADDRINFO */ @@ -507,7 +545,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam                  s.sin_port = htons(a.port);                  if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s)))) -                	start_timeout(c); +                    start_timeout(c);  #endif /* HAVE_GETADDRINFO */              }  #endif /* HAVE_LIBASYNCNS */ @@ -524,6 +562,8 @@ finish:     local. "local" means UNIX socket or TCP socket on localhost. Other     local IP addresses are not considered local. */  int pa_socket_client_is_local(pa_socket_client *c) { -    assert(c); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); +      return c->local;  } diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c index b5a6dc31..162a1aac 100644 --- a/src/pulsecore/socket-server.c +++ b/src/pulsecore/socket-server.c @@ -27,7 +27,6 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <errno.h>  #include <string.h>  #include <sys/types.h> @@ -72,12 +71,14 @@  #include <pulsecore/socket-util.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include <pulsecore/core-error.h> +#include <pulsecore/refcnt.h>  #include "socket-server.h"  struct pa_socket_server { -    int ref; +    PA_REFCNT_DECLARE;      int fd;      char *filename;      char *tcpwrap_service; @@ -94,7 +95,14 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U      pa_socket_server *s = userdata;      pa_iochannel *io;      int nfd; -    assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd); + +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(s->mainloop == mainloop); +    pa_assert(s->io_event == e); +    pa_assert(e); +    pa_assert(fd >= 0); +    pa_assert(fd == s->fd);      pa_socket_server_ref(s); @@ -103,7 +111,7 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U          goto finish;      } -    pa_fd_set_cloexec(nfd, 1); +    pa_make_fd_cloexec(nfd);      if (!s->on_connection) {          pa_close(nfd); @@ -129,12 +137,11 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U      /* There should be a check for socket type here */      if (s->type == SOCKET_SERVER_IPV4) -        pa_socket_tcp_low_delay(fd); +        pa_make_tcp_socket_low_delay(fd);      else -        pa_socket_low_delay(fd); +        pa_make_socket_low_delay(fd); -    io = pa_iochannel_new(s->mainloop, nfd, nfd); -    assert(io); +    pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));      s->on_connection(s, io, s->userdata);  finish: @@ -143,10 +150,12 @@ finish:  pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {      pa_socket_server *s; -    assert(m && fd >= 0); -    s = pa_xmalloc(sizeof(pa_socket_server)); -    s->ref = 1; +    pa_assert(m); +    pa_assert(fd >= 0); + +    s = pa_xnew(pa_socket_server, 1); +    PA_REFCNT_INIT(s);      s->fd = fd;      s->filename = NULL;      s->on_connection = NULL; @@ -154,8 +163,7 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {      s->tcpwrap_service = NULL;      s->mainloop = m; -    s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s); -    assert(s->io_event); +    pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));      s->type = SOCKET_SERVER_GENERIC; @@ -163,8 +171,10 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {  }  pa_socket_server* pa_socket_server_ref(pa_socket_server *s) { -    assert(s && s->ref >= 1); -    s->ref++; +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); + +    PA_REFCNT_INC(s);      return s;  } @@ -175,20 +185,21 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file      struct sockaddr_un sa;      pa_socket_server *s; -    assert(m && filename); +    pa_assert(m); +    pa_assert(filename);      if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {          pa_log("socket(): %s", pa_cstrerror(errno));          goto fail;      } -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(fd);      sa.sun_family = AF_UNIX;      strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);      sa.sun_path[sizeof(sa.sun_path) - 1] = 0; -    pa_socket_low_delay(fd); +    pa_make_socket_low_delay(fd);      if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {          pa_log("bind(): %s", pa_cstrerror(errno)); @@ -206,8 +217,7 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file          goto fail;      } -    s = pa_socket_server_new(m, fd); -    assert(s); +    pa_assert_se(s = pa_socket_server_new(m, fd));      s->filename = pa_xstrdup(filename);      s->type = SOCKET_SERVER_UNIX; @@ -235,21 +245,22 @@ pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address      struct sockaddr_in sa;      int on = 1; -    assert(m && port); +    pa_assert(m); +    pa_assert(port);      if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {          pa_log("socket(PF_INET): %s", pa_cstrerror(errno));          goto fail;      } -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(fd);  #ifdef SO_REUSEADDR      if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)          pa_log("setsockopt(): %s", pa_cstrerror(errno));  #endif -    pa_socket_tcp_low_delay(fd); +    pa_make_tcp_socket_low_delay(fd);      memset(&sa, 0, sizeof(sa));      sa.sin_family = AF_INET; @@ -286,14 +297,15 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad      struct sockaddr_in6 sa;      int on = 1; -    assert(m && port); +    pa_assert(m); +    pa_assert(port > 0);      if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {          pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));          goto fail;      } -    pa_fd_set_cloexec(fd, 1); +    pa_make_fd_cloexec(fd);  #ifdef IPV6_V6ONLY      if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) @@ -305,7 +317,7 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad          pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));  #endif -    pa_socket_tcp_low_delay(fd); +    pa_make_tcp_socket_low_delay(fd);      memset(&sa, 0, sizeof(sa));      sa.sin6_family = AF_INET6; @@ -337,29 +349,29 @@ fail:  }  pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) { -    assert(m); -    assert(port > 0); +    pa_assert(m); +    pa_assert(port > 0);      return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);  }  pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) { -    assert(m); -    assert(port > 0); +    pa_assert(m); +    pa_assert(port > 0);      return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);  }  pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) { -    assert(m); -    assert(port > 0); +    pa_assert(m); +    pa_assert(port > 0);      return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);  }  pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) { -    assert(m); -    assert(port > 0); +    pa_assert(m); +    pa_assert(port > 0);      return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);  } @@ -367,9 +379,9 @@ pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t por  pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {      struct in_addr ipv4; -    assert(m); -    assert(name); -    assert(port > 0); +    pa_assert(m); +    pa_assert(name); +    pa_assert(port > 0);      if (inet_pton(AF_INET, name, &ipv4) > 0)          return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, tcpwrap_service); @@ -380,9 +392,9 @@ pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const cha  pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {      struct in6_addr ipv6; -    assert(m); -    assert(name); -    assert(port > 0); +    pa_assert(m); +    pa_assert(name); +    pa_assert(port > 0);      if (inet_pton(AF_INET6, name, &ipv6) > 0)          return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, tcpwrap_service); @@ -391,7 +403,7 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha  }  static void socket_server_free(pa_socket_server*s) { -    assert(s); +    pa_assert(s);      if (s->filename) {          unlink(s->filename); @@ -407,21 +419,26 @@ static void socket_server_free(pa_socket_server*s) {  }  void pa_socket_server_unref(pa_socket_server *s) { -    assert(s && s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); -    if (!(--(s->ref))) +    if (PA_REFCNT_DEC(s) <= 0)          socket_server_free(s);  }  void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) { -    assert(s && s->ref >= 1); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1);      s->on_connection = on_connection;      s->userdata = userdata;  }  char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { -    assert(s && c && l > 0); +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); +    pa_assert(c); +    pa_assert(l > 0);      switch (s->type) {          case SOCKET_SERVER_IPV6: { @@ -438,14 +455,14 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {                  if (!pa_get_fqdn(fqdn, sizeof(fqdn)))                      return NULL; -                snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); +                pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));              } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {                  char hn[256];                  if (!pa_get_host_name(hn, sizeof(hn)))                      return NULL; -                snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port)); +                pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));              } else {                  char ip[INET6_ADDRSTRLEN]; @@ -454,7 +471,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {                      return NULL;                  } -                snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); +                pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));              }              return c; @@ -474,13 +491,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {                  if (!pa_get_fqdn(fqdn, sizeof(fqdn)))                      return NULL; -                snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); +                pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));              } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {                  char hn[256];                  if (!pa_get_host_name(hn, sizeof(hn)))                      return NULL; -                snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port)); +                pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));              } else {                  char ip[INET_ADDRSTRLEN]; @@ -489,7 +506,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {                      return NULL;                  } -                snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); +                pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));              } @@ -505,7 +522,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {              if (!pa_get_host_name(hn, sizeof(hn)))                  return NULL; -            snprintf(c, l, "{%s}unix:%s", hn, s->filename); +            pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);              return c;          } diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c index 673058e2..456accb8 100644 --- a/src/pulsecore/socket-util.c +++ b/src/pulsecore/socket-util.c @@ -31,7 +31,6 @@  #include <stdlib.h>  #include <signal.h>  #include <errno.h> -#include <assert.h>  #include <string.h>  #include <stdio.h>  #include <sys/types.h> @@ -75,19 +74,19 @@  #include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h>  #include "socket-util.h"  void pa_socket_peer_to_string(int fd, char *c, size_t l) {      struct stat st; -    assert(c && l && fd >= 0); +    pa_assert(fd >= 0); +    pa_assert(c); +    pa_assert(l > 0);  #ifndef OS_IS_WIN32 -    if (fstat(fd, &st) < 0) { -        snprintf(c, l, "Invalid client fd"); -        return; -    } +    pa_assert_se(fstat(fd, &st) == 0);  #endif  #ifndef OS_IS_WIN32 @@ -108,12 +107,12 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {              if (sa.sa.sa_family == AF_INET) {                  uint32_t ip = ntohl(sa.in.sin_addr.s_addr); -                snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", -                         ip >> 24, -                         (ip >> 16) & 0xFF, -                         (ip >> 8) & 0xFF, -                         ip & 0xFF, -                         ntohs(sa.in.sin_port)); +                pa_snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", +                            ip >> 24, +                            (ip >> 16) & 0xFF, +                            (ip >> 8) & 0xFF, +                            ip & 0xFF, +                            ntohs(sa.in.sin_port));                  return;              } else if (sa.sa.sa_family == AF_INET6) {                  char buf[INET6_ADDRSTRLEN]; @@ -121,94 +120,107 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {                  res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));                  if (res) { -                    snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port)); +                    pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));                      return;                  }  #ifdef HAVE_SYS_UN_H              } else if (sa.sa.sa_family == AF_UNIX) { -                snprintf(c, l, "UNIX socket client"); +                pa_snprintf(c, l, "UNIX socket client");                  return;  #endif              }          }  #ifndef OS_IS_WIN32 -        snprintf(c, l, "Unknown network client"); +        pa_snprintf(c, l, "Unknown network client");          return;      } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) { -        snprintf(c, l, "STDIN/STDOUT client"); +        pa_snprintf(c, l, "STDIN/STDOUT client");          return;      }  #endif /* OS_IS_WIN32 */ -    snprintf(c, l, "Unknown client"); +    pa_snprintf(c, l, "Unknown client");  } -int pa_socket_low_delay(int fd) { +void pa_make_socket_low_delay(int fd) { +  #ifdef SO_PRIORITY      int priority; -    assert(fd >= 0); +    pa_assert(fd >= 0); -    priority = 7; +    priority = 6;      if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0) -        return -1; +        pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));  #endif - -    return 0;  } -int pa_socket_tcp_low_delay(int fd) { -    int ret, tos, on; +void pa_make_tcp_socket_low_delay(int fd) { +    pa_assert(fd >= 0); -    assert(fd >= 0); - -    ret = pa_socket_low_delay(fd); - -    on = 1; -    tos = 0; +    pa_make_socket_low_delay(fd);  #if defined(SOL_TCP) || defined(IPPROTO_TCP) +    { +        int on = 1;  #if defined(SOL_TCP) -    if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) +        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)  #else -    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) +        if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)  #endif -        ret = -1; +            pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno)); +    }  #endif -#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \ -	defined(IPPROTO_IP)) -    tos = IPTOS_LOWDELAY; +#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP)) +    { +        int tos = IPTOS_LOWDELAY;  #ifdef SOL_IP -    if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +        if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)  #else -    if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)  #endif -        ret = -1; +            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno)); +    }  #endif +} -    return ret; +void pa_make_udp_socket_low_delay(int fd) { +    pa_assert(fd >= 0); +    pa_make_socket_low_delay(fd); + +#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP)) +    { +        int tos = IPTOS_LOWDELAY; +#ifdef SOL_IP +        if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +#else +        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +#endif +            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno)); +    } +#endif  }  int pa_socket_set_rcvbuf(int fd, size_t l) { -    assert(fd >= 0); +    pa_assert(fd >= 0); -/*     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */ -/*         pa_log("SO_RCVBUF: %s", strerror(errno)); */ -/*         return -1; */ -/*     } */ +    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { +        pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno)); +        return -1; +    }      return 0;  }  int pa_socket_set_sndbuf(int fd, size_t l) { -    assert(fd >= 0); +    pa_assert(fd >= 0); -/*     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */ -/*         pa_log("SO_SNDBUF: %s", strerror(errno)); */ -/*         return -1; */ -/*     } */ +    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { +        pa_log("SO_SNDBUF: %s", pa_cstrerror(errno)); +        return -1; +    }      return 0;  } @@ -219,6 +231,8 @@ int pa_unix_socket_is_stale(const char *fn) {      struct sockaddr_un sa;      int fd = -1, ret = -1; +    pa_assert(fn); +      if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {          pa_log("socket(): %s", pa_cstrerror(errno));          goto finish; @@ -244,6 +258,8 @@ finish:  int pa_unix_socket_remove_stale(const char *fn) {      int r; +    pa_assert(fn); +      if ((r = pa_unix_socket_is_stale(fn)) < 0)          return errno != ENOENT ? -1 : 0; diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h index 616c40ac..a0344c68 100644 --- a/src/pulsecore/socket-util.h +++ b/src/pulsecore/socket-util.h @@ -29,8 +29,9 @@  void pa_socket_peer_to_string(int fd, char *c, size_t l); -int pa_socket_low_delay(int fd); -int pa_socket_tcp_low_delay(int fd); +void pa_make_socket_low_delay(int fd); +void pa_make_tcp_socket_low_delay(int fd); +void pa_make_udp_socket_low_delay(int fd);  int pa_socket_set_sndbuf(int fd, size_t l);  int pa_socket_set_rcvbuf(int fd, size_t l); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 7a43c743..bb1f3e9a 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -26,100 +26,196 @@  #endif  #include <stdlib.h> -#include <assert.h>  #include <stdio.h>  #include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h>  #include <sndfile.h>  #include <pulse/xmalloc.h> +#include <pulsecore/core-error.h>  #include <pulsecore/sink-input.h>  #include <pulsecore/log.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/core-util.h>  #include "sound-file-stream.h" -#define BUF_SIZE (1024*10) - -struct userdata { +typedef struct file_stream { +    pa_msgobject parent; +    pa_core *core;      SNDFILE *sndfile;      pa_sink_input *sink_input;      pa_memchunk memchunk;      sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); +    size_t drop; +} file_stream; + +enum { +    FILE_STREAM_MESSAGE_UNLINK  }; -static void free_userdata(struct userdata *u) { -    assert(u); -    if (u->sink_input) { -        pa_sink_input_disconnect(u->sink_input); -        pa_sink_input_unref(u->sink_input); -    } +PA_DECLARE_CLASS(file_stream); +#define FILE_STREAM(o) (file_stream_cast(o)) +static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject); + +static void file_stream_unlink(file_stream *u) { +    pa_assert(u); + +    if (!u->sink_input) +        return; + +    pa_sink_input_unlink(u->sink_input); + +    pa_sink_input_unref(u->sink_input); +    u->sink_input = NULL; + +    /* Make sure we don't decrease the ref count twice. */ +    file_stream_unref(u); +} + +static void file_stream_free(pa_object *o) { +    file_stream *u = FILE_STREAM(o); +    pa_assert(u); + +    file_stream_unlink(u);      if (u->memchunk.memblock)          pa_memblock_unref(u->memchunk.memblock); +      if (u->sndfile)          sf_close(u->sndfile);      pa_xfree(u);  } -static void sink_input_kill(pa_sink_input *i) { -    assert(i && i->userdata); -    free_userdata(i->userdata); +static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { +    file_stream *u = FILE_STREAM(o); +    file_stream_assert_ref(u); + +    switch (code) { +        case FILE_STREAM_MESSAGE_UNLINK: +            file_stream_unlink(u); +            break; +    } + +    return 0; +} + +static void sink_input_kill_cb(pa_sink_input *i) { +    pa_sink_input_assert_ref(i); + +    file_stream_unlink(FILE_STREAM(i->userdata));  } -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { -    struct userdata *u; -    assert(i && chunk && i->userdata); -    u = i->userdata; +static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +    file_stream *u; + +    pa_assert(i); +    pa_assert(chunk); +    u = FILE_STREAM(i->userdata); +    file_stream_assert_ref(u); + +    if (!u->sndfile) +        return -1; + +    for (;;) { + +        if (!u->memchunk.memblock) { + +            u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); +            u->memchunk.index = 0; + +            if (u->readf_function) { +                sf_count_t n; +                void *p; +                size_t fs = pa_frame_size(&i->sample_spec); + +                p = pa_memblock_acquire(u->memchunk.memblock); +                n = u->readf_function(u->sndfile, p, length/fs); +                pa_memblock_release(u->memchunk.memblock); -    if (!u->memchunk.memblock) { -        uint32_t fs = pa_frame_size(&i->sample_spec); -        sf_count_t n; +                if (n <= 0) +                    n = 0; -        u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE); -        u->memchunk.index = 0; +                u->memchunk.length = n * fs; +            } else { +                sf_count_t n; +                void *p; -        if (u->readf_function) { -            if ((n = u->readf_function(u->sndfile, u->memchunk.memblock->data, BUF_SIZE/fs)) <= 0) -                n = 0; +                p = pa_memblock_acquire(u->memchunk.memblock); +                n = sf_read_raw(u->sndfile, p, length); +                pa_memblock_release(u->memchunk.memblock); -            u->memchunk.length = n * fs; -        } else { -            if ((n = sf_read_raw(u->sndfile, u->memchunk.memblock->data, BUF_SIZE)) <= 0) -                n = 0; +                if (n <= 0) +                    n = 0; -            u->memchunk.length = n; +                u->memchunk.length = n; +            } + +            if (u->memchunk.length <= 0) { + +                pa_memblock_unref(u->memchunk.memblock); +                pa_memchunk_reset(&u->memchunk); + +                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + +                sf_close(u->sndfile); +                u->sndfile = NULL; + +                return -1; +            }          } -        if (!u->memchunk.length) { -            free_userdata(u); -            return -1; +        pa_assert(u->memchunk.memblock); +        pa_assert(u->memchunk.length > 0); + +        if (u->drop < u->memchunk.length) { +            u->memchunk.index += u->drop; +            u->memchunk.length -= u->drop; +            u->drop = 0; +            break;          } + +        u->drop -= u->memchunk.length; +        pa_memblock_unref(u->memchunk.memblock); +        pa_memchunk_reset(&u->memchunk);      }      *chunk = u->memchunk;      pa_memblock_ref(chunk->memblock); -    assert(chunk->length); + +    pa_assert(chunk->length > 0); +    pa_assert(u->drop <= 0); +      return 0;  } -static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { -    struct userdata *u; -    assert(i && chunk && length && i->userdata); -    u = i->userdata; +static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +    file_stream *u; -    assert(!memcmp(chunk, &u->memchunk, sizeof(chunk))); -    assert(length <= u->memchunk.length); +    pa_assert(i); +    pa_assert(length > 0); +    u = FILE_STREAM(i->userdata); +    file_stream_assert_ref(u); -    u->memchunk.index += length; -    u->memchunk.length -= length; +    if (u->memchunk.memblock) { -    if (u->memchunk.length <= 0) { +        if (length < u->memchunk.length) { +            u->memchunk.index += length; +            u->memchunk.length -= length; +            return; +        } + +        length -= u->memchunk.length;          pa_memblock_unref(u->memchunk.memblock); -        u->memchunk.memblock = NULL; -        u->memchunk.index = u->memchunk.length = 0; +        pa_memchunk_reset(&u->memchunk);      } + +    u->drop += length;  }  int pa_play_file( @@ -127,28 +223,60 @@ int pa_play_file(          const char *fname,          const pa_cvolume *volume) { -    struct userdata *u = NULL; +    file_stream *u = NULL;      SF_INFO sfinfo;      pa_sample_spec ss;      pa_sink_input_new_data data; +    int fd; -    assert(sink); -    assert(fname); +    pa_assert(sink); +    pa_assert(fname); -    u = pa_xnew(struct userdata, 1); +    u = pa_msgobject_new(file_stream); +    u->parent.parent.free = file_stream_free; +    u->parent.process_msg = file_stream_process_msg; +    u->core = sink->core;      u->sink_input = NULL; -    u->memchunk.memblock = NULL; -    u->memchunk.index = u->memchunk.length = 0; +    pa_memchunk_reset(&u->memchunk);      u->sndfile = NULL; +    u->readf_function = NULL; +    u->drop = 0;      memset(&sfinfo, 0, sizeof(sfinfo)); -    if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) { -        pa_log("Failed to open file %s", fname); +    if ((fd = open(fname, O_RDONLY +#ifdef O_NOCTTY +                   |O_NOCTTY +#endif +                   )) < 0) { +        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));          goto fail;      } -    u->readf_function = NULL; +    /* FIXME: For now we just use posix_fadvise to avoid page faults +     * when accessing the file data. Eventually we should move the +     * file reader into the main event loop and pass the data over the +     * asyncmsgq. */ + +#ifdef HAVE_POSIX_FADVISE +    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { +        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); +        goto fail; +    } else +        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); + +    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) { +        pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno)); +        goto fail; +    } else +        pa_log_debug("POSIX_FADV_WILLNEED succeeded."); +#endif + +    if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) { +        pa_log("Failed to open file %s", fname); +        pa_close(fd); +        goto fail; +    }      switch (sfinfo.format & 0xFF) {          case SF_FORMAT_PCM_16: @@ -191,18 +319,21 @@ int pa_play_file(      if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))          goto fail; -    u->sink_input->peek = sink_input_peek; -    u->sink_input->drop = sink_input_drop; -    u->sink_input->kill = sink_input_kill; +    u->sink_input->peek = sink_input_peek_cb; +    u->sink_input->drop = sink_input_drop_cb; +    u->sink_input->kill = sink_input_kill_cb;      u->sink_input->userdata = u; -    pa_sink_notify(u->sink_input->sink); +    pa_sink_input_put(u->sink_input); + +    /* The reference to u is dangling here, because we want to keep +     * this stream around until it is fully played. */      return 0;  fail:      if (u) -        free_userdata(u); +        file_stream_unref(u);      return -1;  } diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c index 69b543ab..7e88734c 100644 --- a/src/pulsecore/sound-file.c +++ b/src/pulsecore/sound-file.c @@ -26,31 +26,63 @@  #endif  #include <string.h> -#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h>  #include <sndfile.h>  #include <pulse/sample.h>  #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h>  #include "sound-file.h"  #include "core-scache.h" -int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk) { -    SNDFILE*sf = NULL; +int pa_sound_file_load( +        pa_mempool *pool, +        const char *fname, +        pa_sample_spec *ss, +        pa_channel_map *map, +        pa_memchunk *chunk) { + +    SNDFILE *sf = NULL;      SF_INFO sfinfo;      int ret = -1;      size_t l;      sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL; -    assert(fname && ss && chunk); +    void *ptr = NULL; +    int fd; -    chunk->memblock = NULL; -    chunk->index = chunk->length = 0; +    pa_assert(fname); +    pa_assert(ss); +    pa_assert(chunk); +    pa_memchunk_reset(chunk);      memset(&sfinfo, 0, sizeof(sfinfo)); -    if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { +    if ((fd = open(fname, O_RDONLY +#ifdef O_NOCTTY +                   |O_NOCTTY +#endif +                   )) < 0) { +        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno)); +        goto finish; +    } + +#ifdef HAVE_POSIX_FADVISE +    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { +        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); +        goto finish; +    } else +        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); +#endif + +    if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {          pa_log("Failed to open file %s", fname); +        pa_close(fd);          goto finish;      } @@ -89,18 +121,19 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,      if (map)          pa_channel_map_init_auto(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) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {          pa_log("File too large");          goto finish;      }      chunk->memblock = pa_memblock_new(pool, l); -    assert(chunk->memblock);      chunk->index = 0;      chunk->length = l; -    if ((readf_function && readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) || -        (!readf_function && sf_read_raw(sf, chunk->memblock->data, l) != l)) { +    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)) {          pa_log("Premature file end");          goto finish;      } @@ -112,21 +145,26 @@ finish:      if (sf)          sf_close(sf); +    if (ptr) +        pa_memblock_release(chunk->memblock); +      if (ret != 0 && chunk->memblock)          pa_memblock_unref(chunk->memblock);      return ret; -  }  int pa_sound_file_too_big_to_cache(const char *fname) { +      SNDFILE*sf = NULL;      SF_INFO sfinfo;      pa_sample_spec ss; +    pa_assert(fname); +      if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {          pa_log("Failed to open file %s", fname); -        return 0; +        return -1;      }      sf_close(sf); @@ -156,8 +194,13 @@ int pa_sound_file_too_big_to_cache(const char *fname) {      ss.rate = sfinfo.samplerate;      ss.channels = 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) { -        pa_log("File too large %s", fname); +        pa_log("File too large: %s", fname);          return 1;      } diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c7a9858c..2a902dc2 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -26,7 +26,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -39,14 +38,12 @@  #include "source-output.h" -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ -    return NULL; \ -} while (0) +static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject); + +static void source_output_free(pa_object* mo);  pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) { -    assert(data); +    pa_assert(data);      memset(data, 0, sizeof(*data));      data->resample_method = PA_RESAMPLER_INVALID; @@ -54,14 +51,14 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d  }  void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) { -    assert(data); +    pa_assert(data);      if ((data->channel_map_is_set = !!map))          data->channel_map = *map;  }  void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) { -    assert(data); +    pa_assert(data);      if ((data->sample_spec_is_set = !!spec))          data->sample_spec = *spec; @@ -74,288 +71,415 @@ pa_source_output* pa_source_output_new(      pa_source_output *o;      pa_resampler *resampler = NULL; -    int r;      char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; -    assert(core); -    assert(data); +    pa_assert(core); +    pa_assert(data); -    if (!(flags & PA_SOURCE_OUTPUT_NO_HOOKS)) -        if (pa_hook_fire(&core->hook_source_output_new, data) < 0) -            return NULL; +    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0) +        return NULL; -    CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver)); -    CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name)); +    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); +    pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name));      if (!data->source)          data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1); -    CHECK_VALIDITY_RETURN_NULL(data->source); -    CHECK_VALIDITY_RETURN_NULL(data->source->state == PA_SOURCE_RUNNING); +    pa_return_null_if_fail(data->source); +    pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);      if (!data->sample_spec_is_set)          data->sample_spec = data->source->sample_spec; -    CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec)); +    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); -    if (!data->channel_map_is_set) -        pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); +    if (!data->channel_map_is_set) { +        if (data->source->channel_map.channels == data->sample_spec.channels) +            data->channel_map = data->source->channel_map; +        else +            pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); +    } -    CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map)); -    CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels); +    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);      if (data->resample_method == PA_RESAMPLER_INVALID)          data->resample_method = core->resample_method; -    CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX); +    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);      if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {          pa_log("Failed to create source output: too many outputs per source.");          return NULL;      } -    if (!pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || -        !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) +    if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || +        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || +        !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { +          if (!(resampler = pa_resampler_new(                        core->mempool,                        &data->source->sample_spec, &data->source->channel_map,                        &data->sample_spec, &data->channel_map, -                      data->resample_method))) { +                      data->resample_method, +                      !!(flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {              pa_log_warn("Unsupported resampling operation.");              return NULL;          } -    o = pa_xnew(pa_source_output, 1); -    o->ref = 1; -    o->state = PA_SOURCE_OUTPUT_RUNNING; +        data->resample_method = pa_resampler_get_method(resampler); +    } + +    o = pa_msgobject_new(pa_source_output); +    o->parent.parent.free = source_output_free; +    o->parent.process_msg = pa_source_output_process_msg; + +    o->core = core; +    o->state = PA_SOURCE_OUTPUT_INIT; +    o->flags = flags;      o->name = pa_xstrdup(data->name);      o->driver = pa_xstrdup(data->driver);      o->module = data->module;      o->source = data->source;      o->client = data->client; +    o->resample_method = data->resample_method;      o->sample_spec = data->sample_spec;      o->channel_map = data->channel_map;      o->push = NULL;      o->kill = NULL;      o->get_latency = NULL; +    o->detach = NULL; +    o->attach = NULL; +    o->suspend = NULL;      o->userdata = NULL; -    o->resampler = resampler; -    o->resample_method = data->resample_method; +    o->thread_info.state = o->state; +    o->thread_info.attached = FALSE; +    o->thread_info.sample_spec = o->sample_spec; +    o->thread_info.resampler = resampler; -    r = pa_idxset_put(core->source_outputs, o, &o->index); -    assert(r == 0); -    r = pa_idxset_put(o->source->outputs, o, NULL); -    assert(r == 0); +    pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); +    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); -    pa_log_info("created %u \"%s\" on %s with sample spec %s", +    pa_log_info("Created output %u \"%s\" on %s with sample spec %s",                  o->index,                  o->name,                  o->source->name,                  pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec)); -    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); - -    /* We do not call pa_source_notify() here, because the virtual -     * functions have not yet been initialized */ +    /* Don't forget to call pa_source_output_put! */      return o;  } -void pa_source_output_disconnect(pa_source_output*o) { -    assert(o); -    assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED); -    assert(o->source); -    assert(o->source->core); +static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { +    pa_assert(o); + +    if (o->state == state) +        return 0; + +    if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) +        return -1; + +    if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED) +        pa_assert_se(o->source->n_corked -- >= 1); +    else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) +        o->source->n_corked++; + +    pa_source_update_status(o->source); + +    o->state = state; + +    if (state != PA_SOURCE_OUTPUT_UNLINKED) +        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); + +    return 0; +} + +void pa_source_output_unlink(pa_source_output*o) { +    pa_bool_t linked; +    pa_assert(o); + +    /* See pa_sink_unlink() for a couple of comments how this function +     * works */ + +    pa_source_output_ref(o); + +    linked = PA_SOURCE_OUTPUT_LINKED(o->state); + +    if (linked) +        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);      pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); -    pa_idxset_remove_by_data(o->source->outputs, o, NULL); +    if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) +        pa_source_output_unref(o); -    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); -    o->source = NULL; +    if (linked) { +        pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); +        source_output_set_state(o, PA_SOURCE_OUTPUT_UNLINKED); +        pa_source_update_status(o->source); +    } else +        o->state = PA_SOURCE_OUTPUT_UNLINKED;      o->push = NULL;      o->kill = NULL;      o->get_latency = NULL; +    o->attach = NULL; +    o->detach = NULL; +    o->suspend = NULL; + +    if (linked) { +        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); +        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); +    } -    o->state = PA_SOURCE_OUTPUT_DISCONNECTED; +    o->source = NULL; +    pa_source_output_unref(o);  } -static void source_output_free(pa_source_output* o) { -    assert(o); +static void source_output_free(pa_object* mo) { +    pa_source_output *o = PA_SOURCE_OUTPUT(mo); + +    pa_assert(pa_source_output_refcnt(o) == 0); + +    if (PA_SOURCE_OUTPUT_LINKED(o->state)) +        pa_source_output_unlink(o); -    if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED) -        pa_source_output_disconnect(o); +    pa_log_info("Freeing output %u \"%s\"", o->index, o->name); -    pa_log_info("freed %u \"%s\"", o->index, o->name); +    pa_assert(!o->thread_info.attached); -    if (o->resampler) -        pa_resampler_free(o->resampler); +    if (o->thread_info.resampler) +        pa_resampler_free(o->thread_info.resampler);      pa_xfree(o->name);      pa_xfree(o->driver);      pa_xfree(o);  } -void pa_source_output_unref(pa_source_output* o) { -    assert(o); -    assert(o->ref >= 1); +void pa_source_output_put(pa_source_output *o) { +    pa_source_output_assert_ref(o); -    if (!(--o->ref)) -        source_output_free(o); -} +    pa_assert(o->state == PA_SOURCE_OUTPUT_INIT); +    pa_assert(o->push); -pa_source_output* pa_source_output_ref(pa_source_output *o) { -    assert(o); -    assert(o->ref >= 1); +    o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; -    o->ref++; -    return o; +    if (o->state == PA_SOURCE_OUTPUT_CORKED) +        o->source->n_corked++; + +    pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); +    pa_source_update_status(o->source); + +    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); + +    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);  }  void pa_source_output_kill(pa_source_output*o) { -    assert(o); -    assert(o->ref >= 1); +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));      if (o->kill)          o->kill(o);  } +pa_usec_t pa_source_output_get_latency(pa_source_output *o) { +    pa_usec_t r = 0; + +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + +    if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) +        r = 0; + +    if (o->get_latency) +        r += o->get_latency(o); + +    return r; +} + +/* Called from thread context */  void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {      pa_memchunk rchunk; -    assert(o); -    assert(chunk); -    assert(chunk->length); -    assert(o->push); +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); +    pa_assert(chunk); +    pa_assert(chunk->length); -    if (o->state == PA_SOURCE_OUTPUT_CORKED) +    if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED)          return; -    if (!o->resampler) { +    pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING); + +    if (!o->thread_info.resampler) {          o->push(o, chunk);          return;      } -    pa_resampler_run(o->resampler, chunk, &rchunk); +    pa_resampler_run(o->thread_info.resampler, chunk, &rchunk);      if (!rchunk.length)          return; -    assert(rchunk.memblock); +    pa_assert(rchunk.memblock);      o->push(o, &rchunk);      pa_memblock_unref(rchunk.memblock);  } -void pa_source_output_set_name(pa_source_output *o, const char *name) { -    assert(o); -    assert(o->ref >= 1); - -    if (!o->name && !name) -        return; +void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); -    if (o->name && name && !strcmp(o->name, name)) -        return; +    source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); +} -    pa_xfree(o->name); -    o->name = pa_xstrdup(name); +int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); +    pa_return_val_if_fail(o->thread_info.resampler, -1); -    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); -} +    if (o->sample_spec.rate == rate) +        return 0; -pa_usec_t pa_source_output_get_latency(pa_source_output *o) { -    assert(o); -    assert(o->ref >= 1); +    o->sample_spec.rate = rate; -    if (o->get_latency) -        return o->get_latency(o); +    pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); +    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);      return 0;  } -void pa_source_output_cork(pa_source_output *o, int b) { -    int n; - -    assert(o); -    assert(o->ref >= 1); +void pa_source_output_set_name(pa_source_output *o, const char *name) { +    pa_source_output_assert_ref(o); -    if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED) +    if (!o->name && !name)          return; -    n = o->state == PA_SOURCE_OUTPUT_CORKED && !b; +    if (o->name && name && !strcmp(o->name, name)) +        return; -    o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; +    pa_xfree(o->name); +    o->name = pa_xstrdup(name); -    if (n) -        pa_source_notify(o->source); +    if (PA_SOURCE_OUTPUT_LINKED(o->state)) { +        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o); +        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +    }  }  pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { -    assert(o); -    assert(o->ref >= 1); +    pa_source_output_assert_ref(o); -    if (!o->resampler) -        return o->resample_method; - -    return pa_resampler_get_method(o->resampler); +    return o->resample_method;  }  int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {      pa_source *origin;      pa_resampler *new_resampler = NULL; -    assert(o); -    assert(o->ref >= 1); -    assert(dest); +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); +    pa_source_assert_ref(dest);      origin = o->source;      if (dest == origin)          return 0; +    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) +        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->resampler && +    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))          /* Try to reuse the old resampler if possible */ -        new_resampler = o->resampler; +        new_resampler = o->thread_info.resampler; -    else if (!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || -        !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { +    else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || +             !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || +             !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { -        /* Okey, we need a new resampler for the new sink */ +        /* Okey, we need a new resampler for the new source */          if (!(new_resampler = pa_resampler_new(                        dest->core->mempool,                        &dest->sample_spec, &dest->channel_map,                        &o->sample_spec, &o->channel_map, -                      o->resample_method))) { +                      o->resample_method, +                      !!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {              pa_log_warn("Unsupported resampling operation.");              return -1;          }      } +    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o); +      /* Okey, let's move it */ +    pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); +      pa_idxset_remove_by_data(origin->outputs, o, NULL);      pa_idxset_put(dest->outputs, o, NULL);      o->source = dest; +    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) { +        pa_assert_se(origin->n_corked-- >= 1); +        dest->n_corked++; +    } +      /* Replace resampler */ -    if (new_resampler != o->resampler) { -        if (o->resampler) -            pa_resampler_free(o->resampler); -        o->resampler = new_resampler; +    if (new_resampler != o->thread_info.resampler) { +        if (o->thread_info.resampler) +            pa_resampler_free(o->thread_info.resampler); +        o->thread_info.resampler = new_resampler;      } +    pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); + +    pa_source_update_status(origin); +    pa_source_update_status(dest); + +    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o); + +    pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name); +      /* Notify everyone */      pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); -    pa_source_notify(o->source);      return 0;  } + +/* Called from thread context */ +int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) { +    pa_source_output *o = PA_SOURCE_OUTPUT(mo); + +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + +    switch (code) { + +        case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: { + +            o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); +            pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata)); + +            return 0; +        } + +        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: { +            o->thread_info.state = PA_PTR_TO_UINT(userdata); + +            return 0; +        } +    } + +    return -1; +} diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 3da6caac..e38a1e5a 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -1,5 +1,5 @@ -#ifndef foosourceoutputhfoo -#define foosourceoutputhfoo +#ifndef foopulsesourceoutputhfoo +#define foopulsesourceoutputhfoo  /* $Id$ */ @@ -35,40 +35,91 @@ typedef struct pa_source_output pa_source_output;  #include <pulsecore/module.h>  #include <pulsecore/client.h> -typedef enum { +typedef enum pa_source_output_state { +    PA_SOURCE_OUTPUT_INIT,      PA_SOURCE_OUTPUT_RUNNING,      PA_SOURCE_OUTPUT_CORKED, -    PA_SOURCE_OUTPUT_DISCONNECTED +    PA_SOURCE_OUTPUT_UNLINKED  } pa_source_output_state_t; +static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) { +    return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED; +} +  typedef enum pa_source_output_flags { -    PA_SOURCE_OUTPUT_NO_HOOKS = 1 +    PA_SOURCE_OUTPUT_VARIABLE_RATE = 1, +    PA_SOURCE_OUTPUT_DONT_MOVE = 2, +    PA_SOURCE_OUTPUT_START_CORKED = 4  } pa_source_output_flags_t;  struct pa_source_output { -    int ref; +    pa_msgobject parent; +      uint32_t index; +    pa_core *core;      pa_source_output_state_t state; +    pa_source_output_flags_t flags;      char *name, *driver;                  /* may be NULL */      pa_module *module;                    /* may be NULL */ +    pa_client *client;                    /* may be NULL */      pa_source *source; -    pa_client *client;                    /* may be NULL */      pa_sample_spec sample_spec;      pa_channel_map channel_map; +    /* Pushes a new memchunk into the output. Called from IO thread +     * context. */      void (*push)(pa_source_output *o, const pa_memchunk *chunk); + +    /* If non-NULL this function is called when the output is first +     * connected to a source. Called from IO thread context */ +    void (*attach) (pa_source_output *o);           /* may be NULL */ + +    /* If non-NULL this function is called when the output is +     * disconnected from its source. Called from IO thread context */ +    void (*detach) (pa_source_output *o);           /* may be NULL */ + +    /* If non-NULL called whenever the the source this output is attached +     * to suspends or resumes. Called from main context */ +    void (*suspend) (pa_source_output *o, int b);   /* may be NULL */ + +    /* Supposed to unlink and destroy this stream. Called from main +     * context. */      void (*kill)(pa_source_output* o);              /* may be NULL */ + +    /* Return the current latency (i.e. length of bufferd audio) of +    this stream. Called from main context. If NULL a +    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message is sent to the IO +    thread instead. */      pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ -    pa_resampler* resampler;              /* may be NULL */      pa_resample_method_t resample_method; +    struct { +        pa_source_output_state_t state; + +        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ + +        pa_sample_spec sample_spec; + +        pa_resampler* resampler;              /* may be NULL */ +    } thread_info; +      void *userdata;  }; +PA_DECLARE_CLASS(pa_source_output); +#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o) + +enum { +    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, +    PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, +    PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, +    PA_SOURCE_OUTPUT_MESSAGE_MAX +}; +  typedef struct pa_source_output_new_data {      const char *name, *driver;      pa_module *module; @@ -77,9 +128,9 @@ typedef struct pa_source_output_new_data {      pa_source *source;      pa_sample_spec sample_spec; -    int sample_spec_is_set; +    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    int channel_map_is_set; +    pa_bool_t channel_map_is_set;      pa_resample_method_t resample_method;  } pa_source_output_new_data; @@ -89,30 +140,38 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data,  void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);  void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume); +/* To be called by the implementing module only */ +  pa_source_output* pa_source_output_new(          pa_core *core,          pa_source_output_new_data *data,          pa_source_output_flags_t flags); -void pa_source_output_unref(pa_source_output* o); -pa_source_output* pa_source_output_ref(pa_source_output *o); +void pa_source_output_put(pa_source_output *o); +void pa_source_output_unlink(pa_source_output*o); -/* To be called by the implementing module only */ -void pa_source_output_disconnect(pa_source_output*o); +void pa_source_output_set_name(pa_source_output *i, const char *name); + +/* Callable by everyone */  /* External code may request disconnection with this funcion */  void pa_source_output_kill(pa_source_output*o); -void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); - -void pa_source_output_set_name(pa_source_output *i, const char *name); -  pa_usec_t pa_source_output_get_latency(pa_source_output *i); -void pa_source_output_cork(pa_source_output *i, int b); +void pa_source_output_cork(pa_source_output *i, pa_bool_t b); + +int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);  pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);  int pa_source_output_move_to(pa_source_output *o, pa_source *dest); +#define pa_source_output_get_state(o) ((o)->state) + +/* To be used exclusively by the source driver thread */ + +void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); +int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +  #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 9bb2d342..9a6902ae 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -27,7 +27,6 @@  #endif  #include <stdio.h> -#include <assert.h>  #include <stdlib.h>  #include <string.h> @@ -42,11 +41,9 @@  #include "source.h" -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ -    return NULL; \ -} while (0) +static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject); + +static void source_free(pa_object *o);  pa_source* pa_source_new(          pa_core *core, @@ -58,274 +55,359 @@ pa_source* pa_source_new(      pa_source *s;      char st[256]; -    int r;      pa_channel_map tmap; -    assert(core); -    assert(name); -    assert(spec); +    pa_assert(core); +    pa_assert(name); +    pa_assert(spec); -    CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); +    pa_return_null_if_fail(pa_sample_spec_valid(spec));      if (!map)          map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); -    CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); -    CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); -    CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); -    CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); +    pa_return_null_if_fail(map && pa_channel_map_valid(map)); +    pa_return_null_if_fail(map->channels == spec->channels); +    pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); +    pa_return_null_if_fail(pa_utf8_valid(name) && *name); -    s = pa_xnew(pa_source, 1); +    s = pa_msgobject_new(pa_source);      if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {          pa_xfree(s);          return NULL;      } -    s->ref = 1; +    s->parent.parent.free = source_free; +    s->parent.process_msg = pa_source_process_msg; +      s->core = core; -    s->state = PA_SOURCE_RUNNING; +    s->state = PA_SOURCE_INIT; +    s->flags = 0;      s->name = pa_xstrdup(name);      s->description = NULL;      s->driver = pa_xstrdup(driver); -    s->owner = NULL; +    s->module = NULL;      s->sample_spec = *spec;      s->channel_map = *map;      s->outputs = pa_idxset_new(NULL, NULL); +    s->n_corked = 0;      s->monitor_of = NULL; -    pa_cvolume_reset(&s->sw_volume, spec->channels); -    pa_cvolume_reset(&s->hw_volume, spec->channels); -    s->sw_muted = 0; -    s->hw_muted = 0; - -    s->is_hardware = 0; +    pa_cvolume_reset(&s->volume, spec->channels); +    s->muted = FALSE; +    s->refresh_volume = s->refresh_muted = FALSE;      s->get_latency = NULL; -    s->notify = NULL; -    s->set_hw_volume = NULL; -    s->get_hw_volume = NULL; -    s->set_hw_mute = NULL; -    s->get_hw_mute = NULL; +    s->set_volume = NULL; +    s->get_volume = NULL; +    s->set_mute = NULL; +    s->get_mute = NULL; +    s->set_state = NULL;      s->userdata = NULL; -    r = pa_idxset_put(core->sources, s, &s->index); -    assert(s->index != PA_IDXSET_INVALID && r >= 0); +    s->asyncmsgq = NULL; +    s->rtpoll = NULL; + +    pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);      pa_sample_spec_snprint(st, sizeof(st), spec); -    pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); +    pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); -    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); +    s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); +    s->thread_info.soft_volume = s->volume; +    s->thread_info.soft_muted = s->muted; +    s->thread_info.state = s->state;      return s;  } -void pa_source_disconnect(pa_source *s) { +static int source_set_state(pa_source *s, pa_source_state_t state) { +    int ret; + +    pa_assert(s); + +    if (s->state == state) +        return 0; + +    if ((s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) || +        (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED)) { +        pa_source_output *o; +        uint32_t idx; + +        /* We're suspending or resuming, tell everyone about it */ + +        for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) +            if (o->suspend) +                o->suspend(o, state == PA_SINK_SUSPENDED); +    } + +    if (s->set_state) +        if ((ret = s->set_state(s, state)) < 0) +            return -1; + +    if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) +        return -1; + +    s->state = state; + +    if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */ +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s); +    return 0; +} + +void pa_source_put(pa_source *s) { +    pa_source_assert_ref(s); + +    pa_assert(s->state == PA_SINK_INIT); +    pa_assert(s->rtpoll); +    pa_assert(s->asyncmsgq); + +    pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); + +    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); +    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], s); +} + +void pa_source_unlink(pa_source *s) { +    pa_bool_t linked;      pa_source_output *o, *j = NULL; -    assert(s); -    assert(s->state == PA_SOURCE_RUNNING); +    pa_assert(s); + +    /* See pa_sink_unlink() for a couple of comments how this function +     * works. */ -    s->state = PA_SOURCE_DISCONNECTED; -    pa_namereg_unregister(s->core, s->name); +    linked = PA_SOURCE_LINKED(s->state); -    pa_hook_fire(&s->core->hook_source_disconnect, s); +    if (linked) +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); + +    if (s->state != PA_SOURCE_UNLINKED) +        pa_namereg_unregister(s->core, s->name); +    pa_idxset_remove_by_data(s->core->sources, s, NULL);      while ((o = pa_idxset_first(s->outputs, NULL))) { -        assert(o != j); +        pa_assert(o != j);          pa_source_output_kill(o);          j = o;      } -    pa_idxset_remove_by_data(s->core->sources, s, NULL); +    if (linked) +        source_set_state(s, PA_SOURCE_UNLINKED); +    else +        s->state = PA_SOURCE_UNLINKED;      s->get_latency = NULL; -    s->notify = NULL; -    s->get_hw_volume = NULL; -    s->set_hw_volume = NULL; -    s->set_hw_mute = NULL; -    s->get_hw_mute = NULL; - -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +    s->get_volume = NULL; +    s->set_volume = NULL; +    s->set_mute = NULL; +    s->get_mute = NULL; +    s->set_state = NULL; + +    if (linked) { +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s); +    }  } -static void source_free(pa_source *s) { -    assert(s); -    assert(!s->ref); +static void source_free(pa_object *o) { +    pa_source_output *so; +    pa_source *s = PA_SOURCE(o); -    if (s->state != PA_SOURCE_DISCONNECTED) -        pa_source_disconnect(s); +    pa_assert(s); +    pa_assert(pa_source_refcnt(s) == 0); -    pa_log_info("freed %u \"%s\"", s->index, s->name); +    if (PA_SOURCE_LINKED(s->state)) +        pa_source_unlink(s); + +    pa_log_info("Freeing source %u \"%s\"", s->index, s->name);      pa_idxset_free(s->outputs, NULL, NULL); +    while ((so = pa_hashmap_steal_first(s->thread_info.outputs))) +        pa_source_output_unref(so); + +    pa_hashmap_free(s->thread_info.outputs, NULL, NULL); +      pa_xfree(s->name);      pa_xfree(s->description);      pa_xfree(s->driver);      pa_xfree(s);  } -void pa_source_unref(pa_source *s) { -    assert(s); -    assert(s->ref >= 1); - -    if (!(--s->ref)) -        source_free(s); -} +int pa_source_update_status(pa_source*s) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); -pa_source* pa_source_ref(pa_source *s) { -    assert(s); -    assert(s->ref >= 1); +    if (s->state == PA_SOURCE_SUSPENDED) +        return 0; -    s->ref++; -    return s; +    return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);  } -void pa_source_notify(pa_source*s) { -    assert(s); -    assert(s->ref >= 1); +int pa_source_suspend(pa_source *s, pa_bool_t suspend) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); -    if (s->notify) -        s->notify(s); +    if (suspend) +        return source_set_state(s, PA_SOURCE_SUSPENDED); +    else +        return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);  } -static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, PA_GCC_UNUSED int *del, void*userdata) { -    pa_source_output *o = p; -    const pa_memchunk *chunk = userdata; +void pa_source_ping(pa_source *s) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); -    assert(o); -    assert(chunk); - -    pa_source_output_push(o, chunk); -    return 0; +    pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);  }  void pa_source_post(pa_source*s, const pa_memchunk *chunk) { -    assert(s); -    assert(s->ref >= 1); -    assert(chunk); +    pa_source_output *o; +    void *state = NULL; -    pa_source_ref(s); +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); +    pa_assert(chunk); -    if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) { +    if (s->thread_info.state != PA_SOURCE_RUNNING) +        return; + +    if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {          pa_memchunk vchunk = *chunk;          pa_memblock_ref(vchunk.memblock);          pa_memchunk_make_writable(&vchunk, 0); -        if (s->sw_muted) + +        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))              pa_silence_memchunk(&vchunk, &s->sample_spec);          else -            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->sw_volume); -        pa_idxset_foreach(s->outputs, do_post, &vchunk); +            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume); + +        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) +            pa_source_output_push(o, &vchunk); +          pa_memblock_unref(vchunk.memblock); -    } else -        pa_idxset_foreach(s->outputs, do_post, (void*) chunk); +    } else { -    pa_source_unref(s); +        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) +            pa_source_output_push(o, chunk); +    }  } -void pa_source_set_owner(pa_source *s, pa_module *m) { -    assert(s); -    assert(s->ref >= 1); +pa_usec_t pa_source_get_latency(pa_source *s) { +    pa_usec_t usec; -    if (m == s->owner) -        return; +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); -    s->owner = m; -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -} +    if (!PA_SOURCE_OPENED(s->state)) +        return 0; -pa_usec_t pa_source_get_latency(pa_source *s) { -    assert(s); -    assert(s->ref >= 1); +    if (s->get_latency) +        return s->get_latency(s); -    if (!s->get_latency) +    if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)          return 0; -    return s->get_latency(s); +    return usec;  } -void pa_source_set_volume(pa_source *s, pa_mixer_t m, const pa_cvolume *volume) { -    pa_cvolume *v; +void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { +    int changed; -    assert(s); -    assert(s->ref >= 1); -    assert(volume); +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); +    pa_assert(volume); -    if (m == PA_MIXER_HARDWARE && s->set_hw_volume) -        v = &s->hw_volume; -    else -        v = &s->sw_volume; +    changed = !pa_cvolume_equal(volume, &s->volume); +    s->volume = *volume; -    if (pa_cvolume_equal(v, volume)) -        return; +    if (s->set_volume && s->set_volume(s) < 0) +        s->set_volume = NULL; -    *v = *volume; +    if (!s->set_volume) +        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); -    if (v == &s->hw_volume) -        if (s->set_hw_volume(s) < 0) -            s->sw_volume =  *volume; +    if (changed) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +const pa_cvolume *pa_source_get_volume(pa_source *s) { +    pa_cvolume old_volume; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); + +    old_volume = s->volume; + +    if (s->get_volume && s->get_volume(s) < 0) +        s->get_volume = NULL; + +    if (!s->get_volume && s->refresh_volume) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); + +    if (!pa_cvolume_equal(&old_volume, &s->volume)) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + +    return &s->volume;  } -const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) { -    assert(s); -    assert(s->ref >= 1); +void pa_source_set_mute(pa_source *s, pa_bool_t mute) { +    int changed; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); + +    changed = s->muted != mute; +    s->muted = mute; -    if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { +    if (s->set_mute && s->set_mute(s) < 0) +        s->set_mute = NULL; -        if (s->get_hw_volume) -            s->get_hw_volume(s); +    if (!s->set_mute) +        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); -        return &s->hw_volume; -    } else -        return &s->sw_volume; +    if (changed) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  } -void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) { -    int *t; +pa_bool_t pa_source_get_mute(pa_source *s) { +    pa_bool_t old_muted; -    assert(s); -    assert(s->ref >= 1); +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); -    if (m == PA_MIXER_HARDWARE && s->set_hw_mute) -        t = &s->hw_muted; -    else -        t = &s->sw_muted; +    old_muted = s->muted; -    if (!!*t == !!mute) -        return; +    if (s->get_mute && s->get_mute(s) < 0) +        s->get_mute = NULL; -    *t = !!mute; +    if (!s->get_mute && s->refresh_muted) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL); -    if (t == &s->hw_muted) -        if (s->set_hw_mute(s) < 0) -            s->sw_muted = !!mute; +    if (old_muted != s->muted) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    return s->muted;  } -int pa_source_get_mute(pa_source *s, pa_mixer_t m) { -    assert(s); -    assert(s->ref >= 1); +void pa_source_set_module(pa_source *s, pa_module *m) { +    pa_source_assert_ref(s); -    if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { +    if (m == s->module) +        return; -        if (s->get_hw_mute) -            s->get_hw_mute(s); +    s->module = m; -        return s->hw_muted; -    } else -        return s->sw_muted; +    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  }  void pa_source_set_description(pa_source *s, const char *description) { -    assert(s); -    assert(s->ref >= 1); +    pa_source_assert_ref(s);      if (!description && !s->description)          return; @@ -336,12 +418,172 @@ void pa_source_set_description(pa_source *s, const char *description) {      pa_xfree(s->description);      s->description = pa_xstrdup(description); -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    if (PA_SOURCE_LINKED(s->state)) { +        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s); +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +    }  } -unsigned pa_source_used_by(pa_source *s) { -    assert(s); -    assert(s->ref >= 1); +void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { +    pa_source_assert_ref(s); +    pa_assert(q); + +    s->asyncmsgq = q; +} + +void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { +    pa_source_assert_ref(s); +    pa_assert(p); + +    s->rtpoll = p; +} + +unsigned pa_source_linked_by(pa_source *s) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state));      return pa_idxset_size(s->outputs);  } + +unsigned pa_source_used_by(pa_source *s) { +    unsigned ret; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); + +    ret = pa_idxset_size(s->outputs); +    pa_assert(ret >= s->n_corked); + +    return ret - s->n_corked; +} + +int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { +    pa_source *s = PA_SOURCE(object); +    pa_source_assert_ref(s); +    pa_assert(s->thread_info.state != PA_SOURCE_UNLINKED); + +    switch ((pa_source_message_t) code) { +        case PA_SOURCE_MESSAGE_ADD_OUTPUT: { +            pa_source_output *o = PA_SOURCE_OUTPUT(userdata); +            pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); + +            pa_assert(!o->thread_info.attached); +            o->thread_info.attached = TRUE; + +            if (o->attach) +                o->attach(o); + +            return 0; +        } + +        case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: { +            pa_source_output *o = PA_SOURCE_OUTPUT(userdata); + +            if (o->detach) +                o->detach(o); + +            pa_assert(o->thread_info.attached); +            o->thread_info.attached = FALSE; + +            if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index))) +                pa_source_output_unref(o); + +            return 0; +        } + +        case PA_SOURCE_MESSAGE_SET_VOLUME: +            s->thread_info.soft_volume = *((pa_cvolume*) userdata); +            return 0; + +        case PA_SOURCE_MESSAGE_SET_MUTE: +            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); +            return 0; + +        case PA_SOURCE_MESSAGE_GET_VOLUME: +            *((pa_cvolume*) userdata) = s->thread_info.soft_volume; +            return 0; + +        case PA_SOURCE_MESSAGE_GET_MUTE: +            *((pa_bool_t*) userdata) = s->thread_info.soft_muted; +            return 0; + +        case PA_SOURCE_MESSAGE_PING: +            return 0; + +        case PA_SOURCE_MESSAGE_SET_STATE: +            s->thread_info.state = PA_PTR_TO_UINT(userdata); +            return 0; + +        case PA_SOURCE_MESSAGE_DETACH: + +            /* We're detaching all our output streams so that the +             * asyncmsgq and rtpoll fields can be changed without +             * problems */ +            pa_source_detach_within_thread(s); +            break; + +        case PA_SOURCE_MESSAGE_ATTACH: + +            /* Reattach all streams */ +            pa_source_attach_within_thread(s); +            break; + +        case PA_SOURCE_MESSAGE_GET_LATENCY: +        case PA_SOURCE_MESSAGE_MAX: +            ; +    } + +    return -1; +} + +int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { +    uint32_t idx; +    pa_source *source; +    int ret = 0; + +    pa_core_assert_ref(c); + +    for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) +        ret -= pa_source_suspend(source, suspend) < 0; + +    return ret; +} + +void pa_source_detach(pa_source *s) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); + +    pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL); +} + +void pa_source_attach(pa_source *s) { +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->state)); + +    pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL); +} + +void pa_source_detach_within_thread(pa_source *s) { +    pa_source_output *o; +    void *state = NULL; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + +    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) +        if (o->detach) +            o->detach(o); +} + +void pa_source_attach_within_thread(pa_source *s) { +    pa_source_output *o; +    void *state = NULL; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + +    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) +        if (o->attach) +            o->attach(o); + +} diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 5a28cf4b..bd0a9122 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -1,5 +1,5 @@ -#ifndef foosourcehfoo -#define foosourcehfoo +#ifndef foopulsesourcehfoo +#define foopulsesourcehfoo  /* $Id$ */ @@ -32,6 +32,7 @@ typedef struct pa_source pa_source;  #include <pulse/sample.h>  #include <pulse/channelmap.h>  #include <pulse/volume.h> +  #include <pulsecore/core-def.h>  #include <pulsecore/core.h>  #include <pulsecore/idxset.h> @@ -39,73 +40,140 @@ typedef struct pa_source pa_source;  #include <pulsecore/memchunk.h>  #include <pulsecore/sink.h>  #include <pulsecore/module.h> +#include <pulsecore/asyncmsgq.h> +#include <pulsecore/msgobject.h> +#include <pulsecore/rtpoll.h> -#define PA_MAX_OUTPUTS_PER_SOURCE 16 +#define PA_MAX_OUTPUTS_PER_SOURCE 32  typedef enum pa_source_state { +    PA_SOURCE_INIT,      PA_SOURCE_RUNNING, -    PA_SOURCE_DISCONNECTED +    PA_SOURCE_SUSPENDED, +    PA_SOURCE_IDLE, +    PA_SOURCE_UNLINKED  } pa_source_state_t; +static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) { +    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; +} + +static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) { +    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED; +} +  struct pa_source { -    int ref; +    pa_msgobject parent; +      uint32_t index;      pa_core *core;      pa_source_state_t state; +    pa_source_flags_t flags;      char *name;      char *description, *driver;              /* may be NULL */ -    pa_module *owner;                        /* may be NULL */ +    pa_module *module;                        /* may be NULL */      pa_sample_spec sample_spec;      pa_channel_map channel_map;      pa_idxset *outputs; +    unsigned n_corked;      pa_sink *monitor_of;                     /* may be NULL */ -    pa_cvolume hw_volume, sw_volume; -    int hw_muted, sw_muted; - -    int is_hardware; +    pa_cvolume volume; +    pa_bool_t muted; +    pa_bool_t refresh_volume; +    pa_bool_t refresh_muted; -    void (*notify)(pa_source*source);        /* may be NULL */ +    int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */ +    int (*set_volume)(pa_source *s);         /* dito */ +    int (*get_volume)(pa_source *s);         /* dito */ +    int (*set_mute)(pa_source *s);           /* dito */ +    int (*get_mute)(pa_source *s);           /* dito */      pa_usec_t (*get_latency)(pa_source *s);  /* dito */ -    int (*set_hw_volume)(pa_source *s);      /* dito */ -    int (*get_hw_volume)(pa_source *s);      /* dito */ -    int (*set_hw_mute)(pa_source *s);        /* dito */ -    int (*get_hw_mute)(pa_source *s);        /* dito */ + +    pa_asyncmsgq *asyncmsgq; +    pa_rtpoll *rtpoll; + +    /* Contains copies of the above data so that the real-time worker +     * thread can work without access locking */ +    struct { +        pa_source_state_t state; +        pa_hashmap *outputs; +        pa_cvolume soft_volume; +        pa_bool_t soft_muted; +    } thread_info;      void *userdata;  }; +PA_DECLARE_CLASS(pa_source); +#define PA_SOURCE(s) pa_source_cast(s) + +typedef enum pa_source_message { +    PA_SOURCE_MESSAGE_ADD_OUTPUT, +    PA_SOURCE_MESSAGE_REMOVE_OUTPUT, +    PA_SOURCE_MESSAGE_GET_VOLUME, +    PA_SOURCE_MESSAGE_SET_VOLUME, +    PA_SOURCE_MESSAGE_GET_MUTE, +    PA_SOURCE_MESSAGE_SET_MUTE, +    PA_SOURCE_MESSAGE_GET_LATENCY, +    PA_SOURCE_MESSAGE_SET_STATE, +    PA_SOURCE_MESSAGE_PING, +    PA_SOURCE_MESSAGE_ATTACH, +    PA_SOURCE_MESSAGE_DETACH, +    PA_SOURCE_MESSAGE_MAX +} pa_source_message_t; + +/* To be called exclusively by the source driver, from main context */ +  pa_source* pa_source_new( -    pa_core *core, -    const char *driver, -    const char *name, -    int namereg_fail, -    const pa_sample_spec *spec, -    const pa_channel_map *map); - -void pa_source_disconnect(pa_source *s); -void pa_source_unref(pa_source *s); -pa_source* pa_source_ref(pa_source *c); - -/* Pass a new memory block to all output streams */ -void pa_source_post(pa_source*s, const pa_memchunk *b); +        pa_core *core, +        const char *driver, +        const char *name, +        int namereg_fail, +        const pa_sample_spec *spec, +        const pa_channel_map *map); + +void pa_source_put(pa_source *s); +void pa_source_unlink(pa_source *s); -void pa_source_notify(pa_source *s); +void pa_source_set_module(pa_source *s, pa_module *m); +void pa_source_set_description(pa_source *s, const char *description); +void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q); +void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p); + +void pa_source_detach(pa_source *s); +void pa_source_attach(pa_source *s); -void pa_source_set_owner(pa_source *s, pa_module *m); +/* May be called by everyone, from main context */  pa_usec_t pa_source_get_latency(pa_source *s); -void pa_source_set_volume(pa_source *source, pa_mixer_t m, const pa_cvolume *volume); -const pa_cvolume *pa_source_get_volume(pa_source *source, pa_mixer_t m); -void pa_source_set_mute(pa_source *source, pa_mixer_t m, int mute); -int pa_source_get_mute(pa_source *source, pa_mixer_t m); +int pa_source_update_status(pa_source*s); +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_description(pa_source *s, const char *description); +void pa_source_ping(pa_source *s); + +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_mute(pa_source *source, pa_bool_t mute); +pa_bool_t pa_source_get_mute(pa_source *source); + +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 */ +#define pa_source_get_state(s) ((pa_source_state_t) (s)->state) + +/* To be called exclusively by the source driver, from IO context */ + +void pa_source_post(pa_source*s, const pa_memchunk *b); + +int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk); + +void pa_source_attach_within_thread(pa_source *s); +void pa_source_detach_within_thread(pa_source *s); -unsigned pa_source_used_by(pa_source *s);  #endif diff --git a/src/pulsecore/speex/Makefile b/src/pulsecore/speex/Makefile new file mode 100644 index 00000000..316beb72 --- /dev/null +++ b/src/pulsecore/speex/Makefile @@ -0,0 +1,13 @@ +# 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 diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h new file mode 100644 index 00000000..4be693c3 --- /dev/null +++ b/src/pulsecore/speex/arch.h @@ -0,0 +1,197 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** +   @file arch.h +   @brief Various architecture definitions Speex +*/ +/* +   Redistribution and use in source and binary forms, with or without +   modification, are permitted provided that the following conditions +   are met: + +   - Redistributions of source code must retain the above copyright +   notice, this list of conditions and the following disclaimer. + +   - Redistributions in binary form must reproduce the above copyright +   notice, this list of conditions and the following disclaimer in the +   documentation and/or other materials provided with the distribution. + +   - Neither the name of the Xiph.org Foundation nor the names of its +   contributors may be used to endorse or promote products derived from +   this software without specific prior written permission. + +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR +   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARCH_H +#define ARCH_H + +#ifndef OUTSIDE_SPEEX +#include "speex/speex_types.h" +#endif + +#define ABS(x) ((x) < 0 ? (-(x)) : (x))      /**< Absolute integer value. */ +#define ABS16(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 16-bit value.  */ +#define MIN16(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 16-bit value.   */ +#define MAX16(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 16-bit value.   */ +#define ABS32(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 32-bit value.  */ +#define MIN32(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 32-bit value.   */ +#define MAX32(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 32-bit value.   */ + +#ifdef FIXED_POINT + +typedef spx_int16_t spx_word16_t; +typedef spx_int32_t   spx_word32_t; +typedef spx_word32_t spx_mem_t; +typedef spx_word16_t spx_coef_t; +typedef spx_word16_t spx_lsp_t; +typedef spx_word32_t spx_sig_t; + +#define Q15ONE 32767 + +#define LPC_SCALING  8192 +#define SIG_SCALING  16384 +#define LSP_SCALING  8192. +#define GAMMA_SCALING 32768. +#define GAIN_SCALING 64 +#define GAIN_SCALING_1 0.015625 + +#define LPC_SHIFT    13 +#define LSP_SHIFT    13 +#define SIG_SHIFT    14 + +#define VERY_SMALL 0 +#define VERY_LARGE32 ((spx_word32_t)2147483647) +#define VERY_LARGE16 ((spx_word16_t)32767) +#define Q15_ONE ((spx_word16_t)32767) + + +#ifdef FIXED_DEBUG +#include "fixed_debug.h" +#else + +#include "fixed_generic.h" + +#ifdef ARM5E_ASM +#include "fixed_arm5e.h" +#elif defined (ARM4_ASM) +#include "fixed_arm4.h" +#elif defined (ARM5E_ASM) +#include "fixed_arm5e.h" +#elif defined (BFIN_ASM) +#include "fixed_bfin.h" +#endif + +#endif + + +#else + +typedef float spx_mem_t; +typedef float spx_coef_t; +typedef float spx_lsp_t; +typedef float spx_sig_t; +typedef float spx_word16_t; +typedef float spx_word32_t; + +#define Q15ONE 1.0f +#define LPC_SCALING  1.f +#define SIG_SCALING  1.f +#define LSP_SCALING  1.f +#define GAMMA_SCALING 1.f +#define GAIN_SCALING 1.f +#define GAIN_SCALING_1 1.f + +#define LPC_SHIFT    0 +#define LSP_SHIFT    0 +#define SIG_SHIFT    0 + +#define VERY_SMALL 1e-15f +#define VERY_LARGE32 1e15f +#define VERY_LARGE16 1e15f +#define Q15_ONE ((spx_word16_t)1.f) + +#define QCONST16(x,bits) (x) +#define QCONST32(x,bits) (x) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) (x) +#define EXTEND32(x) (x) +#define SHR16(a,shift) (a) +#define SHL16(a,shift) (a) +#define SHR32(a,shift) (a) +#define SHL32(a,shift) (a) +#define PSHR16(a,shift) (a) +#define PSHR32(a,shift) (a) +#define VSHR32(a,shift) (a) +#define SATURATE16(x,a) (x) +#define SATURATE32(x,a) (x) + +#define PSHR(a,shift)       (a) +#define SHR(a,shift)       (a) +#define SHL(a,shift)       (a) +#define SATURATE(x,a) (x) + +#define ADD16(a,b) ((a)+(b)) +#define SUB16(a,b) ((a)-(b)) +#define ADD32(a,b) ((a)+(b)) +#define SUB32(a,b) ((a)-(b)) +#define MULT16_16_16(a,b)     ((a)*(b)) +#define MULT16_16(a,b)     ((spx_word32_t)(a)*(spx_word32_t)(b)) +#define MAC16_16(c,a,b)     ((c)+(spx_word32_t)(a)*(spx_word32_t)(b)) + +#define MULT16_32_Q11(a,b)     ((a)*(b)) +#define MULT16_32_Q13(a,b)     ((a)*(b)) +#define MULT16_32_Q14(a,b)     ((a)*(b)) +#define MULT16_32_Q15(a,b)     ((a)*(b)) +#define MULT16_32_P15(a,b)     ((a)*(b)) + +#define MAC16_32_Q11(c,a,b)     ((c)+(a)*(b)) +#define MAC16_32_Q15(c,a,b)     ((c)+(a)*(b)) + +#define MAC16_16_Q11(c,a,b)     ((c)+(a)*(b)) +#define MAC16_16_Q13(c,a,b)     ((c)+(a)*(b)) +#define MAC16_16_P13(c,a,b)     ((c)+(a)*(b)) +#define MULT16_16_Q11_32(a,b)     ((a)*(b)) +#define MULT16_16_Q13(a,b)     ((a)*(b)) +#define MULT16_16_Q14(a,b)     ((a)*(b)) +#define MULT16_16_Q15(a,b)     ((a)*(b)) +#define MULT16_16_P15(a,b)     ((a)*(b)) +#define MULT16_16_P13(a,b)     ((a)*(b)) +#define MULT16_16_P14(a,b)     ((a)*(b)) + +#define DIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define PDIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define DIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b)) +#define PDIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b)) + + +#endif + + +#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + +/* 2 on TI C5x DSP */ +#define BYTES_PER_CHAR 2 +#define BITS_PER_CHAR 16 +#define LOG2_BITS_PER_CHAR 4 + +#else + +#define BYTES_PER_CHAR 1 +#define BITS_PER_CHAR 8 +#define LOG2_BITS_PER_CHAR 3 + +#endif + +#endif diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h new file mode 100644 index 00000000..547e22c7 --- /dev/null +++ b/src/pulsecore/speex/fixed_generic.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** +   @file fixed_generic.h +   @brief Generic fixed-point operations +*/ +/* +   Redistribution and use in source and binary forms, with or without +   modification, are permitted provided that the following conditions +   are met: + +   - Redistributions of source code must retain the above copyright +   notice, this list of conditions and the following disclaimer. + +   - Redistributions in binary form must reproduce the above copyright +   notice, this list of conditions and the following disclaimer in the +   documentation and/or other materials provided with the distribution. + +   - Neither the name of the Xiph.org Foundation nor the names of its +   contributors may be used to endorse or promote products derived from +   this software without specific prior written permission. + +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR +   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_GENERIC_H +#define FIXED_GENERIC_H + +#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) +#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) ((spx_word16_t)(x)) +#define EXTEND32(x) ((spx_word32_t)(x)) +#define SHR16(a,shift) ((a) >> (shift)) +#define SHL16(a,shift) ((a) << (shift)) +#define SHR32(a,shift) ((a) >> (shift)) +#define SHL32(a,shift) ((a) << (shift)) +#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift)) +#define PSHR32(a,shift) (SHR32((a)+((1<<((shift))>>1)),shift)) +#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) +#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) +#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + +#define SHR(a,shift) ((a) >> (shift)) +#define SHL(a,shift) ((spx_word32_t)(a) << (shift)) +#define PSHR(a,shift) (SHR((a)+((1<<((shift))>>1)),shift)) +#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + + +#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b))) +#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b)) +#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b)) +#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b)) + + +/* result fits in 16 bits */ +#define MULT16_16_16(a,b)     ((((spx_word16_t)(a))*((spx_word16_t)(b)))) + +/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */ +#define MULT16_16(a,b)     (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b))) + +#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) +#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12)) +#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) +#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) + +#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)) +#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))) + +#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + + +#define MAC16_16_Q11(c,a,b)     (ADD32((c),SHR(MULT16_16((a),(b)),11))) +#define MAC16_16_Q13(c,a,b)     (ADD32((c),SHR(MULT16_16((a),(b)),13))) +#define MAC16_16_P13(c,a,b)     (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13))) + +#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) +#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) +#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) +#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + +#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) +#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) +#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + +#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15)) + +#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b)))) +#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b)))) +#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b))) +#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b))) + +#endif diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c new file mode 100644 index 00000000..bf1f88b4 --- /dev/null +++ b/src/pulsecore/speex/resample.c @@ -0,0 +1,1114 @@ +/* Copyright (C) 2007 Jean-Marc Valin + +   File: resample.c +   Arbitrary resampling code + +   Redistribution and use in source and binary forms, with or without +   modification, are permitted provided that the following conditions are +   met: + +   1. Redistributions of source code must retain the above copyright notice, +   this list of conditions and the following disclaimer. + +   2. Redistributions in binary form must reproduce the above copyright +   notice, this list of conditions and the following disclaimer in the +   documentation and/or other materials provided with the distribution. + +   3. The name of the author may not be used to endorse or promote products +   derived from this software without specific prior written permission. + +   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +   POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +   The design goals of this code are: +      - Very fast algorithm +      - SIMD-friendly algorithm +      - Low memory requirement +      - Good *perceptual* quality (and not best SNR) + +   The code is working, but it's in a very early stage, so it may have +   artifacts, noise or subliminal messages from satan. Also, the API +   isn't stable and I can actually promise that I *will* change the API +   some time in the future. + +TODO list: +      - Variable calculation resolution depending on quality setting +         - Single vs double in float mode +         - 16-bit vs 32-bit (sinc only) in fixed-point mode +      - Make sure the filter update works even when changing params +             after only a few samples procesed +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef OUTSIDE_SPEEX +#include <stdlib.h> +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} +#include "speex_resampler.h" +#include "arch.h" +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_resampler.h" +#include "misc.h" +#endif /* OUTSIDE_SPEEX */ + +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159263 +#endif + +#ifdef FIXED_POINT +#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x))) +#else +#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x)))) +#endif + +/*#define float double*/ +#define FILTER_SIZE 64 +#define OVERSAMPLE 8 + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef NULL +#define NULL 0 +#endif + +typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); + +struct SpeexResamplerState_ { +   spx_uint32_t in_rate; +   spx_uint32_t out_rate; +   spx_uint32_t num_rate; +   spx_uint32_t den_rate; + +   int    quality; +   spx_uint32_t nb_channels; +   spx_uint32_t filt_len; +   spx_uint32_t mem_alloc_size; +   int          int_advance; +   int          frac_advance; +   float  cutoff; +   spx_uint32_t oversample; +   int          initialised; +   int          started; + +   /* These are per-channel */ +   spx_int32_t  *last_sample; +   spx_uint32_t *samp_frac_num; +   spx_uint32_t *magic_samples; + +   spx_word16_t *mem; +   spx_word16_t *sinc_table; +   spx_uint32_t sinc_table_length; +   resampler_basic_func resampler_ptr; + +   int    in_stride; +   int    out_stride; +} ; + +static double kaiser12_table[68] = { +   0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, +   0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, +   0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, +   0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, +   0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, +   0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, +   0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, +   0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, +   0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, +   0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, +   0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, +   0.00001000, 0.00000000}; +/* +static double kaiser12_table[36] = { +   0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, +   0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, +   0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, +   0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, +   0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, +   0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +*/ +static double kaiser10_table[36] = { +   0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, +   0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, +   0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, +   0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, +   0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, +   0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + +static double kaiser8_table[36] = { +   0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, +   0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, +   0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, +   0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, +   0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, +   0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + +static double kaiser6_table[36] = { +   0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, +   0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, +   0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, +   0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, +   0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, +   0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + +struct FuncDef { +   double *table; +   int oversample; +}; + +static struct FuncDef _KAISER12 = {kaiser12_table, 64}; +#define KAISER12 (&_KAISER12) +/*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; +#define KAISER12 (&_KAISER12)*/ +static struct FuncDef _KAISER10 = {kaiser10_table, 32}; +#define KAISER10 (&_KAISER10) +static struct FuncDef _KAISER8 = {kaiser8_table, 32}; +#define KAISER8 (&_KAISER8) +static struct FuncDef _KAISER6 = {kaiser6_table, 32}; +#define KAISER6 (&_KAISER6) + +struct QualityMapping { +   int base_length; +   int oversample; +   float downsample_bandwidth; +   float upsample_bandwidth; +   struct FuncDef *window_func; +}; + + +/* This table maps conversion quality to internal parameters. There are two +   reasons that explain why the up-sampling bandwidth is larger than the +   down-sampling bandwidth: +   1) When up-sampling, we can assume that the spectrum is already attenuated +      close to the Nyquist rate (from an A/D or a previous resampling filter) +   2) Any aliasing that occurs very close to the Nyquist rate will be masked +      by the sinusoids/noise just below the Nyquist rate (guaranteed only for +      up-sampling). +*/ +static const struct QualityMapping quality_map[11] = { +   {  8,  4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ +   { 16,  4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ +   { 32,  4, 0.882f, 0.910f, KAISER6 }, /* Q2 */  /* 82.3% cutoff ( ~60 dB stop) 6  */ +   { 48,  8, 0.895f, 0.917f, KAISER8 }, /* Q3 */  /* 84.9% cutoff ( ~80 dB stop) 8  */ +   { 64,  8, 0.921f, 0.940f, KAISER8 }, /* Q4 */  /* 88.7% cutoff ( ~80 dB stop) 8  */ +   { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */  /* 89.1% cutoff (~100 dB stop) 10 */ +   { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */  /* 91.5% cutoff (~100 dB stop) 10 */ +   {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */  /* 93.1% cutoff (~100 dB stop) 10 */ +   {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */  /* 94.5% cutoff (~100 dB stop) 10 */ +   {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */  /* 95.5% cutoff (~100 dB stop) 10 */ +   {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +}; +/*8,24,40,56,80,104,128,160,200,256,320*/ +static double compute_func(float x, struct FuncDef *func) +{ +   float y, frac; +   double interp[4]; +   int ind; +   y = x*func->oversample; +   ind = (int)floor(y); +   frac = (y-ind); +   /* CSE with handle the repeated powers */ +   interp[3] =  -0.1666666667*frac + 0.1666666667*(frac*frac*frac); +   interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); +   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ +   interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); +   /* Just to make sure we don't have rounding problems */ +   interp[1] = 1.f-interp[3]-interp[2]-interp[0]; + +   /*sum = frac*accum[1] + (1-frac)*accum[2];*/ +   return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; +} + +#if 0 +#include <stdio.h> +int main(int argc, char **argv) +{ +   int i; +   for (i=0;i<256;i++) +   { +      printf ("%f\n", compute_func(i/256., KAISER12)); +   } +   return 0; +} +#endif + +#ifdef FIXED_POINT +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ +   /*fprintf (stderr, "%f ", x);*/ +   float xx = x * cutoff; +   if (fabs(x)<1e-6f) +      return WORD2INT(32768.*cutoff); +   else if (fabs(x) > .5f*N) +      return 0; +   /*FIXME: Can it really be any slower than this? */ +   return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); +} +#else +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ +   /*fprintf (stderr, "%f ", x);*/ +   float xx = x * cutoff; +   if (fabs(x)<1e-6) +      return cutoff; +   else if (fabs(x) > .5*N) +      return 0; +   /*FIXME: Can it really be any slower than this? */ +   return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); +} +#endif + +#ifdef FIXED_POINT +static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) +{ +   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation +   but I know it's MMSE-optimal on a sinc */ +   spx_word16_t x2, x3; +   x2 = MULT16_16_P15(x, x); +   x3 = MULT16_16_P15(x, x2); +   interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); +   interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); +   interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); +   /* Just to make sure we don't have rounding problems */ +   interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; +   if (interp[2]<32767) +      interp[2]+=1; +} +#else +static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) +{ +   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation +   but I know it's MMSE-optimal on a sinc */ +   interp[0] =  -0.16667f*frac + 0.16667f*frac*frac*frac; +   interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; +   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ +   interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; +   /* Just to make sure we don't have rounding problems */ +   interp[2] = 1.-interp[0]-interp[1]-interp[3]; +} +#endif + +static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ +   int N = st->filt_len; +   int out_sample = 0; +   spx_word16_t *mem; +   int last_sample = st->last_sample[channel_index]; +   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; +   mem = st->mem + channel_index * st->mem_alloc_size; +   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) +   { +      int j; +      spx_word32_t sum=0; + +      /* We already have all the filter coefficients pre-computed in the table */ +      const spx_word16_t *ptr; +      /* Do the memory part */ +      for (j=0;last_sample-N+1+j < 0;j++) +      { +         sum += MULT16_16(mem[last_sample+j],st->sinc_table[samp_frac_num*st->filt_len+j]); +      } + +      /* Do the new part */ +      ptr = in+st->in_stride*(last_sample-N+1+j); +      for (;j<N;j++) +      { +         sum += MULT16_16(*ptr,st->sinc_table[samp_frac_num*st->filt_len+j]); +         ptr += st->in_stride; +      } + +      *out = PSHR32(sum,15); +      out += st->out_stride; +      out_sample++; +      last_sample += st->int_advance; +      samp_frac_num += st->frac_advance; +      if (samp_frac_num >= st->den_rate) +      { +         samp_frac_num -= st->den_rate; +         last_sample++; +      } +   } +   st->last_sample[channel_index] = last_sample; +   st->samp_frac_num[channel_index] = samp_frac_num; +   return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ +   int N = st->filt_len; +   int out_sample = 0; +   spx_word16_t *mem; +   int last_sample = st->last_sample[channel_index]; +   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; +   mem = st->mem + channel_index * st->mem_alloc_size; +   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) +   { +      int j; +      double sum=0; + +      /* We already have all the filter coefficients pre-computed in the table */ +      const spx_word16_t *ptr; +      /* Do the memory part */ +      for (j=0;last_sample-N+1+j < 0;j++) +      { +         sum += MULT16_16(mem[last_sample+j],(double)st->sinc_table[samp_frac_num*st->filt_len+j]); +      } + +      /* Do the new part */ +      ptr = in+st->in_stride*(last_sample-N+1+j); +      for (;j<N;j++) +      { +         sum += MULT16_16(*ptr,(double)st->sinc_table[samp_frac_num*st->filt_len+j]); +         ptr += st->in_stride; +      } + +      *out = sum; +      out += st->out_stride; +      out_sample++; +      last_sample += st->int_advance; +      samp_frac_num += st->frac_advance; +      if (samp_frac_num >= st->den_rate) +      { +         samp_frac_num -= st->den_rate; +         last_sample++; +      } +   } +   st->last_sample[channel_index] = last_sample; +   st->samp_frac_num[channel_index] = samp_frac_num; +   return out_sample; +} +#endif + +static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ +   int N = st->filt_len; +   int out_sample = 0; +   spx_word16_t *mem; +   int last_sample = st->last_sample[channel_index]; +   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; +   mem = st->mem + channel_index * st->mem_alloc_size; +   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) +   { +      int j; +      spx_word32_t sum=0; + +      /* We need to interpolate the sinc filter */ +      spx_word32_t accum[4] = {0.f,0.f, 0.f, 0.f}; +      spx_word16_t interp[4]; +      const spx_word16_t *ptr; +      int offset; +      spx_word16_t frac; +      offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT +      frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else +      frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif +         /* This code is written like this to make it easy to optimise with SIMD. +      For most DSPs, it would be best to split the loops in two because most DSPs +      have only two accumulators */ +      for (j=0;last_sample-N+1+j < 0;j++) +      { +         spx_word16_t curr_mem = mem[last_sample+j]; +         accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]); +         accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]); +         accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]); +         accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]); +      } +      ptr = in+st->in_stride*(last_sample-N+1+j); +      /* Do the new part */ +      for (;j<N;j++) +      { +         spx_word16_t curr_in = *ptr; +         ptr += st->in_stride; +         accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]); +         accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); +         accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); +         accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); +      } +      cubic_coef(frac, interp); +      sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); + +      *out = PSHR32(sum,15); +      out += st->out_stride; +      out_sample++; +      last_sample += st->int_advance; +      samp_frac_num += st->frac_advance; +      if (samp_frac_num >= st->den_rate) +      { +         samp_frac_num -= st->den_rate; +         last_sample++; +      } +   } +   st->last_sample[channel_index] = last_sample; +   st->samp_frac_num[channel_index] = samp_frac_num; +   return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ +   int N = st->filt_len; +   int out_sample = 0; +   spx_word16_t *mem; +   int last_sample = st->last_sample[channel_index]; +   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; +   mem = st->mem + channel_index * st->mem_alloc_size; +   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) +   { +      int j; +      spx_word32_t sum=0; + +      /* We need to interpolate the sinc filter */ +      double accum[4] = {0.f,0.f, 0.f, 0.f}; +      float interp[4]; +      const spx_word16_t *ptr; +      float alpha = ((float)samp_frac_num)/st->den_rate; +      int offset = samp_frac_num*st->oversample/st->den_rate; +      float frac = alpha*st->oversample - offset; +         /* This code is written like this to make it easy to optimise with SIMD. +      For most DSPs, it would be best to split the loops in two because most DSPs +      have only two accumulators */ +      for (j=0;last_sample-N+1+j < 0;j++) +      { +         double curr_mem = mem[last_sample+j]; +         accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]); +         accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]); +         accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]); +         accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]); +      } +      ptr = in+st->in_stride*(last_sample-N+1+j); +      /* Do the new part */ +      for (;j<N;j++) +      { +         double curr_in = *ptr; +         ptr += st->in_stride; +         accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]); +         accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); +         accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); +         accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); +      } +      cubic_coef(frac, interp); +      sum = interp[0]*accum[0] + interp[1]*accum[1] + interp[2]*accum[2] + interp[3]*accum[3]; + +      *out = PSHR32(sum,15); +      out += st->out_stride; +      out_sample++; +      last_sample += st->int_advance; +      samp_frac_num += st->frac_advance; +      if (samp_frac_num >= st->den_rate) +      { +         samp_frac_num -= st->den_rate; +         last_sample++; +      } +   } +   st->last_sample[channel_index] = last_sample; +   st->samp_frac_num[channel_index] = samp_frac_num; +   return out_sample; +} +#endif + +static void update_filter(SpeexResamplerState *st) +{ +   spx_uint32_t old_length; + +   old_length = st->filt_len; +   st->oversample = quality_map[st->quality].oversample; +   st->filt_len = quality_map[st->quality].base_length; + +   if (st->num_rate > st->den_rate) +   { +      /* down-sampling */ +      st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; +      /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ +      st->filt_len = st->filt_len*st->num_rate / st->den_rate; +      /* Round down to make sure we have a multiple of 4 */ +      st->filt_len &= (~0x3); +      if (2*st->den_rate < st->num_rate) +         st->oversample >>= 1; +      if (4*st->den_rate < st->num_rate) +         st->oversample >>= 1; +      if (8*st->den_rate < st->num_rate) +         st->oversample >>= 1; +      if (16*st->den_rate < st->num_rate) +         st->oversample >>= 1; +      if (st->oversample < 1) +         st->oversample = 1; +   } else { +      /* up-sampling */ +      st->cutoff = quality_map[st->quality].upsample_bandwidth; +   } + +   /* Choose the resampling type that requires the least amount of memory */ +   if (st->den_rate <= st->oversample) +   { +      spx_uint32_t i; +      if (!st->sinc_table) +         st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t)); +      else if (st->sinc_table_length < st->filt_len*st->den_rate) +      { +         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t)); +         st->sinc_table_length = st->filt_len*st->den_rate; +      } +      for (i=0;i<st->den_rate;i++) +      { +         spx_int32_t j; +         for (j=0;j<st->filt_len;j++) +         { +            st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); +         } +      } +#ifdef FIXED_POINT +      st->resampler_ptr = resampler_basic_direct_single; +#else +      if (st->quality>8) +         st->resampler_ptr = resampler_basic_direct_double; +      else +         st->resampler_ptr = resampler_basic_direct_single; +#endif +      /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ +   } else { +      spx_int32_t i; +      if (!st->sinc_table) +         st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); +      else if (st->sinc_table_length < st->filt_len*st->oversample+8) +      { +         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); +         st->sinc_table_length = st->filt_len*st->oversample+8; +      } +      for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) +         st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); +#ifdef FIXED_POINT +      st->resampler_ptr = resampler_basic_interpolate_single; +#else +      if (st->quality>8) +         st->resampler_ptr = resampler_basic_interpolate_double; +      else +         st->resampler_ptr = resampler_basic_interpolate_single; +#endif +      /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ +   } +   st->int_advance = st->num_rate/st->den_rate; +   st->frac_advance = st->num_rate%st->den_rate; + + +   /* Here's the place where we update the filter memory to take into account +      the change in filter length. It's probably the messiest part of the code +      due to handling of lots of corner cases. */ +   if (!st->mem) +   { +      spx_uint32_t i; +      st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t)); +      for (i=0;i<st->nb_channels*(st->filt_len-1);i++) +         st->mem[i] = 0; +      st->mem_alloc_size = st->filt_len-1; +      /*speex_warning("init filter");*/ +   } else if (!st->started) +   { +      spx_uint32_t i; +      st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t)); +      for (i=0;i<st->nb_channels*(st->filt_len-1);i++) +         st->mem[i] = 0; +      st->mem_alloc_size = st->filt_len-1; +      /*speex_warning("reinit filter");*/ +   } else if (st->filt_len > old_length) +   { +      spx_int32_t i; +      /* Increase the filter length */ +      /*speex_warning("increase filter size");*/ +      int old_alloc_size = st->mem_alloc_size; +      if (st->filt_len-1 > st->mem_alloc_size) +      { +         st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t)); +         st->mem_alloc_size = st->filt_len-1; +      } +      for (i=st->nb_channels-1;i>=0;i--) +      { +         spx_int32_t j; +         spx_uint32_t olen = old_length; +         /*if (st->magic_samples[i])*/ +         { +            /* Try and remove the magic samples as if nothing had happened */ + +            /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ +            olen = old_length + 2*st->magic_samples[i]; +            for (j=old_length-2+st->magic_samples[i];j>=0;j--) +               st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; +            for (j=0;j<st->magic_samples[i];j++) +               st->mem[i*st->mem_alloc_size+j] = 0; +            st->magic_samples[i] = 0; +         } +         if (st->filt_len > olen) +         { +            /* If the new filter length is still bigger than the "augmented" length */ +            /* Copy data going backward */ +            for (j=0;j<olen-1;j++) +               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; +            /* Then put zeros for lack of anything better */ +            for (;j<st->filt_len-1;j++) +               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; +            /* Adjust last_sample */ +            st->last_sample[i] += (st->filt_len - olen)/2; +         } else { +            /* Put back some of the magic! */ +            st->magic_samples[i] = (olen - st->filt_len)/2; +            for (j=0;j<st->filt_len-1+st->magic_samples[i];j++) +               st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; +         } +      } +   } else if (st->filt_len < old_length) +   { +      spx_uint32_t i; +      /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" +         samples so they can be used directly as input the next time(s) */ +      for (i=0;i<st->nb_channels;i++) +      { +         spx_uint32_t j; +         spx_uint32_t old_magic = st->magic_samples[i]; +         st->magic_samples[i] = (old_length - st->filt_len)/2; +         /* We must copy some of the memory that's no longer used */ +         /* Copy data going backward */ +         for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++) +            st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; +         st->magic_samples[i] += old_magic; +      } +   } + +} + +SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ +   return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); +} + +SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ +   spx_uint32_t i; +   SpeexResamplerState *st; +   if (quality > 10 || quality < 0) +   { +      if (err) +         *err = RESAMPLER_ERR_INVALID_ARG; +      return NULL; +   } +   st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); +   st->initialised = 0; +   st->started = 0; +   st->in_rate = 0; +   st->out_rate = 0; +   st->num_rate = 0; +   st->den_rate = 0; +   st->quality = -1; +   st->sinc_table_length = 0; +   st->mem_alloc_size = 0; +   st->filt_len = 0; +   st->mem = 0; +   st->resampler_ptr = 0; + +   st->cutoff = 1.f; +   st->nb_channels = nb_channels; +   st->in_stride = 1; +   st->out_stride = 1; + +   /* Per channel data */ +   st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int)); +   st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); +   st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); +   for (i=0;i<nb_channels;i++) +   { +      st->last_sample[i] = 0; +      st->magic_samples[i] = 0; +      st->samp_frac_num[i] = 0; +   } + +   speex_resampler_set_quality(st, quality); +   speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); + + +   update_filter(st); + +   st->initialised = 1; +   if (err) +      *err = RESAMPLER_ERR_SUCCESS; + +   return st; +} + +void speex_resampler_destroy(SpeexResamplerState *st) +{ +   speex_free(st->mem); +   speex_free(st->sinc_table); +   speex_free(st->last_sample); +   speex_free(st->magic_samples); +   speex_free(st->samp_frac_num); +   speex_free(st); +} + + + +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ +   int j=0; +   int N = st->filt_len; +   int out_sample = 0; +   spx_word16_t *mem; +   spx_uint32_t tmp_out_len = 0; +   mem = st->mem + channel_index * st->mem_alloc_size; +   st->started = 1; + +   /* Handle the case where we have samples left from a reduction in filter length */ +   if (st->magic_samples[channel_index]) +   { +      int istride_save; +      spx_uint32_t tmp_in_len; +      spx_uint32_t tmp_magic; + +      istride_save = st->in_stride; +      tmp_in_len = st->magic_samples[channel_index]; +      tmp_out_len = *out_len; +      /* magic_samples needs to be set to zero to avoid infinite recursion */ +      tmp_magic = st->magic_samples[channel_index]; +      st->magic_samples[channel_index] = 0; +      st->in_stride = 1; +      speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len); +      st->in_stride = istride_save; +      /*speex_warning_int("extra samples:", tmp_out_len);*/ +      /* If we couldn't process all "magic" input samples, save the rest for next time */ +      if (tmp_in_len < tmp_magic) +      { +         spx_uint32_t i; +         st->magic_samples[channel_index] = tmp_magic-tmp_in_len; +         for (i=0;i<st->magic_samples[channel_index];i++) +            mem[N-1+i]=mem[N-1+i+tmp_in_len]; +      } +      out += tmp_out_len*st->out_stride; +      *out_len -= tmp_out_len; +   } + +   /* Call the right resampler through the function ptr */ +   out_sample = st->resampler_ptr(st, channel_index, in, in_len, out, out_len); + +   if (st->last_sample[channel_index] < (spx_int32_t)*in_len) +      *in_len = st->last_sample[channel_index]; +   *out_len = out_sample+tmp_out_len; +   st->last_sample[channel_index] -= *in_len; + +   for (j=0;j<N-1-(spx_int32_t)*in_len;j++) +      mem[j] = mem[j+*in_len]; +   for (;j<N-1;j++) +      mem[j] = in[st->in_stride*(j+*in_len-N+1)]; + +   return RESAMPLER_ERR_SUCCESS; +} + +#define FIXED_STACK_ALLOC 1024 + +#ifdef FIXED_POINT +int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ +   spx_uint32_t i; +   int istride_save, ostride_save; +#ifdef VAR_ARRAYS +   spx_word16_t x[*in_len]; +   spx_word16_t y[*out_len]; +   /*VARDECL(spx_word16_t *x); +   VARDECL(spx_word16_t *y); +   ALLOC(x, *in_len, spx_word16_t); +   ALLOC(y, *out_len, spx_word16_t);*/ +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   for (i=0;i<*in_len;i++) +      x[i] = WORD2INT(in[i*st->in_stride]); +   st->in_stride = st->out_stride = 1; +   speex_resampler_process_native(st, channel_index, x, in_len, y, out_len); +   st->in_stride = istride_save; +   st->out_stride = ostride_save; +   for (i=0;i<*out_len;i++) +      out[i*st->out_stride] = y[i]; +#else +   spx_word16_t x[FIXED_STACK_ALLOC]; +   spx_word16_t y[FIXED_STACK_ALLOC]; +   spx_uint32_t ilen=*in_len, olen=*out_len; +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   while (ilen && olen) +   { +      spx_uint32_t ichunk, ochunk; +      ichunk = ilen; +      ochunk = olen; +      if (ichunk>FIXED_STACK_ALLOC) +         ichunk=FIXED_STACK_ALLOC; +      if (ochunk>FIXED_STACK_ALLOC) +         ochunk=FIXED_STACK_ALLOC; +      for (i=0;i<ichunk;i++) +         x[i] = WORD2INT(in[i*st->in_stride]); +      st->in_stride = st->out_stride = 1; +      speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk); +      st->in_stride = istride_save; +      st->out_stride = ostride_save; +      for (i=0;i<ochunk;i++) +         out[i*st->out_stride] = y[i]; +      out += ochunk; +      in += ichunk; +      ilen -= ichunk; +      olen -= ochunk; +   } +   *in_len -= ilen; +   *out_len -= olen; +#endif +   return RESAMPLER_ERR_SUCCESS; +} +int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ +   return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); +} +#else +int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ +   return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); +} +int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ +   spx_uint32_t i; +   int istride_save, ostride_save; +#ifdef VAR_ARRAYS +   spx_word16_t x[*in_len]; +   spx_word16_t y[*out_len]; +   /*VARDECL(spx_word16_t *x); +   VARDECL(spx_word16_t *y); +   ALLOC(x, *in_len, spx_word16_t); +   ALLOC(y, *out_len, spx_word16_t);*/ +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   for (i=0;i<*in_len;i++) +      x[i] = in[i*st->in_stride]; +   st->in_stride = st->out_stride = 1; +   speex_resampler_process_native(st, channel_index, x, in_len, y, out_len); +   st->in_stride = istride_save; +   st->out_stride = ostride_save; +   for (i=0;i<*out_len;i++) +      out[i*st->out_stride] = WORD2INT(y[i]); +#else +   spx_word16_t x[FIXED_STACK_ALLOC]; +   spx_word16_t y[FIXED_STACK_ALLOC]; +   spx_uint32_t ilen=*in_len, olen=*out_len; +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   while (ilen && olen) +   { +      spx_uint32_t ichunk, ochunk; +      ichunk = ilen; +      ochunk = olen; +      if (ichunk>FIXED_STACK_ALLOC) +         ichunk=FIXED_STACK_ALLOC; +      if (ochunk>FIXED_STACK_ALLOC) +         ochunk=FIXED_STACK_ALLOC; +      for (i=0;i<ichunk;i++) +         x[i] = in[i*st->in_stride]; +      st->in_stride = st->out_stride = 1; +      speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk); +      st->in_stride = istride_save; +      st->out_stride = ostride_save; +      for (i=0;i<ochunk;i++) +         out[i*st->out_stride] = WORD2INT(y[i]); +      out += ochunk; +      in += ichunk; +      ilen -= ichunk; +      olen -= ochunk; +   } +   *in_len -= ilen; +   *out_len -= olen; +#endif +   return RESAMPLER_ERR_SUCCESS; +} +#endif + +int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ +   spx_uint32_t i; +   int istride_save, ostride_save; +   spx_uint32_t bak_len = *out_len; +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   st->in_stride = st->out_stride = st->nb_channels; +   for (i=0;i<st->nb_channels;i++) +   { +      *out_len = bak_len; +      speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); +   } +   st->in_stride = istride_save; +   st->out_stride = ostride_save; +   return RESAMPLER_ERR_SUCCESS; +} + + +int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ +   spx_uint32_t i; +   int istride_save, ostride_save; +   spx_uint32_t bak_len = *out_len; +   istride_save = st->in_stride; +   ostride_save = st->out_stride; +   st->in_stride = st->out_stride = st->nb_channels; +   for (i=0;i<st->nb_channels;i++) +   { +      *out_len = bak_len; +      speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); +   } +   st->in_stride = istride_save; +   st->out_stride = ostride_save; +   return RESAMPLER_ERR_SUCCESS; +} + +int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ +   return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); +} + +void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) +{ +   *in_rate = st->in_rate; +   *out_rate = st->out_rate; +} + +int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ +   spx_uint32_t fact; +   spx_uint32_t old_den; +   spx_uint32_t i; +   if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) +      return RESAMPLER_ERR_SUCCESS; + +   old_den = st->den_rate; +   st->in_rate = in_rate; +   st->out_rate = out_rate; +   st->num_rate = ratio_num; +   st->den_rate = ratio_den; +   /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ +   for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) +   { +      while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) +      { +         st->num_rate /= fact; +         st->den_rate /= fact; +      } +   } + +   if (old_den > 0) +   { +      for (i=0;i<st->nb_channels;i++) +      { +         st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; +         /* Safety net */ +         if (st->samp_frac_num[i] >= st->den_rate) +            st->samp_frac_num[i] = st->den_rate-1; +      } +   } + +   if (st->initialised) +      update_filter(st); +   return RESAMPLER_ERR_SUCCESS; +} + +void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) +{ +   *ratio_num = st->num_rate; +   *ratio_den = st->den_rate; +} + +int speex_resampler_set_quality(SpeexResamplerState *st, int quality) +{ +   if (quality > 10 || quality < 0) +      return RESAMPLER_ERR_INVALID_ARG; +   if (st->quality == quality) +      return RESAMPLER_ERR_SUCCESS; +   st->quality = quality; +   if (st->initialised) +      update_filter(st); +   return RESAMPLER_ERR_SUCCESS; +} + +void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) +{ +   *quality = st->quality; +} + +void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ +   st->in_stride = stride; +} + +void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ +   *stride = st->in_stride; +} + +void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ +   st->out_stride = stride; +} + +void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ +   *stride = st->out_stride; +} + +int speex_resampler_skip_zeros(SpeexResamplerState *st) +{ +   spx_uint32_t i; +   for (i=0;i<st->nb_channels;i++) +      st->last_sample[i] = st->filt_len/2; +   return RESAMPLER_ERR_SUCCESS; +} + +int speex_resampler_reset_mem(SpeexResamplerState *st) +{ +   spx_uint32_t i; +   for (i=0;i<st->nb_channels*(st->filt_len-1);i++) +      st->mem[i] = 0; +   return RESAMPLER_ERR_SUCCESS; +} + +const char *speex_resampler_strerror(int err) +{ +   switch (err) +   { +      case RESAMPLER_ERR_SUCCESS: +         return "Success."; +      case RESAMPLER_ERR_ALLOC_FAILED: +         return "Memory allocation failed."; +      case RESAMPLER_ERR_BAD_STATE: +         return "Bad resampler state."; +      case RESAMPLER_ERR_INVALID_ARG: +         return "Invalid argument."; +      case RESAMPLER_ERR_PTR_OVERLAP: +         return "Input and output buffers overlap."; +      default: +         return "Unknown error. Bad error code or strange version mismatch."; +   } +} diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h new file mode 100644 index 00000000..8629eeb3 --- /dev/null +++ b/src/pulsecore/speex/speex_resampler.h @@ -0,0 +1,328 @@ +/* Copyright (C) 2007 Jean-Marc Valin + +   File: speex_resampler.h +   Resampling code + +   The design goals of this code are: +      - Very fast algorithm +      - Low memory requirement +      - Good *perceptual* quality (and not best SNR) + +   Redistribution and use in source and binary forms, with or without +   modification, are permitted provided that the following conditions are +   met: + +   1. Redistributions of source code must retain the above copyright notice, +   this list of conditions and the following disclaimer. + +   2. Redistributions in binary form must reproduce the above copyright +   notice, this list of conditions and the following disclaimer in the +   documentation and/or other materials provided with the distribution. + +   3. The name of the author may not be used to endorse or promote products +   derived from this software without specific prior written permission. + +   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +   POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef SPEEX_RESAMPLER_H +#define SPEEX_RESAMPLER_H + +#ifdef OUTSIDE_SPEEX + +/********* WARNING: MENTAL SANITY ENDS HERE *************/ + +/* If the resampler is defined outside of Speex, we change the symbol names so that +   there won't be any clash if linking with Speex later on. */ + +/* #define RANDOM_PREFIX your software name here */ +#ifndef RANDOM_PREFIX +#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes" +#endif + +#define CAT_PREFIX2(a,b) a ## b +#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b) + +#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init) +#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac) +#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy) +#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float) +#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int) +#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float) +#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int) +#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate) +#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate) +#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac) +#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio) +#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality) +#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality) +#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride) +#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride) +#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride) +#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) +#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) +#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) +#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) + +#define spx_int16_t short +#define spx_int32_t int +#define spx_uint16_t unsigned short +#define spx_uint32_t unsigned int + +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_types.h" + +#endif /* OUTSIDE_SPEEX */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPEEX_RESAMPLER_QUALITY_MAX 10 +#define SPEEX_RESAMPLER_QUALITY_MIN 0 +#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4 +#define SPEEX_RESAMPLER_QUALITY_VOIP 3 +#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5 + +enum { +   RESAMPLER_ERR_SUCCESS         = 0, +   RESAMPLER_ERR_ALLOC_FAILED    = 1, +   RESAMPLER_ERR_BAD_STATE       = 2, +   RESAMPLER_ERR_INVALID_ARG     = 3, +   RESAMPLER_ERR_PTR_OVERLAP     = 4, + +   RESAMPLER_ERR_MAX_ERROR +}; + +struct SpeexResamplerState_; +typedef struct SpeexResamplerState_ SpeexResamplerState; + +/** Create a new resampler with integer input and output rates. + * @param nb_channels Number of channels to be processed + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, +                                          spx_uint32_t in_rate, +                                          spx_uint32_t out_rate, +                                          int quality, +                                          int *err); + +/** Create a new resampler with fractional input/output rates. The sampling + * rate ratio is an arbitrary rational number with both the numerator and + * denominator being 32-bit integers. + * @param nb_channels Number of channels to be processed + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, +                                               spx_uint32_t ratio_num, +                                               spx_uint32_t ratio_den, +                                               spx_uint32_t in_rate, +                                               spx_uint32_t out_rate, +                                               int quality, +                                               int *err); + +/** Destroy a resampler state. + * @param st Resampler state + */ +void speex_resampler_destroy(SpeexResamplerState *st); + +/** Resample a float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the + * number of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_float(SpeexResamplerState *st, +                                   spx_uint32_t channel_index, +                                   const float *in, +                                   spx_uint32_t *in_len, +                                   float *out, +                                   spx_uint32_t *out_len); + +/** Resample an int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_int(SpeexResamplerState *st, +                                 spx_uint32_t channel_index, +                                 const spx_int16_t *in, +                                 spx_uint32_t *in_len, +                                 spx_int16_t *out, +                                 spx_uint32_t *out_len); + +/** Resample an interleaved float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_float(SpeexResamplerState *st, +                                               const float *in, +                                               spx_uint32_t *in_len, +                                               float *out, +                                               spx_uint32_t *out_len); + +/** Resample an interleaved int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_int(SpeexResamplerState *st, +                                             const spx_int16_t *in, +                                             spx_uint32_t *in_len, +                                             spx_int16_t *out, +                                             spx_uint32_t *out_len); + +/** Set (change) the input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + */ +int speex_resampler_set_rate(SpeexResamplerState *st, +                              spx_uint32_t in_rate, +                              spx_uint32_t out_rate); + +/** Get the current input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz) copied. + * @param out_rate Output sampling rate (integer number of Hz) copied. + */ +void speex_resampler_get_rate(SpeexResamplerState *st, +                              spx_uint32_t *in_rate, +                              spx_uint32_t *out_rate); + +/** Set (change) the input/output sampling rates and resampling ratio + * (fractional values in Hz supported). + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + */ +int speex_resampler_set_rate_frac(SpeexResamplerState *st, +                                   spx_uint32_t ratio_num, +                                   spx_uint32_t ratio_den, +                                   spx_uint32_t in_rate, +                                   spx_uint32_t out_rate); + +/** Get the current resampling ratio. This will be reduced to the least + * common denominator. + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio copied + * @param ratio_den Denominator of the sampling rate ratio copied + */ +void speex_resampler_get_ratio(SpeexResamplerState *st, +                               spx_uint32_t *ratio_num, +                               spx_uint32_t *ratio_den); + +/** Set (change) the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +int speex_resampler_set_quality(SpeexResamplerState *st, +                                 int quality); + +/** Get the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +void speex_resampler_get_quality(SpeexResamplerState *st, +                                 int *quality); + +/** Set (change) the input stride. + * @param st Resampler state + * @param stride Input stride + */ +void speex_resampler_set_input_stride(SpeexResamplerState *st, +                                      spx_uint32_t stride); + +/** Get the input stride. + * @param st Resampler state + * @param stride Input stride copied + */ +void speex_resampler_get_input_stride(SpeexResamplerState *st, +                                      spx_uint32_t *stride); + +/** Set (change) the output stride. + * @param st Resampler state + * @param stride Output stride + */ +void speex_resampler_set_output_stride(SpeexResamplerState *st, +                                      spx_uint32_t stride); + +/** Get the output stride. + * @param st Resampler state copied + * @param stride Output stride + */ +void speex_resampler_get_output_stride(SpeexResamplerState *st, +                                      spx_uint32_t *stride); + +/** Make sure that the first samples to go out of the resamplers don't have + * leading zeros. This is only useful before starting to use a newly created + * resampler. It is recommended to use that when resampling an audio file, as + * it will generate a file with the same length. For real-time processing, + * it is probably easier not to use this call (so that the output duration + * is the same for the first frame). + * @param st Resampler state + */ +int speex_resampler_skip_zeros(SpeexResamplerState *st); + +/** Reset a resampler so a new (unrelated) stream can be processed. + * @param st Resampler state + */ +int speex_resampler_reset_mem(SpeexResamplerState *st); + +/** Returns the English meaning for an error code + * @param err Error code + * @return English string + */ +const char *speex_resampler_strerror(int err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h new file mode 100644 index 00000000..c0d5c0c0 --- /dev/null +++ b/src/pulsecore/speexwrap.h @@ -0,0 +1,48 @@ +#ifndef foopulsespeexwraphfoo +#define foopulsespeexwraphfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB + +  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. +***/ + +/* We define a minimal version of speex_resampler.h however define one + * version for fixed and another one for float. Yes, somewhat ugly */ + +#define spx_int16_t short +#define spx_int32_t int +#define spx_uint16_t unsigned short +#define spx_uint32_t unsigned int + +typedef struct SpeexResamplerState_ SpeexResamplerState; + +SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate,  int quality, int *err); +void paspfx_resampler_destroy(SpeexResamplerState *st); +int paspfx_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in,  spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len); +int paspfx_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate); + +SpeexResamplerState *paspfl_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate,  int quality, int *err); +void paspfl_resampler_destroy(SpeexResamplerState *st); +int paspfl_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in,  spx_uint32_t *in_len, float *out, spx_uint32_t *out_len); +int paspfl_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate); + +#endif diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index a3ddc114..7c576c67 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -27,12 +27,12 @@  #include <sys/types.h>  #include <stdlib.h> -#include <assert.h>  #include <string.h>  #include <stdarg.h>  #include <stdio.h>  #include <pulse/xmalloc.h> +#include <pulsecore/macro.h>  #include "strbuf.h" @@ -42,7 +42,7 @@ struct chunk {      size_t length;  }; -#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk)) +#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))  struct pa_strbuf {      size_t length; @@ -50,14 +50,18 @@ struct pa_strbuf {  };  pa_strbuf *pa_strbuf_new(void) { -    pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf)); +    pa_strbuf *sb; + +    sb = pa_xnew(pa_strbuf, 1);      sb->length = 0;      sb->head = sb->tail = NULL; +      return sb;  }  void pa_strbuf_free(pa_strbuf *sb) { -    assert(sb); +    pa_assert(sb); +      while (sb->head) {          struct chunk *c = sb->head;          sb->head = sb->head->next; @@ -72,12 +76,13 @@ void pa_strbuf_free(pa_strbuf *sb) {  char *pa_strbuf_tostring(pa_strbuf *sb) {      char *t, *e;      struct chunk *c; -    assert(sb); -    e = t = pa_xmalloc(sb->length+1); +    pa_assert(sb); + +    e = t = pa_xnew(char, sb->length+1);      for (c = sb->head; c; c = c->next) { -        assert((size_t) (e-t) <= sb->length); +        pa_assert((size_t) (e-t) <= sb->length);          memcpy(e, CHUNK_TO_TEXT(c), c->length);          e += c->length;      } @@ -85,7 +90,7 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {      /* Trailing NUL */      *e = 0; -    assert(e == t+sb->length); +    pa_assert(e == t+sb->length);      return t;  } @@ -93,27 +98,33 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {  /* Combination of pa_strbuf_free() and pa_strbuf_tostring() */  char *pa_strbuf_tostring_free(pa_strbuf *sb) {      char *t; -    assert(sb); + +    pa_assert(sb);      t = pa_strbuf_tostring(sb);      pa_strbuf_free(sb); +      return t;  }  /* Append a string to the string buffer */  void pa_strbuf_puts(pa_strbuf *sb, const char *t) { -    assert(sb && t); + +    pa_assert(sb); +    pa_assert(t); +      pa_strbuf_putsn(sb, t, strlen(t));  }  /* Append a new chunk to the linked list */  static void append(pa_strbuf *sb, struct chunk *c) { -    assert(sb && c); +    pa_assert(sb); +    pa_assert(c);      if (sb->tail) { -        assert(sb->head); +        pa_assert(sb->head);          sb->tail->next = c;      } else { -        assert(!sb->head); +        pa_assert(!sb->head);          sb->head = c;      } @@ -125,12 +136,14 @@ static void append(pa_strbuf *sb, struct chunk *c) {  /* Append up to l bytes of a string to the string buffer */  void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {      struct chunk *c; -    assert(sb && t); + +    pa_assert(sb); +    pa_assert(t);      if (!l)         return; -    c = pa_xmalloc(sizeof(struct chunk)+l); +    c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);      c->length = l;      memcpy(CHUNK_TO_TEXT(c), t, l); @@ -143,16 +156,18 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {      int size = 100;      struct chunk *c = NULL; -    assert(sb); +    pa_assert(sb); +    pa_assert(format);      for(;;) {          va_list ap;          int r; -        c = pa_xrealloc(c, sizeof(struct chunk)+size); +        c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);          va_start(ap, format);          r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); +        CHUNK_TO_TEXT(c)[size-1] = 0;          va_end(ap);          if (r > -1 && r < size) { diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c index 955b78e4..792af0ff 100644 --- a/src/pulsecore/strlist.c +++ b/src/pulsecore/strlist.c @@ -26,26 +26,31 @@  #endif  #include <string.h> -#include <assert.h>  #include <pulse/xmalloc.h>  #include <pulsecore/strbuf.h> +#include <pulsecore/macro.h>  #include <pulsecore/core-util.h>  #include "strlist.h"  struct pa_strlist {      pa_strlist *next; -    char *str;  }; +#define ITEM_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(pa_strlist))) +  pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {      pa_strlist *n; -    assert(s); -    n = pa_xmalloc(sizeof(pa_strlist)); -    n->str = pa_xstrdup(s); +    size_t size; + +    pa_assert(s); +    size = strlen(s); +    n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1); +    memcpy(ITEM_TO_TEXT(n), s, size + 1);      n->next = l; +      return  n;  } @@ -58,7 +63,7 @@ char *pa_strlist_tostring(pa_strlist *l) {          if (!first)              pa_strbuf_puts(b, " ");          first = 0; -        pa_strbuf_puts(b, l->str); +        pa_strbuf_puts(b, ITEM_TO_TEXT(l));      }      return pa_strbuf_tostring_free(b); @@ -66,19 +71,20 @@ char *pa_strlist_tostring(pa_strlist *l) {  pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {      pa_strlist *ret = l, *prev = NULL; -    assert(l && s); + +    pa_assert(l); +    pa_assert(s);      while (l) { -        if (!strcmp(l->str, s)) { +        if (!strcmp(ITEM_TO_TEXT(l), s)) {              pa_strlist *n = l->next;              if (!prev) { -                assert(ret == l); +                pa_assert(ret == l);                  ret = n;              } else                  prev->next = n; -            pa_xfree(l->str);              pa_xfree(l);              l = n; @@ -96,22 +102,21 @@ void pa_strlist_free(pa_strlist *l) {      while (l) {          pa_strlist *c = l;          l = l->next; - -        pa_xfree(c->str);          pa_xfree(c);      }  }  pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {      pa_strlist *r; -    assert(s); + +    pa_assert(s);      if (!l) {          *s = NULL;          return NULL;      } -    *s = l->str; +    *s = pa_xstrdup(ITEM_TO_TEXT(l));      r = l->next;      pa_xfree(l);      return r; @@ -124,10 +129,12 @@ pa_strlist* pa_strlist_parse(const char *s) {      while ((r = pa_split_spaces(s, &state))) {          pa_strlist *n; +        size_t size = strlen(r); -        n = pa_xmalloc(sizeof(pa_strlist)); -        n->str = r; +        n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);          n->next = NULL; +        memcpy(ITEM_TO_TEXT(n), r, size+1); +        pa_xfree(r);          if (p)              p->next = n; diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index ac7ae1ab..556fe806 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -29,19 +29,18 @@  #include <string.h>  #include <unistd.h>  #include <sys/time.h> -#include <assert.h>  #include <stdarg.h>  #ifdef HAVE_NETINET_IN_H  #include <netinet/in.h>  #endif -#include "winsock.h" -  #include <pulse/xmalloc.h> -#include "tagstruct.h" +#include <pulsecore/winsock.h> +#include <pulsecore/macro.h> +#include "tagstruct.h"  struct pa_tagstruct {      uint8_t *data; @@ -54,18 +53,20 @@ struct pa_tagstruct {  pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {      pa_tagstruct*t; -    assert(!data || (data && length)); +    pa_assert(!data || (data && length)); -    t = pa_xmalloc(sizeof(pa_tagstruct)); +    t = pa_xnew(pa_tagstruct, 1);      t->data = (uint8_t*) data;      t->allocated = t->length = data ? length : 0;      t->rindex = 0;      t->dynamic = !data; +      return t;  }  void pa_tagstruct_free(pa_tagstruct*t) { -    assert(t); +    pa_assert(t); +      if (t->dynamic)          pa_xfree(t->data);      pa_xfree(t); @@ -73,7 +74,11 @@ void pa_tagstruct_free(pa_tagstruct*t) {  uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {      uint8_t *p; -    assert(t && t->dynamic && l); + +    pa_assert(t); +    pa_assert(t->dynamic); +    pa_assert(l); +      p = t->data;      *l = t->length;      pa_xfree(t); @@ -81,8 +86,8 @@ uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {  }  static void extend(pa_tagstruct*t, size_t l) { -    assert(t); -    assert(t->dynamic); +    pa_assert(t); +    pa_assert(t->dynamic);      if (t->length+l <= t->allocated)          return; @@ -92,7 +97,8 @@ static void extend(pa_tagstruct*t, size_t l) {  void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {      size_t l; -    assert(t); +    pa_assert(t); +      if (s) {          l = strlen(s)+2;          extend(t, l); @@ -107,7 +113,8 @@ void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {  }  void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) { -    assert(t); +    pa_assert(t); +      extend(t, 5);      t->data[t->length] = PA_TAG_U32;      i = htonl(i); @@ -116,7 +123,8 @@ void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {  }  void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) { -    assert(t); +    pa_assert(t); +      extend(t, 2);      t->data[t->length] = PA_TAG_U8;      *(t->data+t->length+1) = c; @@ -125,7 +133,10 @@ void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {  void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {      uint32_t rate; -    assert(t && ss); + +    pa_assert(t); +    pa_assert(ss); +      extend(t, 7);      t->data[t->length] = PA_TAG_SAMPLE_SPEC;      t->data[t->length+1] = (uint8_t) ss->format; @@ -137,7 +148,9 @@ void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {  void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {      uint32_t tmp; -    assert(t && p); + +    pa_assert(t); +    pa_assert(p);      extend(t, 5+length);      t->data[t->length] = PA_TAG_ARBITRARY; @@ -149,7 +162,8 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {  }  void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) { -    assert(t); +    pa_assert(t); +      extend(t, 1);      t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;      t->length += 1; @@ -157,7 +171,8 @@ void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {  void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {      uint32_t tmp; -    assert(t); +    pa_assert(t); +      extend(t, 9);      t->data[t->length] = PA_TAG_TIMEVAL;      tmp = htonl(tv->tv_sec); @@ -169,7 +184,9 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {  void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {      uint32_t tmp; -    assert(t); + +    pa_assert(t); +      extend(t, 9);      t->data[t->length] = PA_TAG_USEC;      tmp = htonl((uint32_t) (u >> 32)); @@ -181,7 +198,9 @@ void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {  void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {      uint32_t tmp; -    assert(t); + +    pa_assert(t); +      extend(t, 9);      t->data[t->length] = PA_TAG_U64;      tmp = htonl((uint32_t) (u >> 32)); @@ -193,7 +212,9 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {  void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {      uint32_t tmp; -    assert(t); + +    pa_assert(t); +      extend(t, 9);      t->data[t->length] = PA_TAG_S64;      tmp = htonl((uint32_t) ((uint64_t) u >> 32)); @@ -206,7 +227,7 @@ void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {  void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {      unsigned i; -    assert(t); +    pa_assert(t);      extend(t, 2 + map->channels);      t->data[t->length++] = PA_TAG_CHANNEL_MAP; @@ -220,7 +241,7 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {      unsigned i;      pa_volume_t vol; -    assert(t); +    pa_assert(t);      extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));      t->data[t->length++] = PA_TAG_CVOLUME; @@ -237,7 +258,9 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {      int error = 0;      size_t n;      char *c; -    assert(t && s); + +    pa_assert(t); +    pa_assert(s);      if (t->rindex+1 > t->length)          return -1; @@ -271,7 +294,8 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {  }  int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) { -    assert(t && i); +    pa_assert(t); +    pa_assert(i);      if (t->rindex+5 > t->length)          return -1; @@ -286,7 +310,8 @@ int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {  }  int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) { -    assert(t && c); +    pa_assert(t); +    pa_assert(c);      if (t->rindex+2 > t->length)          return -1; @@ -300,7 +325,8 @@ int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {  }  int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) { -    assert(t && ss); +    pa_assert(t); +    pa_assert(ss);      if (t->rindex+7 > t->length)          return -1; @@ -319,7 +345,9 @@ int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {  int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {      uint32_t len; -    assert(t && p); + +    pa_assert(t); +    pa_assert(p);      if (t->rindex+5+length > t->length)          return -1; @@ -337,18 +365,23 @@ int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {  }  int pa_tagstruct_eof(pa_tagstruct*t) { -    assert(t); +    pa_assert(t); +      return t->rindex >= t->length;  }  const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { -    assert(t && t->dynamic && l); +    pa_assert(t); +    pa_assert(t->dynamic); +    pa_assert(l); +      *l = t->length;      return t->data;  }  int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { -    assert(t && b); +    pa_assert(t); +    pa_assert(b);      if (t->rindex+1 > t->length)          return -1; @@ -366,6 +399,9 @@ int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {  int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) { +    pa_assert(t); +    pa_assert(tv); +      if (t->rindex+9 > t->length)          return -1; @@ -382,7 +418,9 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {  int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {      uint32_t tmp; -    assert(t && u); + +    pa_assert(t); +    pa_assert(u);      if (t->rindex+9 > t->length)          return -1; @@ -400,7 +438,9 @@ int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {  int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {      uint32_t tmp; -    assert(t && u); + +    pa_assert(t); +    pa_assert(u);      if (t->rindex+9 > t->length)          return -1; @@ -418,7 +458,9 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {  int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {      uint32_t tmp; -    assert(t && u); + +    pa_assert(t); +    pa_assert(u);      if (t->rindex+9 > t->length)          return -1; @@ -437,8 +479,8 @@ int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {  int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {      unsigned i; -    assert(t); -    assert(map); +    pa_assert(t); +    pa_assert(map);      if (t->rindex+2 > t->length)          return -1; @@ -463,8 +505,8 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {      unsigned i;      pa_volume_t vol; -    assert(t); -    assert(cvolume); +    pa_assert(t); +    pa_assert(cvolume);      if (t->rindex+2 > t->length)          return -1; @@ -489,7 +531,7 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {  void pa_tagstruct_put(pa_tagstruct *t, ...) {      va_list va; -    assert(t); +    pa_assert(t);      va_start(va, t); @@ -550,7 +592,7 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {                  break;              default: -                abort(); +                pa_assert_not_reached();          }      } @@ -561,7 +603,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {      va_list va;      int ret = 0; -    assert(t); +    pa_assert(t);      va_start(va, t);      while (ret == 0) { @@ -620,9 +662,8 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {                  ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));                  break; -              default: -                abort(); +                pa_assert_not_reached();          }      } diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c new file mode 100644 index 00000000..9b879425 --- /dev/null +++ b/src/pulsecore/thread-mq.c @@ -0,0 +1,112 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2006 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 <unistd.h> +#include <errno.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/atomic.h> +#include <pulsecore/once.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/semaphore.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulsecore/flist.h> + +#include "thread-mq.h" + +PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq); + +static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { +    pa_thread_mq *q = userdata; +    pa_asyncmsgq *aq; + +    pa_assert(pa_asyncmsgq_get_fd(q->outq) == fd); +    pa_assert(events == PA_IO_EVENT_INPUT); + +    pa_asyncmsgq_ref(aq = q->outq); +    pa_asyncmsgq_after_poll(aq); + +    for (;;) { +        pa_msgobject *object; +        int code; +        void *data; +        int64_t offset; +        pa_memchunk chunk; + +        /* Check whether there is a message for us to process */ +        while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) == 0) { +            int ret; + +            ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk); +            pa_asyncmsgq_done(aq, ret); +        } + +        if (pa_asyncmsgq_before_poll(aq) == 0) +            break; +    } + +    pa_asyncmsgq_unref(aq); +} + +void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) { +    pa_assert(q); +    pa_assert(mainloop); + +    q->mainloop = mainloop; +    pa_assert_se(q->inq = pa_asyncmsgq_new(0)); +    pa_assert_se(q->outq = pa_asyncmsgq_new(0)); + +    pa_assert_se(pa_asyncmsgq_before_poll(q->outq) == 0); +    pa_assert_se(q->io_event = mainloop->io_new(mainloop, pa_asyncmsgq_get_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_cb, q)); +} + +void pa_thread_mq_done(pa_thread_mq *q) { +    pa_assert(q); + +    q->mainloop->io_free(q->io_event); +    q->io_event = NULL; + +    pa_asyncmsgq_unref(q->inq); +    pa_asyncmsgq_unref(q->outq); +    q->inq = q->outq = NULL; + +    q->mainloop = NULL; +} + +void pa_thread_mq_install(pa_thread_mq *q) { +    pa_assert(q); + +    pa_assert(!(PA_STATIC_TLS_GET(thread_mq))); +    PA_STATIC_TLS_SET(thread_mq, q); +} + +pa_thread_mq *pa_thread_mq_get(void) { +    return PA_STATIC_TLS_GET(thread_mq); +} diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h new file mode 100644 index 00000000..13b6e01f --- /dev/null +++ b/src/pulsecore/thread-mq.h @@ -0,0 +1,49 @@ +#ifndef foopulsethreadmqhfoo +#define foopulsethreadmqhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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 <pulse/mainloop-api.h> +#include <pulsecore/asyncmsgq.h> + +/* Two way communication between a thread and a mainloop. Before the + * thread is started a pa_pthread_mq should be initialized and than + * attached to the thread using pa_thread_mq_install(). */ + +typedef struct pa_thread_mq { +    pa_mainloop_api *mainloop; +    pa_asyncmsgq *inq, *outq; +    pa_io_event *io_event; +} pa_thread_mq; + +void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop); +void pa_thread_mq_done(pa_thread_mq *q); + +/* Install the specified pa_thread_mq object for the current thread */ +void pa_thread_mq_install(pa_thread_mq *q); + +/* Return the pa_thread_mq object that is set for the current thread */ +pa_thread_mq *pa_thread_mq_get(void); + +#endif diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c index 4271fa42..7f43f43e 100644 --- a/src/pulsecore/thread-posix.c +++ b/src/pulsecore/thread-posix.c @@ -26,7 +26,6 @@  #include <config.h>  #endif -#include <assert.h>  #include <pthread.h>  #include <sched.h>  #include <errno.h> @@ -35,56 +34,44 @@  #include <pulsecore/mutex.h>  #include <pulsecore/once.h>  #include <pulsecore/atomic.h> +#include <pulsecore/macro.h>  #include "thread.h" -#define ASSERT_SUCCESS(x) do { \ -    int _r = (x); \ -    assert(_r == 0); \ -} while(0) -  struct pa_thread {      pthread_t id;      pa_thread_func_t thread_func;      void *userdata; -    pa_atomic_int_t running; +    pa_atomic_t running;  };  struct pa_tls {      pthread_key_t key;  }; -static pa_tls *thread_tls; -static pa_once_t thread_tls_once = PA_ONCE_INIT; - -static void tls_free_cb(void *p) { +static void thread_free_cb(void *p) {      pa_thread *t = p; -    assert(t); +    pa_assert(t);      if (!t->thread_func)          /* This is a foreign thread, we need to free the struct */          pa_xfree(t);  } -static void thread_tls_once_func(void) { -    thread_tls = pa_tls_new(tls_free_cb); -    assert(thread_tls); -} +PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);  static void* internal_thread_func(void *userdata) {      pa_thread *t = userdata; -    assert(t); +    pa_assert(t);      t->id = pthread_self(); -    pa_once(&thread_tls_once, thread_tls_once_func); - -    pa_tls_set(thread_tls, t); +    PA_STATIC_TLS_SET(current_thread, t);      pa_atomic_inc(&t->running);      t->thread_func(t->userdata); -    pa_atomic_add(&t->running, -2); +    pa_atomic_sub(&t->running, 2);      return NULL;  } @@ -92,7 +79,7 @@ static void* internal_thread_func(void *userdata) {  pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {      pa_thread *t; -    assert(thread_func); +    pa_assert(thread_func);      t = pa_xnew(pa_thread, 1);      t->thread_func = thread_func; @@ -110,26 +97,26 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {  }  int pa_thread_is_running(pa_thread *t) { -    assert(t); +    pa_assert(t);      /* Unfortunately there is no way to tell whether a "foreign"       * thread is still running. See       * http://udrepper.livejournal.com/16844.html for more       * information */ -    assert(t->thread_func); +    pa_assert(t->thread_func);      return pa_atomic_load(&t->running) > 0;  }  void pa_thread_free(pa_thread *t) { -    assert(t); +    pa_assert(t);      pa_thread_join(t);      pa_xfree(t);  }  int pa_thread_join(pa_thread *t) { -    assert(t); +    pa_assert(t);      return pthread_join(t->id, NULL);  } @@ -137,9 +124,7 @@ int pa_thread_join(pa_thread *t) {  pa_thread* pa_thread_self(void) {      pa_thread *t; -    pa_once(&thread_tls_once, thread_tls_once_func); - -    if ((t = pa_tls_get(thread_tls))) +    if ((t = PA_STATIC_TLS_GET(current_thread)))          return t;      /* This is a foreign thread, let's create a pthread structure to @@ -151,19 +136,19 @@ pa_thread* pa_thread_self(void) {      t->userdata = NULL;      pa_atomic_store(&t->running, 2); -    pa_tls_set(thread_tls, t); +    PA_STATIC_TLS_SET(current_thread, t);      return t;  }  void* pa_thread_get_data(pa_thread *t) { -    assert(t); +    pa_assert(t);      return t->userdata;  }  void pa_thread_set_data(pa_thread *t, void *userdata) { -    assert(t); +    pa_assert(t);      t->userdata = userdata;  } @@ -172,7 +157,7 @@ void pa_thread_yield(void) {  #ifdef HAVE_PTHREAD_YIELD      pthread_yield();  #else -    ASSERT_SUCCESS(sched_yield()); +    pa_assert_se(sched_yield() == 0);  #endif  } @@ -190,14 +175,14 @@ pa_tls* pa_tls_new(pa_free_cb_t free_cb) {  }  void pa_tls_free(pa_tls *t) { -    assert(t); +    pa_assert(t); -    ASSERT_SUCCESS(pthread_key_delete(t->key)); +    pa_assert_se(pthread_key_delete(t->key) == 0);      pa_xfree(t);  }  void *pa_tls_get(pa_tls *t) { -    assert(t); +    pa_assert(t);      return pthread_getspecific(t->key);  } @@ -206,7 +191,7 @@ void *pa_tls_set(pa_tls *t, void *userdata) {      void *r;      r = pthread_getspecific(t->key); -    ASSERT_SUCCESS(pthread_setspecific(t->key, userdata)); +    pa_assert_se(pthread_setspecific(t->key, userdata) == 0);      return r;  } diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c index 46d273b4..cad1420a 100644 --- a/src/pulsecore/thread-win32.c +++ b/src/pulsecore/thread-win32.c @@ -53,9 +53,8 @@ struct pa_tls_monitor {  };  static pa_tls *thread_tls; -static pa_once_t thread_tls_once = PA_ONCE_INIT; +static pa_once thread_tls_once = PA_ONCE_INIT;  static pa_tls *monitor_tls; -static pa_once_t monitor_tls_once = PA_ONCE_INIT;  static void thread_tls_once_func(void) {      thread_tls = pa_tls_new(NULL); @@ -66,7 +65,7 @@ static DWORD WINAPI internal_thread_func(LPVOID param) {      pa_thread *t = param;      assert(t); -    pa_once(&thread_tls_once, thread_tls_once_func); +    pa_run_once(&thread_tls_once, thread_tls_once_func);      pa_tls_set(thread_tls, t);      t->thread_func(t->userdata); @@ -122,7 +121,7 @@ int pa_thread_join(pa_thread *t) {  }  pa_thread* pa_thread_self(void) { -    pa_once(&thread_tls_once, thread_tls_once_func); +    pa_run_once(&thread_tls_once, thread_tls_once_func);      return pa_tls_get(thread_tls);  } @@ -130,12 +129,6 @@ void pa_thread_yield(void) {      Sleep(0);  } -static void monitor_tls_once_func(void) { -    monitor_tls = pa_tls_new(NULL); -    assert(monitor_tls); -    pa_tls_set(monitor_tls, NULL); -} -  static DWORD WINAPI monitor_thread_func(LPVOID param) {      struct pa_tls_monitor *m = param;      assert(m); @@ -191,7 +184,11 @@ void *pa_tls_set(pa_tls *t, void *userdata) {      if (t->free_func) {          struct pa_tls_monitor *m; -        pa_once(&monitor_tls_once, monitor_tls_once_func); +        PA_ONCE_BEGIN { +            monitor_tls = pa_tls_new(NULL); +            assert(monitor_tls); +            pa_tls_set(monitor_tls, NULL); +        } PA_ONCE_END;          m = pa_tls_get(monitor_tls);          if (!m) { diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h index ca1fe4da..54ef320e 100644 --- a/src/pulsecore/thread.h +++ b/src/pulsecore/thread.h @@ -26,6 +26,11 @@  ***/  #include <pulse/def.h> +#include <pulsecore/once.h> + +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif  typedef struct pa_thread pa_thread; @@ -48,4 +53,60 @@ void pa_tls_free(pa_tls *t);  void * pa_tls_get(pa_tls *t);  void *pa_tls_set(pa_tls *t, void *userdata); +#define PA_STATIC_TLS_DECLARE(name, free_cb)                            \ +    static struct {                                                     \ +        pa_once once;                                                   \ +        pa_tls *tls;                                                    \ +    } name##_tls = {                                                    \ +        .once = PA_ONCE_INIT,                                           \ +        .tls = NULL                                                     \ +    };                                                                  \ +    static void name##_tls_init(void) {                                 \ +        name##_tls.tls = pa_tls_new(free_cb);                           \ +    }                                                                   \ +    static inline pa_tls* name##_tls_obj(void) {                        \ +        pa_run_once(&name##_tls.once, name##_tls_init);                 \ +        return name##_tls.tls;                                          \ +    }                                                                   \ +    static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR;          \ +    static void name##_tls_destructor(void) {                           \ +        static void (*_free_cb)(void*) = free_cb;                       \ +        if (!name##_tls.tls)                                            \ +            return;                                                     \ +        if (_free_cb) {                                                 \ +            void *p;                                                    \ +            if ((p = pa_tls_get(name##_tls.tls)))                       \ +                _free_cb(p);                                            \ +        }                                                               \ +        pa_tls_free(name##_tls.tls);                                    \ +    }                                                                   \ +    static inline void* name##_tls_get(void) {                          \ +        return pa_tls_get(name##_tls_obj());                            \ +    }                                                                   \ +    static inline void* name##_tls_set(void *p) {                       \ +        return pa_tls_set(name##_tls_obj(), p);                         \ +    }                                                                   \ +    struct __stupid_useless_struct_to_allow_trailing_semicolon + +#ifdef HAVE_TLS_BUILTIN +/* An optimized version of the above that requires no dynamic + * allocation if the compiler supports __thread */ +#define PA_STATIC_TLS_DECLARE_NO_FREE(name)                             \ +    static __thread void *name##_tls = NULL;                            \ +    static inline void* name##_tls_get(void) {                          \ +        return name##_tls;                                              \ +    }                                                                   \ +    static inline void* name##_tls_set(void *p) {                       \ +        void *r = name##_tls;                                           \ +        name##_tls = p;                                                 \ +        return r;                                                       \ +    }                                                                   \ +    struct __stupid_useless_struct_to_allow_trailing_semicolon +#else +#define PA_STATIC_TLS_DECLARE_NO_FREE(name) PA_STATIC_TLS_DECLARE(name, NULL) +#endif + +#define PA_STATIC_TLS_GET(name) (name##_tls_get()) +#define PA_STATIC_TLS_SET(name, p) (name##_tls_set(p)) +  #endif diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c new file mode 100644 index 00000000..6bda3df0 --- /dev/null +++ b/src/pulsecore/time-smoother.c @@ -0,0 +1,378 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  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 <stdio.h> + +#include <pulse/sample.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/macro.h> + +#include "time-smoother.h" + +#define HISTORY_MAX 50 + +/* + * Implementation of a time smoothing algorithm to synchronize remote + * clocks to a local one. Evens out noise, adjusts to clock skew and + * allows cheap estimations of the remote time while clock updates may + * be seldom and recieved in non-equidistant intervals. + * + * Basically, we estimate the gradient of received clock samples in a + * certain history window (of size 'history_time') with linear + * regression. With that info we estimate the remote time in + * 'adjust_time' ahead and smoothen our current estimation function + * towards that point with a 3rd order polynomial interpolation with + * fitting derivatives. (more or less a b-spline) + * + * The larger 'history_time' is chosen the better we will surpress + * noise -- but we'll adjust to clock skew slower.. + * + * The larger 'adjust_time' is chosen the smoother our estimation + * function will be -- but we'll adjust to clock skew slower, too. + * + * If 'monotonic' is TRUE the resulting estimation function is + * guaranteed to be monotonic. + */ + +struct pa_smoother { +    pa_usec_t adjust_time, history_time; +    pa_bool_t monotonic; + +    pa_usec_t time_offset; + +    pa_usec_t px, py;     /* Point p, where we want to reach stability */ +    double dp;            /* Gradient we want at point p */ + +    pa_usec_t ex, ey;     /* Point e, which we estimated before and need to smooth to */ +    double de;            /* Gradient we estimated for point e */ + +                          /* History of last measurements */ +    pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX]; +    unsigned history_idx, n_history; + +    /* To even out for monotonicity */ +    pa_usec_t last_y; + +    /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */ +    double a, b, c; +    pa_bool_t abc_valid; + +    pa_bool_t paused; +    pa_usec_t pause_time; +}; + +pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic) { +    pa_smoother *s; + +    pa_assert(adjust_time > 0); +    pa_assert(history_time > 0); + +    s = pa_xnew(pa_smoother, 1); +    s->adjust_time = adjust_time; +    s->history_time = history_time; +    s->time_offset = 0; +    s->monotonic = monotonic; + +    s->px = s->py = 0; +    s->dp = 1; + +    s->ex = s->ey = 0; +    s->de = 1; + +    s->history_idx = 0; +    s->n_history = 0; + +    s->last_y = 0; + +    s->abc_valid = FALSE; + +    s->paused = FALSE; + +    return s; +} + +void pa_smoother_free(pa_smoother* s) { +    pa_assert(s); + +    pa_xfree(s); +} + +static void drop_old(pa_smoother *s, pa_usec_t x) { +    unsigned j; + +    /* First drop items from history which are too old, but make sure +     * to always keep two entries in the history */ + +    for (j = s->n_history; j > 2; j--) { + +        if (s->history_x[s->history_idx] + s->history_time >= x) { +            /* This item is still valid, and thus all following ones +             * are too, so let's quit this loop */ +            break; +        } + +        /* Item is too old, let's drop it */ +        s->history_idx ++; +        while (s->history_idx >= HISTORY_MAX) +            s->history_idx -= HISTORY_MAX; + +        s->n_history --; +    } +} + +static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) { +    unsigned j; +    pa_assert(s); + +    drop_old(s, x); + +    /* Calculate position for new entry */ +    j = s->history_idx + s->n_history; +    while (j >= HISTORY_MAX) +        j -= HISTORY_MAX; + +    /* Fill in entry */ +    s->history_x[j] = x; +    s->history_y[j] = y; + +    /* Adjust counter */ +    s->n_history ++; + +    /* And make sure we don't store more entries than fit in */ +    if (s->n_history >= HISTORY_MAX) { +        s->history_idx += s->n_history - HISTORY_MAX; +        s->n_history = HISTORY_MAX; +    } +} + +static double avg_gradient(pa_smoother *s, pa_usec_t x) { +    unsigned i, j, c = 0; +    int64_t ax = 0, ay = 0, k, t; +    double r; + +    drop_old(s, x); + +    /* First, calculate average of all measurements */ +    i = s->history_idx; +    for (j = s->n_history; j > 0; j--) { + +        ax += s->history_x[i]; +        ay += s->history_y[i]; +        c++; + +        i++; +        while (i >= HISTORY_MAX) +            i -= HISTORY_MAX; +    } + +    /* Too few measurements, assume gradient of 1 */ +    if (c < 2) +        return 1; + +    ax /= c; +    ay /= c; + +    /* Now, do linear regression */ +    k = t = 0; + +    i = s->history_idx; +    for (j = s->n_history; j > 0; j--) { +        int64_t dx, dy; + +        dx = (int64_t) s->history_x[i] - ax; +        dy = (int64_t) s->history_y[i] - ay; + +        k += dx*dy; +        t += dx*dx; + +        i++; +        while (i >= HISTORY_MAX) +            i -= HISTORY_MAX; +    } + +    r = (double) k / t; + +    return s->monotonic && r < 0 ? 0 : r; +} + +static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { +    pa_assert(s); +    pa_assert(y); + +    if (x >= s->px) { +        int64_t t; + +        /* 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)); + +        if (t < 0) +            t = 0; + +        *y = (pa_usec_t) t; + +        if (deriv) +            *deriv = s->dp; + +    } else { + +        if (!s->abc_valid) { +            pa_usec_t ex, ey, px, py; +            int64_t kx, ky; +            double de, dp; + +            /* Ok, we're not yet on track, thus let's interpolate, and +             * make sure that the first derivative is smooth */ + +            /* We have two points: (ex|ey) and (px|py) with two gradients +             * at these points de and dp. We do a polynomial interpolation +             * of degree 3 with these 6 values */ + +            ex = s->ex; ey = s->ey; +            px = s->px; py = s->py; +            de = s->de; dp = s->dp; + +            pa_assert(ex < px); + +            /* To increase the dynamic range and symplify calculation, we +             * move these values to the origin */ +            kx = (int64_t) px - (int64_t) ex; +            ky = (int64_t) py - (int64_t) ey; + +            /* Calculate a, b, c for y=ax^3+b^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->abc_valid = TRUE; +        } + +        /* Move to origin */ +        x -= s->ex; + +        /* Horner scheme */ +        *y = (pa_usec_t) ((double) x * (s->c + (double) x * (s->b + (double) x * s->a))); + +        /* Move back from origin */ +        *y += s->ey; + +        /* Horner scheme */ +        if (deriv) +            *deriv = s->c + ((double) x * (s->b*2 + (double) x * s->a*3)); +    } + +    /* Guarantee monotonicity */ +    if (s->monotonic) { + +        if (*y < s->last_y) +            *y = s->last_y; +        else +            s->last_y = *y; + +        if (deriv && *deriv < 0) +            *deriv = 0; +    } +} + +void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { +    pa_usec_t ney; +    double nde; + +    pa_assert(s); +    pa_assert(x >= s->time_offset); + +    /* Fix up x value */ +    if (s->paused) +        x = s->pause_time; +    else +        x -= s->time_offset; + +    pa_assert(x >= s->ex); + +    /* First, we calculate the position we'd estimate for x, so that +     * we can adjust our position smoothly from this one */ +    estimate(s, x, &ney, &nde); +    s->ex = x; s->ey = ney; s->de = nde; + +    /* Then, we add the new measurement to our history */ +    add_to_history(s, x, y); + +    /* And determine the average gradient of the history */ +    s->dp = avg_gradient(s, x); + +    /* And calculate when we want to be on track again */ +    s->px = x + s->adjust_time; +    s->py = y + s->dp *s->adjust_time; + +    s->abc_valid = FALSE; +} + +pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { +    pa_usec_t y; + +    pa_assert(s); +    pa_assert(x >= s->time_offset); + +    /* Fix up x value */ +    if (s->paused) +        x = s->pause_time; +    else +        x -= s->time_offset; + +    pa_assert(x >= s->ex); + +    estimate(s, x, &y, NULL); +    return y; +} + +void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) { +    pa_assert(s); + +    s->time_offset = offset; +} + +void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { +    pa_assert(s); + +    if (s->paused) +        return; + +    s->paused = TRUE; +    s->pause_time = x; +} + +void pa_smoother_resume(pa_smoother *s, pa_usec_t x) { +    pa_assert(s); + +    if (!s->paused) +        return; + +    s->paused = FALSE; +    s->time_offset += x - s->pause_time; +} diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h new file mode 100644 index 00000000..8b8512e2 --- /dev/null +++ b/src/pulsecore/time-smoother.h @@ -0,0 +1,43 @@ +#ifndef foopulsetimesmootherhfoo +#define foopulsetimesmootherhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  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> +#include <pulse/sample.h> + +typedef struct pa_smoother pa_smoother; + +pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic); +void pa_smoother_free(pa_smoother* s); + +void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y); +pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x); + +void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset); + +void pa_smoother_pause(pa_smoother *s, pa_usec_t x); +void pa_smoother_resume(pa_smoother *s, pa_usec_t x); + +#endif diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c index 117c7f88..f79c19c5 100644 --- a/src/pulsecore/tokenizer.c +++ b/src/pulsecore/tokenizer.c @@ -26,20 +26,16 @@  #endif  #include <string.h> -#include <assert.h>  #include <stdlib.h>  #include <pulse/xmalloc.h>  #include <pulsecore/dynarray.h>  #include <pulsecore/gccmacro.h> +#include <pulsecore/macro.h>  #include "tokenizer.h" -struct pa_tokenizer { -    pa_dynarray *dynarray; -}; -  static void token_free(void *p, PA_GCC_UNUSED void *userdata) {      pa_xfree(p);  } @@ -48,7 +44,9 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {      int infty = 0;      const char delimiter[] = " \t\n\r";      const char *p; -    assert(a && s); + +    pa_assert(a); +    pa_assert(s);      if (args == 0)          infty = 1; @@ -70,23 +68,23 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {  }  pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { -    pa_tokenizer *t; - -    t = pa_xmalloc(sizeof(pa_tokenizer)); -    t->dynarray = pa_dynarray_new(); -    assert(t->dynarray); +    pa_dynarray *a; -    parse(t->dynarray, s, args); -    return t; +    a = pa_dynarray_new(); +    parse(a, s, args); +    return (pa_tokenizer*) a;  }  void pa_tokenizer_free(pa_tokenizer *t) { -    assert(t); -    pa_dynarray_free(t->dynarray, token_free, NULL); -    pa_xfree(t); +    pa_dynarray *a = (pa_dynarray*) t; + +    pa_assert(a); +    pa_dynarray_free(a, token_free, NULL);  }  const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) { -    assert(t); -    return pa_dynarray_get(t->dynarray, i); +    pa_dynarray *a = (pa_dynarray*) t; + +    pa_assert(a); +    return pa_dynarray_get(a, i);  } diff --git a/src/pulsecore/winsock.h b/src/pulsecore/winsock.h index ae868b38..0352bf4d 100644 --- a/src/pulsecore/winsock.h +++ b/src/pulsecore/winsock.h @@ -15,6 +15,8 @@  #define EHOSTUNREACH    WSAEHOSTUNREACH  #define EWOULDBLOCK     WSAEWOULDBLOCK +typedef long suseconds_t; +  #endif  #ifdef HAVE_WS2TCPIP_H diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c index 5b85ea42..a740e39b 100644 --- a/src/pulsecore/x11prop.c +++ b/src/pulsecore/x11prop.c @@ -32,7 +32,6 @@  #include "x11prop.h" -  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); diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c index 6a6a2692..800a9458 100644 --- a/src/pulsecore/x11wrap.c +++ b/src/pulsecore/x11wrap.c @@ -21,7 +21,10 @@    USA.  ***/ -#include <assert.h> +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +  #include <stdio.h>  #include <pulse/xmalloc.h> @@ -29,6 +32,8 @@  #include <pulsecore/llist.h>  #include <pulsecore/log.h>  #include <pulsecore/props.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h>  #include "x11wrap.h" @@ -42,8 +47,8 @@ struct pa_x11_internal {  };  struct pa_x11_wrapper { +    PA_REFCNT_DECLARE;      pa_core *core; -    int ref;      char *property_name;      Display *display; @@ -64,7 +69,8 @@ struct pa_x11_client {  /* Dispatch all pending X11 events */  static void work(pa_x11_wrapper *w) { -    assert(w && w->ref >= 1); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1);      while (XPending(w->display)) {          pa_x11_client *c; @@ -72,7 +78,7 @@ static void work(pa_x11_wrapper *w) {          XNextEvent(w->display, &e);          for (c = w->clients; c; c = c->next) { -            assert(c->callback); +            pa_assert(c->callback);              if (c->callback(w, &e, c->userdata) != 0)                  break;          } @@ -82,14 +88,24 @@ 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) {      pa_x11_wrapper *w = userdata; -    assert(m && e && fd >= 0 && w && w->ref >= 1); + +    pa_assert(m); +    pa_assert(e); +    pa_assert(fd >= 0); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1); +      work(w);  }  /* Deferred notification event. Called once each main loop iteration */  static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {      pa_x11_wrapper *w = userdata; -    assert(m && e && w && w->ref >= 1); + +    pa_assert(m); +    pa_assert(e); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1);      m->defer_enable(e, 0); @@ -99,7 +115,12 @@ 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) {      pa_x11_wrapper *w = userdata; -    assert(m && e && fd >= 0 && w && w->ref >= 1); + +    pa_assert(m); +    pa_assert(e); +    pa_assert(fd >= 0); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1);      XProcessInternalConnection(w->display, fd); @@ -109,10 +130,9 @@ static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC  /* Add a new IO source for the specified X11 internal connection */  static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {      pa_x11_internal *i; -    assert(fd >= 0); +    pa_assert(fd >= 0); -    i = pa_xmalloc(sizeof(pa_x11_internal)); -    assert(i); +    i = pa_xnew(pa_x11_internal, 1);      i->wrapper = w;      i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);      i->fd = fd; @@ -123,7 +143,7 @@ static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {  /* Remove an IO source for an X11 internal connection */  static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) { -    assert(i); +    pa_assert(i);      PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);      w->core->mainloop->io_free(i->io_event); @@ -133,7 +153,10 @@ static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {  /* Implementation of XConnectionWatchProc */  static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {      pa_x11_wrapper *w = (pa_x11_wrapper*) userdata; -    assert(display && w && fd >= 0); + +    pa_assert(display); +    pa_assert(w); +    pa_assert(fd >= 0);      if (opening)          *watch_data = (XPointer) x11_internal_add(w, fd); @@ -144,16 +167,15 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,  static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {      pa_x11_wrapper*w;      Display *d; -    int r;      if (!(d = XOpenDisplay(name))) {          pa_log("XOpenDisplay() failed");          return NULL;      } -    w = pa_xmalloc(sizeof(pa_x11_wrapper)); +    w = pa_xnew(pa_x11_wrapper, 1); +    PA_REFCNT_INIT(w);      w->core = c; -    w->ref = 1;      w->property_name = pa_xstrdup(t);      w->display = d; @@ -165,20 +187,17 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char      XAddConnectionWatch(d, x11_watch, (XPointer) w); -    r = pa_property_set(c, w->property_name, w); -    assert(r >= 0); +    pa_assert_se(pa_property_set(c, w->property_name, w) >= 0);      return w;  }  static void x11_wrapper_free(pa_x11_wrapper*w) { -    int r; -    assert(w); +    pa_assert(w); -    r = pa_property_remove(w->core, w->property_name); -    assert(r >= 0); +    pa_assert_se(pa_property_remove(w->core, w->property_name) >= 0); -    assert(!w->clients); +    pa_assert(!w->clients);      XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);      XCloseDisplay(w->display); @@ -196,9 +215,10 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {  pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {      char t[256];      pa_x11_wrapper *w; -    assert(c); -    snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : ""); +    pa_core_assert_ref(c); + +    pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");      if ((w = pa_property_get(c, t)))          return pa_x11_wrapper_ref(w); @@ -206,20 +226,24 @@ pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {  }  pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) { -    assert(w && w->ref >= 1); -    w->ref++; +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1); + +    PA_REFCNT_INC(w);      return w;  }  void pa_x11_wrapper_unref(pa_x11_wrapper* w) { -    assert(w && w->ref >= 1); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1); -    if (!(--w->ref)) +    if (PA_REFCNT_DEC(w) <= 0)          x11_wrapper_free(w);  }  Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { -    assert(w && w->ref >= 1); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1);      /* Somebody is using us, schedule a output buffer flush */      w->core->mainloop->defer_enable(w->defer_event, 1); @@ -229,9 +253,11 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {  pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {      pa_x11_client *c; -    assert(w && w->ref >= 1); -    c = pa_xmalloc(sizeof(pa_x11_client)); +    pa_assert(w); +    pa_assert(PA_REFCNT_VALUE(w) >= 1); + +    c = pa_xnew(pa_x11_client, 1);      c->wrapper = w;      c->callback = cb;      c->userdata = userdata; @@ -242,7 +268,9 @@ pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w,  }  void pa_x11_client_free(pa_x11_client *c) { -    assert(c && c->wrapper && c->wrapper->ref >= 1); +    pa_assert(c); +    pa_assert(c->wrapper); +    pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);      PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);      pa_xfree(c); diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c new file mode 100644 index 00000000..380c5e7f --- /dev/null +++ b/src/tests/asyncmsgq-test.c @@ -0,0 +1,110 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> + +#include <pulse/util.h> +#include <pulse/xmalloc.h> +#include <pulsecore/asyncmsgq.h> +#include <pulsecore/thread.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> + +enum { +    OPERATION_A, +    OPERATION_B, +    OPERATION_C, +    QUIT +}; + +static void the_thread(void *_q) { +    pa_asyncmsgq *q = _q; +    int quit = 0; + +    do { +        int code = 0; + +        pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, NULL, NULL, 1) == 0); + +        switch (code) { + +            case OPERATION_A: +                printf("Operation A\n"); +                break; + +            case OPERATION_B: +                printf("Operation B\n"); +                break; + +            case OPERATION_C: +                printf("Operation C\n"); +                break; + +            case QUIT: +                printf("quit\n"); +                quit = 1; +                break; +        } + +        pa_asyncmsgq_done(q, 0); + +    } while (!quit); +} + +int main(int argc, char *argv[]) { +    pa_asyncmsgq *q; +    pa_thread *t; + +    pa_assert_se(q = pa_asyncmsgq_new(0)); + +    pa_assert_se(t = pa_thread_new(the_thread, q)); + +    printf("Operation A post\n"); +    pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, 0, NULL, NULL); + +    pa_thread_yield(); + +    printf("Operation B post\n"); +    pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL); + +    pa_thread_yield(); + +    printf("Operation C send\n"); +    pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL, 0, NULL); + +    pa_thread_yield(); + +    printf("Quit post\n"); +    pa_asyncmsgq_post(q, NULL, QUIT, NULL, 0, NULL, NULL); + +    pa_thread_free(t); + +    pa_asyncmsgq_unref(q); + +    return 0; +} diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c new file mode 100644 index 00000000..09b20047 --- /dev/null +++ b/src/tests/asyncq-test.c @@ -0,0 +1,87 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> + +#include <pulse/util.h> +#include <pulse/xmalloc.h> +#include <pulsecore/asyncq.h> +#include <pulsecore/thread.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> + +static void producer(void *_q) { +    pa_asyncq *q = _q; +    int i; + +    for (i = 0; i < 1000; i++) { +        printf("pushing %i\n", i); +        pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1); +    } + +    pa_asyncq_push(q, PA_UINT_TO_PTR(-1), 1); +    printf("pushed end\n"); +} + +static void consumer(void *_q) { +    pa_asyncq *q = _q; +    void *p; +    int i; + +    sleep(1); + +    for (i = 0;; i++) { +        p = pa_asyncq_pop(q, 1); + +        if (p == PA_UINT_TO_PTR(-1)) +            break; + +        pa_assert(p == PA_UINT_TO_PTR(i+1)); + +        printf("popped %i\n", i); +    } + +    printf("popped end\n"); +} + +int main(int argc, char *argv[]) { +    pa_asyncq *q; +    pa_thread *t1, *t2; + +    pa_assert_se(q = pa_asyncq_new(0)); + +    pa_assert_se(t1 = pa_thread_new(producer, q)); +    pa_assert_se(t2 = pa_thread_new(consumer, q)); + +    pa_thread_free(t1); +    pa_thread_free(t2); + +    pa_asyncq_free(q, NULL); + +    return 0; +} diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c index 6879eae5..8628f521 100644 --- a/src/tests/hook-list-test.c +++ b/src/tests/hook-list-test.c @@ -1,5 +1,9 @@  /* $Id$ */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +  #include <pulsecore/hook-list.h>  #include <pulsecore/log.h> diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c index 3953043f..85a509d4 100644 --- a/src/tests/interpol-test.c +++ b/src/tests/interpol-test.c @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) {              pa_gettimeofday(&now);              rtc = pa_timeval_diff(&now, &start); -            printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\n", k, rtc, t, rtc-old_rtc, t-old_t, changed); +            printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed);              old_t = t;              old_rtc = rtc;          } diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c index db76712b..d1013118 100644 --- a/src/tests/mcalign-test.c +++ b/src/tests/mcalign-test.c @@ -59,24 +59,29 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {              c.index = c.length = 0;          } -        assert(c.index < c.memblock->length); +        assert(c.index < pa_memblock_get_length(c.memblock)); -        l = c.memblock->length - c.index; +        l = pa_memblock_get_length(c.memblock) - c.index;          l = l <= 1 ? l : rand() % (l-1) +1 ; -        if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { +        p = pa_memblock_acquire(c.memblock); + +        if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) { +            pa_memblock_release(c.memblock);              fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");              break;          } +        pa_memblock_release(c.memblock); +          c.length = r;          pa_mcalign_push(a, &c);          fprintf(stderr, "Read %ld bytes\n", (long)r);          c.index += r; -        if (c.index >= c.memblock->length) { +        if (c.index >= pa_memblock_get_length(c.memblock)) {              pa_memblock_unref(c.memblock);              pa_memchunk_reset(&c);          } @@ -87,7 +92,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {              if (pa_mcalign_pop(a, &t) < 0)                  break; -            pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length, NULL); +            p = pa_memblock_acquire(t.memblock); +            pa_loop_write(STDOUT_FILENO, (uint8_t*) p + t.index, t.length, NULL); +            pa_memblock_release(t.memblock);              fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length);              pa_memblock_unref(t.memblock); diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c index 8d25ba38..2b9d3401 100644 --- a/src/tests/memblock-test.c +++ b/src/tests/memblock-test.c @@ -23,11 +23,11 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdio.h>  #include <unistd.h>  #include <pulsecore/memblock.h> +#include <pulsecore/macro.h>  #include <pulse/xmalloc.h>  static void release_cb(pa_memimport *i, uint32_t block_id, void *userdata) { @@ -76,6 +76,7 @@ int main(int argc, char *argv[]) {      pa_memblock* blocks[5];      uint32_t id, shm_id;      size_t offset, size; +    char *x;      const char txt[] = "This is a test!"; @@ -87,13 +88,20 @@ int main(int argc, char *argv[]) {      pa_mempool_get_shm_id(pool_b, &id_b);      pa_mempool_get_shm_id(pool_c, &id_c); -    assert(pool_a && pool_b && pool_c); +    pa_assert(pool_a && pool_b && pool_c);      blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1); +      blocks[1] = pa_memblock_new(pool_a, sizeof(txt)); -    snprintf(blocks[1]->data, blocks[1]->length, "%s", txt); +    x = pa_memblock_acquire(blocks[1]); +    snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt); +    pa_memblock_release(blocks[1]); +      blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt)); -    snprintf(blocks[2]->data, blocks[2]->length, "%s", txt); +    x = pa_memblock_acquire(blocks[2]); +    snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt); +    pa_memblock_release(blocks[2]); +      blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));      blocks[4] = NULL; @@ -101,43 +109,47 @@ int main(int argc, char *argv[]) {          printf("Memory block %u\n", i);          mb_a = blocks[i]; -        assert(mb_a); +        pa_assert(mb_a);          export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A");          export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B"); -        assert(export_a && export_b); +        pa_assert(export_a && export_b);          import_b = pa_memimport_new(pool_b, release_cb, (void*) "B");          import_c = pa_memimport_new(pool_c, release_cb, (void*) "C"); -        assert(import_b && import_c); +        pa_assert(import_b && import_c);          r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size); -        assert(r >= 0); -        assert(shm_id == id_a); +        pa_assert(r >= 0); +        pa_assert(shm_id == id_a);          printf("A: Memory block exported as %u\n", id);          mb_b = pa_memimport_get(import_b, id, shm_id, offset, size); -        assert(mb_b); +        pa_assert(mb_b);          r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size); -        assert(r >= 0); -        assert(shm_id == id_a || shm_id == id_b); +        pa_assert(r >= 0); +        pa_assert(shm_id == id_a || shm_id == id_b);          pa_memblock_unref(mb_b);          printf("B: Memory block exported as %u\n", id);          mb_c = pa_memimport_get(import_c, id, shm_id, offset, size); -        assert(mb_c); -        printf("1 data=%s\n", (char*) mb_c->data); +        pa_assert(mb_c); +        x = pa_memblock_acquire(mb_c); +        printf("1 data=%s\n", x); +        pa_memblock_release(mb_c);          print_stats(pool_a, "A");          print_stats(pool_b, "B");          print_stats(pool_c, "C");          pa_memexport_free(export_b); -        printf("2 data=%s\n", (char*) mb_c->data); +        x = pa_memblock_acquire(mb_c); +        printf("2 data=%s\n", x); +        pa_memblock_release(mb_c);          pa_memblock_unref(mb_c);          pa_memimport_free(import_b); diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 1c0b7fed..25ea399b 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -26,6 +26,7 @@  #include <stdlib.h>  #include <assert.h>  #include <stdio.h> +#include <signal.h>  #include <pulsecore/memblockq.h>  #include <pulsecore/log.h> @@ -48,22 +49,22 @@ int main(int argc, char *argv[]) {      bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, silence);      assert(bq); -    chunk1.memblock = pa_memblock_new_fixed(p, (char*) "AA", 2, 1); +    chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1);      chunk1.index = 0;      chunk1.length = 2;      assert(chunk1.memblock); -    chunk2.memblock = pa_memblock_new_fixed(p, (char*) "TTBB", 4, 1); +    chunk2.memblock = pa_memblock_new_fixed(p, (char*) "XX22", 4, 1);      chunk2.index = 2;      chunk2.length = 2;      assert(chunk2.memblock); -    chunk3.memblock = pa_memblock_new_fixed(p, (char*) "ZZZZ", 4, 1); +    chunk3.memblock = pa_memblock_new_fixed(p, (char*) "3333", 4, 1);      chunk3.index = 0;      chunk3.length = 4;      assert(chunk3.memblock); -    chunk4.memblock = pa_memblock_new_fixed(p, (char*) "KKKKKKKK", 8, 1); +    chunk4.memblock = pa_memblock_new_fixed(p, (char*) "44444444", 8, 1);      chunk4.index = 0;      chunk4.length = 8;      assert(chunk4.memblock); @@ -115,13 +116,12 @@ int main(int argc, char *argv[]) {      chunk3.index += 2;      chunk3.length -= 2; -      ret = pa_memblockq_push(bq, &chunk3);      assert(ret == 0); -    printf(">"); +    pa_memblockq_shorten(bq, pa_memblockq_get_length(bq)-2); -    pa_memblockq_shorten(bq, 6); +    printf(">");      for (;;) {          pa_memchunk out; @@ -131,11 +131,13 @@ int main(int argc, char *argv[]) {          if (pa_memblockq_peek(bq, &out) < 0)              break; -        for (e = (char*) out.memblock->data + out.index, n = 0; n < out.length; n++) +        p = pa_memblock_acquire(out.memblock); +        for (e = (char*) p + out.index, n = 0; n < out.length; n++)              printf("%c", *e); +        pa_memblock_release(out.memblock);          pa_memblock_unref(out.memblock); -        pa_memblockq_drop(bq, &out, out.length); +        pa_memblockq_drop(bq, out.length);      }      printf("<\n"); diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c new file mode 100644 index 00000000..b357ab10 --- /dev/null +++ b/src/tests/queue-test.c @@ -0,0 +1,69 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> + +#include <pulse/util.h> +#include <pulse/xmalloc.h> +#include <pulsecore/queue.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> + +int main(int argc, char *argv[]) { +    pa_queue *q; + +    pa_assert_se(q = pa_queue_new()); + +    pa_assert(pa_queue_is_empty(q)); + +    pa_queue_push(q, (void*) "eins"); +    pa_log("%s\n", (char*) pa_queue_pop(q)); + +    pa_assert(pa_queue_is_empty(q)); + +    pa_queue_push(q, (void*) "zwei"); +    pa_queue_push(q, (void*) "drei"); +    pa_queue_push(q, (void*) "vier"); + +    pa_log("%s\n", (char*) pa_queue_pop(q)); +    pa_log("%s\n", (char*) pa_queue_pop(q)); + +    pa_queue_push(q, (void*) "fuenf"); + +    pa_log("%s\n", (char*) pa_queue_pop(q)); +    pa_log("%s\n", (char*) pa_queue_pop(q)); + +    pa_assert(pa_queue_is_empty(q)); + +    pa_queue_push(q, (void*) "sechs"); +    pa_queue_push(q, (void*) "sieben"); + +    pa_queue_free(q, NULL, NULL); + +    return 0; +} diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c new file mode 100644 index 00000000..3b4a7386 --- /dev/null +++ b/src/tests/resampler-test.c @@ -0,0 +1,227 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#include <pulse/sample.h> +#include <pulse/volume.h> + +#include <pulsecore/resampler.h> +#include <pulsecore/macro.h> +#include <pulsecore/endianmacros.h> +#include <pulsecore/memblock.h> +#include <pulsecore/sample-util.h> + +#include <liboil/liboil.h> + +static float swap_float(float a) { +    uint32_t *b = (uint32_t*) &a; +    *b = PA_UINT32_SWAP(*b); +    return a; +} + +static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) { +    void *d; +    unsigned i; + +    d = pa_memblock_acquire(chunk->memblock); + +    switch (ss->format) { + +        case PA_SAMPLE_U8: +        case PA_SAMPLE_ULAW: +        case PA_SAMPLE_ALAW: { +            uint8_t *u = d; + +            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) +                printf("0x%02x ", *(u++)); + +            break; +        } + +        case PA_SAMPLE_S16NE: +        case PA_SAMPLE_S16RE: { +            uint16_t *u = d; + +            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) +                printf("0x%04x ", *(u++)); + +            break; +        } + +        case PA_SAMPLE_FLOAT32NE: +        case PA_SAMPLE_FLOAT32RE: { +            float *u = d; + +            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { +                printf("%1.3g ",  ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u)); +                u++; +            } + +            break; +        } + +        default: +            pa_assert_not_reached(); +    } + +    printf("\n"); + +    pa_memblock_release(chunk->memblock); +} + +static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) { +    pa_memblock *r; +    void *d; +    unsigned i; + +    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10)); +    d = pa_memblock_acquire(r); + +    switch (ss->format) { + +        case PA_SAMPLE_U8: +        case PA_SAMPLE_ULAW: +        case PA_SAMPLE_ALAW: { +            uint8_t *u = d; + +            u[0] = 0x00; +            u[1] = 0xFF; +            u[2] = 0x7F; +            u[3] = 0x80; +            u[4] = 0x9f; +            u[5] = 0x3f; +            u[6] = 0x1; +            u[7] = 0xF0; +            u[8] = 0x20; +            u[9] = 0x21; +            break; +        } + +        case PA_SAMPLE_S16NE: +        case PA_SAMPLE_S16RE: { +            uint16_t *u = d; + +            u[0] = 0x0000; +            u[1] = 0xFFFF; +            u[2] = 0x7FFF; +            u[3] = 0x8000; +            u[4] = 0x9fff; +            u[5] = 0x3fff; +            u[6] = 0x1; +            u[7] = 0xF000; +            u[8] = 0x20; +            u[9] = 0x21; +            break; +        } + +        case PA_SAMPLE_FLOAT32NE: +        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; + +            if (ss->format == PA_SAMPLE_FLOAT32RE) +                for (i = 0; i < 10; i++) +                    u[i] = swap_float(u[i]); + +            break; +        } + +        default: +            pa_assert_not_reached(); +    } + +    pa_memblock_release(r); + +    return r; +} + +int main(int argc, char *argv[]) { +    pa_mempool *pool; +    pa_sample_spec a, b; +    pa_cvolume v; + +    oil_init(); +    pa_log_set_maximal_level(PA_LOG_DEBUG); + +    pa_assert_se(pool = pa_mempool_new(FALSE)); + +    a.channels = b.channels = 1; +    a.rate = b.rate = 44100; + +    v.channels = a.channels; +    v.values[0] = pa_sw_volume_from_linear(0.5); + +    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) { +        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) { + +            pa_resampler *forth, *back; +            pa_memchunk i, j, k; + +            printf("=== %s -> %s -> %s -> /2\n", +                   pa_sample_format_to_string(a.format), +                   pa_sample_format_to_string(b.format), +                   pa_sample_format_to_string(a.format)); + +            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, PA_RESAMPLER_AUTO, FALSE)); +            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, PA_RESAMPLER_AUTO, FALSE)); + +            i.memblock = generate_block(pool, &a); +            i.length = pa_memblock_get_length(i.memblock); +            i.index = 0; +            pa_resampler_run(forth, &i, &j); +            pa_resampler_run(back, &j, &k); + +            dump_block(&a, &i); +            dump_block(&b, &j); +            dump_block(&a, &k); + +            pa_memblock_unref(j.memblock); +            pa_memblock_unref(k.memblock); + +            pa_volume_memchunk(&i, &a, &v); +            dump_block(&a, &i); + +            pa_memblock_unref(i.memblock); + +            pa_resampler_free(forth); +            pa_resampler_free(back); +        } +    } + +    pa_mempool_free(pool); + +    return 0; +} diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c new file mode 100644 index 00000000..3ab992a1 --- /dev/null +++ b/src/tests/rtpoll-test.c @@ -0,0 +1,91 @@ +/* $Id: thread-test.c 1621 2007-08-10 22:00:22Z lennart $ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <signal.h> +#include <poll.h> + +#include <pulsecore/log.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/rtsig.h> + +static int before(pa_rtpoll_item *i) { +    pa_log("before"); +    return 0; +} + +static void after(pa_rtpoll_item *i) { +    pa_log("after"); +} + +static int worker(pa_rtpoll_item *w) { +    pa_log("worker"); +    return 0; +} + +int main(int argc, char *argv[]) { +    pa_rtpoll *p; +    pa_rtpoll_item *i, *w; +    struct pollfd *pollfd; + +    pa_rtsig_configure(SIGRTMIN+10, SIGRTMAX); + +    p = pa_rtpoll_new(); + +    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1); +    pa_rtpoll_item_set_before_callback(i, before); +    pa_rtpoll_item_set_after_callback(i, after); + +    pollfd = pa_rtpoll_item_get_pollfd(i, NULL); +    pollfd->fd = 0; +    pollfd->events = POLLIN; + +    w = pa_rtpoll_item_new(p, PA_RTPOLL_NORMAL, 0); +    pa_rtpoll_item_set_before_callback(w, worker); + +    pa_rtpoll_install(p); +    pa_rtpoll_set_timer_periodic(p, 10000000); /* 10 s */ + +    pa_rtpoll_run(p, 1); + +    pa_rtpoll_item_free(i); + +    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1); +    pa_rtpoll_item_set_before_callback(i, before); +    pa_rtpoll_item_set_after_callback(i, after); + +    pollfd = pa_rtpoll_item_get_pollfd(i, NULL); +    pollfd->fd = 0; +    pollfd->events = POLLIN; + +    pa_rtpoll_run(p, 1); + +    pa_rtpoll_item_free(i); + +    pa_rtpoll_item_free(w); + +    pa_rtpoll_free(p); + +    return 0; +} diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c new file mode 100644 index 00000000..52cb9db4 --- /dev/null +++ b/src/tests/sig2str-test.c @@ -0,0 +1,39 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <signal.h> +#include <stdio.h> + +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> + +int main(int argc, char *argv[]) { +    int sig; + +    for (sig = -1; sig <= NSIG; sig++) +        printf("%i = %s\n", sig, pa_sig2str(sig)); + +    return 0; +} diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c new file mode 100644 index 00000000..caa7df70 --- /dev/null +++ b/src/tests/smoother-test.c @@ -0,0 +1,80 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#include <pulsecore/time-smoother.h> +#include <pulse/timeval.h> + +int main(int argc, char*argv[]) { +    pa_usec_t x; +    unsigned u = 0; +    pa_smoother *s; +    int m; + +/*     unsigned msec[] = { */ +/*         200, 200, */ +/*         300, 320, */ +/*         400, 400, */ +/*         500, 480, */ +/*         0, 0 */ +/*     }; */ + +    int msec[200]; + +    srand(0); + +    for (m = 0, u = 0; u < PA_ELEMENTSOF(msec)-2; u+= 2) { + +        msec[u] = m+1; +        msec[u+1] = m + rand() % 2000 - 1000; + +        m += rand() % 100; + +        if (msec[u+1] < 0) +            msec[u+1] = 0; +    } + +    msec[PA_ELEMENTSOF(msec)-2] = 0; +    msec[PA_ELEMENTSOF(msec)-1] = 0; + +    s = pa_smoother_new(1000*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE); + +    for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) { + +        while (msec[u] > 0 && (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); +            printf("%i\t\t%i\n", msec[u],  msec[u+1]); +            u += 2; +        } + +        printf("%llu\t%llu\n", x/PA_USEC_PER_MSEC, pa_smoother_get(s, x)/PA_USEC_PER_MSEC); +    } + +    pa_smoother_free(s); + +    return 0; +} diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index 9d0e5de1..558e53a5 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -23,18 +23,19 @@  #include <config.h>  #endif -#include <assert.h>  #include <stdlib.h>  #include <unistd.h>  #include <stdio.h>  #include <pulse/timeval.h>  #include <pulse/util.h> +#include <pulse/thread-mainloop.h>  #include <pulsecore/gccmacro.h> -#include <pulse/thread-mainloop.h> +#include <pulsecore/macro.h>  static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { +    pa_assert_se(pa_threaded_mainloop_in_thread(userdata));      fprintf(stderr, "TIME EVENT START\n");      pa_threaded_mainloop_signal(userdata, 1);      fprintf(stderr, "TIME EVENT END\n"); @@ -45,15 +46,15 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {      pa_threaded_mainloop *m;      struct timeval tv; -    m = pa_threaded_mainloop_new(); -    assert(m); -    a = pa_threaded_mainloop_get_api(m); -    assert(a); +    pa_assert_se(m = pa_threaded_mainloop_new()); +    pa_assert_se(a = pa_threaded_mainloop_get_api(m));      pa_threaded_mainloop_start(m);      pa_threaded_mainloop_lock(m); +    pa_assert_se(!pa_threaded_mainloop_in_thread(m)); +      pa_gettimeofday(&tv);      tv.tv_sec += 5;      a->time_new(a, &tv, tcb, m); diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c index 2153c985..72dde6cb 100644 --- a/src/tests/thread-test.c +++ b/src/tests/thread-test.c @@ -42,7 +42,7 @@ static void once_func(void) {      pa_log("once!");  } -static pa_once_t once = PA_ONCE_INIT; +static pa_once once = PA_ONCE_INIT;  static void thread_func(void *data) {      pa_tls_set(tls, data); @@ -72,7 +72,7 @@ static void thread_func(void *data) {          pa_mutex_unlock(mutex); -        pa_once(&once, once_func); +        pa_run_once(&once, once_func);          pa_cond_signal(cond2, 0); @@ -98,7 +98,7 @@ int main(int argc, char *argv[]) {      assert(pa_thread_is_running(pa_thread_self())); -    mutex = pa_mutex_new(0); +    mutex = pa_mutex_new(FALSE, FALSE);      cond1 = pa_cond_new();      cond2 = pa_cond_new();      tls = pa_tls_new(pa_xfree); diff --git a/src/utils/pactl.c b/src/utils/pactl.c index b95cbfee..c963987f 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -48,8 +48,10 @@  static pa_context *context = NULL;  static pa_mainloop_api *mainloop_api = NULL; -static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL; +static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL;  static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX; +static uint32_t module_index; +static int suspend;  static SNDFILE *sndfile = NULL;  static pa_stream *sample_stream = NULL; @@ -69,7 +71,11 @@ static enum {      REMOVE_SAMPLE,      LIST,      MOVE_SINK_INPUT, -    MOVE_SOURCE_OUTPUT +    MOVE_SOURCE_OUTPUT, +    LOAD_MODULE, +    UNLOAD_MODULE, +    SUSPEND_SINK, +    SUSPEND_SOURCE,  } action = NONE;  static void quit(int ret) { @@ -354,7 +360,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info             i->sink,             pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),             pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), -           pa_cvolume_snprint(cv, sizeof(cv), &i->volume), +           i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),             (double) i->buffer_usec,             (double) i->sink_usec,             i->resample_method ? i->resample_method : "n/a"); @@ -492,6 +498,18 @@ static void simple_callback(pa_context *c, int success, void *userdata) {      complete_action();  } +static void index_callback(pa_context *c, uint32_t idx, void *userdata) { +    if (idx == PA_INVALID_INDEX) { +        fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c))); +        quit(1); +        return; +    } + +    printf("%u\n", idx); + +    complete_action(); +} +  static void stream_state_callback(pa_stream *s, void *userdata) {      assert(s); @@ -594,6 +612,28 @@ static void context_state_callback(pa_context *c, void *userdata) {                      pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));                      break; +                case LOAD_MODULE: +                    pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL)); +                    break; + +                case UNLOAD_MODULE: +                    pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL)); +                    break; + +                case SUSPEND_SINK: +                    if (sink_name) +                        pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL)); +                    else +                        pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL)); +                    break; + +                case SUSPEND_SOURCE: +                    if (source_name) +                        pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL)); +                    else +                        pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL)); +                    break; +                  default:                      assert(0);              } @@ -624,12 +664,16 @@ static void help(const char *argv0) {             "%s [options] play-sample NAME [SINK]\n"             "%s [options] move-sink-input ID SINK\n"             "%s [options] move-source-output ID SOURCE\n" -           "%s [options] remove-sample NAME\n\n" +           "%s [options] remove-sample NAME\n" +           "%s [options] load-module NAME [ARGS ...]\n" +           "%s [options] unload-module ID\n" +           "%s [options] suspend-sink [SINK] 1|0\n" +           "%s [options] suspend-source [SOURCE] 1|0\n\n"             "  -h, --help                            Show this help\n"             "      --version                         Show version\n\n"             "  -s, --server=SERVER                   The name of the server to connect to\n"             "  -n, --client-name=NAME                How to call this client on the server\n", -           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0); +           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);  }  enum { ARG_VERSION = 256 }; @@ -728,7 +772,7 @@ int main(int argc, char *argv[]) {              sample_length = sfinfo.frames*pa_frame_size(&sample_spec);          } else if (!strcmp(argv[optind], "play-sample")) {              action = PLAY_SAMPLE; -            if (optind+1 >= argc) { +            if (argc != optind+2 && argc != optind+3) {                  fprintf(stderr, "You have to specify a sample name to play\n");                  goto quit;              } @@ -740,7 +784,7 @@ int main(int argc, char *argv[]) {          } else if (!strcmp(argv[optind], "remove-sample")) {              action = REMOVE_SAMPLE; -            if (optind+1 >= argc) { +            if (argc != optind+2) {                  fprintf(stderr, "You have to specify a sample name to remove\n");                  goto quit;              } @@ -748,7 +792,7 @@ int main(int argc, char *argv[]) {              sample_name = pa_xstrdup(argv[optind+1]);          } else if (!strcmp(argv[optind], "move-sink-input")) {              action = MOVE_SINK_INPUT; -            if (optind+2 >= argc) { +            if (argc != optind+3) {                  fprintf(stderr, "You have to specify a sink input index and a sink\n");                  goto quit;              } @@ -757,13 +801,72 @@ int main(int argc, char *argv[]) {              sink_name = pa_xstrdup(argv[optind+2]);          } else if (!strcmp(argv[optind], "move-source-output")) {              action = MOVE_SOURCE_OUTPUT; -            if (optind+2 >= argc) { +            if (argc != optind+3) {                  fprintf(stderr, "You have to specify a source output index and a source\n");                  goto quit;              }              source_output_idx = atoi(argv[optind+1]);              source_name = pa_xstrdup(argv[optind+2]); +        } else if (!strcmp(argv[optind], "load-module")) { +            int i; +            size_t n = 0; +            char *p; + +            action = LOAD_MODULE; + +            if (argc <= optind+1) { +                fprintf(stderr, "You have to specify a module name and arguments.\n"); +                goto quit; +            } + +            module_name = argv[optind+1]; + +            for (i = optind+2; i < argc; i++) +                n += strlen(argv[i])+1; + +            if (n > 0) { +                p = module_args = pa_xnew0(char, n); + +                for (i = optind+2; i < argc; i++) +                    p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]); +            } + +        } else if (!strcmp(argv[optind], "unload-module")) { +            action = UNLOAD_MODULE; + +            if (argc != optind+2) { +                fprintf(stderr, "You have to specify a module index\n"); +                goto quit; +            } + +            module_index = atoi(argv[optind+1]); + +        } else if (!strcmp(argv[optind], "suspend-sink")) { +            action = SUSPEND_SINK; + +            if (argc > optind+3 || optind+1 >= argc) { +                fprintf(stderr, "You may not specify more than one sink. You have to specify at least one boolean value.\n"); +                goto quit; +            } + +            suspend = !!atoi(argv[argc-1]); + +            if (argc > optind+2) +                sink_name = pa_xstrdup(argv[optind+1]); + +        } else if (!strcmp(argv[optind], "suspend-source")) { +            action = SUSPEND_SOURCE; + +            if (argc > optind+3 || optind+1 >= argc) { +                fprintf(stderr, "You may not specify more than one source. You have to specify at least one boolean value.\n"); +                goto quit; +            } + +            suspend = !!atoi(argv[argc-1]); + +            if (argc > optind+2) +                source_name = pa_xstrdup(argv[optind+1]);          }      } @@ -819,6 +922,8 @@ quit:      pa_xfree(sample_name);      pa_xfree(sink_name);      pa_xfree(source_name); +    pa_xfree(module_args); +    pa_xfree(client_name);      return ret;  } diff --git a/src/utils/padsp.c b/src/utils/padsp.c index 95fc9ed3..b48af93c 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -61,6 +61,10 @@  # define SIOCINQ FIONREAD  #endif +/* make sure gcc doesn't redefine open and friends as macros */ +#undef open +#undef open64 +  typedef enum {      FD_INFO_MIXER,      FD_INFO_STREAM, @@ -259,9 +263,9 @@ if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \  static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); -#define DEBUG_LEVEL_ALWAYS		0 -#define DEBUG_LEVEL_NORMAL		1 -#define DEBUG_LEVEL_VERBOSE		2 +#define DEBUG_LEVEL_ALWAYS                0 +#define DEBUG_LEVEL_NORMAL                1 +#define DEBUG_LEVEL_VERBOSE                2  static void debug(int level, const char *format, ...) {      va_list ap; @@ -421,7 +425,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) @@ -1395,7 +1399,7 @@ static int sndstat_open(int flags, int *_errno) {      if (flags != O_RDONLY  #ifdef O_LARGEFILE -	&& flags != (O_RDONLY|O_LARGEFILE) +        && flags != (O_RDONLY|O_LARGEFILE)  #endif         ) {          *_errno = EACCES; @@ -1436,34 +1440,23 @@ fail:      return -1;  } -int open(const char *filename, int flags, ...) { -    va_list args; -    mode_t mode = 0; +static int real_open(const char *filename, int flags, mode_t mode) {      int r, _errno = 0;      debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename); -    va_start(args, flags); -    if (flags & O_CREAT) { -      if (sizeof(mode_t) < sizeof(int)) -	mode = va_arg(args, int); -      else -        mode = va_arg(args, mode_t); -    } -    va_end(args); -      if (!function_enter()) {          LOAD_OPEN_FUNC();          return _open(filename, flags, mode);      } -    if (dsp_cloak_enable() && (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0)) { +    if (dsp_cloak_enable() && (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0))          r = dsp_open(flags, &_errno); -    } else if (mixer_cloak_enable() && strcmp(filename, "/dev/mixer") == 0) { +    else if (mixer_cloak_enable() && strcmp(filename, "/dev/mixer") == 0)          r = mixer_open(flags, &_errno); -    } else if (sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0) { +    else if (sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0)          r = sndstat_open(flags, &_errno); -    } else { +    else {          function_exit();          LOAD_OPEN_FUNC();          return _open(filename, flags, mode); @@ -1477,6 +1470,22 @@ int open(const char *filename, int flags, ...) {      return r;  } +int open(const char *filename, int flags, ...) { +    va_list args; +    mode_t mode = 0; + +    if (flags & O_CREAT) { +        va_start(args, flags); +        if (sizeof(mode_t) < sizeof(int)) +            mode = va_arg(args, int); +        else +            mode = va_arg(args, mode_t); +        va_end(args); +    } + +    return real_open(filename, flags, mode); +} +  static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {      int ret = -1; @@ -2023,9 +2032,9 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)              *(int*)  argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER  #ifdef DSP_CAP_MULTI -	      | DSP_CAP_MULTI +              | DSP_CAP_MULTI  #endif -	      ; +              ;              break;          case SNDCTL_DSP_GETODELAY: { @@ -2497,10 +2506,14 @@ int open64(const char *filename, int flags, ...) {      debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename); -    va_start(args, flags); -    if (flags & O_CREAT) -        mode = va_arg(args, mode_t); -    va_end(args); +    if (flags & O_CREAT) { +        va_start(args, flags); +        if (sizeof(mode_t) < sizeof(int)) +            mode = va_arg(args, int); +        else +            mode = va_arg(args, mode_t); +        va_end(args); +    }      if (strcmp(filename, "/dev/dsp") != 0 &&          strcmp(filename, "/dev/adsp") != 0 && @@ -2510,7 +2523,7 @@ int open64(const char *filename, int flags, ...) {          return _open64(filename, flags, mode);      } -    return open(filename, flags, mode); +    return real_open(filename, flags, mode);  }  #endif @@ -2602,7 +2615,7 @@ FILE* fopen(const char *filename, const char *mode) {      if ((((mode[1] == 'b') || (mode[1] == 't')) && (mode[2] == '+')) || (mode[1] == '+'))          m = O_RDWR; -    if ((fd = open(filename, m)) < 0) +    if ((fd = real_open(filename, m, 0)) < 0)          return NULL;      if (!(f = fdopen(fd, mode))) { diff --git a/src/utils/paplay.c b/src/utils/paplay.c index 2c779a7a..e7076d2d 100644 --- a/src/utils/paplay.c +++ b/src/utils/paplay.c @@ -123,7 +123,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {      else          pa_xfree(data); -    if (bytes < length) { +    if (bytes < (sf_count_t) length) {          sf_close(sndfile);          sndfile = NULL;          pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL)); diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c new file mode 100644 index 00000000..ae59086b --- /dev/null +++ b/src/utils/pasuspender.c @@ -0,0 +1,316 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 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/types.h> +#include <sys/wait.h> + +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <getopt.h> + +#include <sndfile.h> + +#ifdef __linux__ +#include <sys/prctl.h> +#endif + +#include <pulse/pulseaudio.h> +#include <pulsecore/macro.h> + +#if PA_API_VERSION < 10 +#error Invalid PulseAudio API version +#endif + +#define BUFSIZE 1024 + +static pa_context *context = NULL; +static pa_mainloop_api *mainloop_api = NULL; +static char **child_argv = NULL; +static int child_argc = 0; +static pid_t child_pid = (pid_t) -1; +static int child_ret = 0; +static int dead = 1; + +static void quit(int ret) { +    pa_assert(mainloop_api); +    mainloop_api->quit(mainloop_api, ret); +} + + +static void context_drain_complete(pa_context *c, void *userdata) { +    pa_context_disconnect(c); +} + +static void drain(void) { +    pa_operation *o; + +    if (!(o = pa_context_drain(context, context_drain_complete, NULL))) +        pa_context_disconnect(context); +    else +        pa_operation_unref(o); +} + +static void start_child(void) { + +    if ((child_pid = fork()) < 0) { + +        fprintf(stderr, "fork(): %s\n", strerror(errno)); +        quit(1); + +    } else if (child_pid == 0) { +        /* Child */ + +#ifdef __linux__ +        prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0); +#endif + +        if (execvp(child_argv[0], child_argv) < 0) +            fprintf(stderr, "execvp(): %s\n", strerror(errno)); + +        _exit(1); + +    } else { + +        /* parent */ +        dead = 0; +    } +} + +static void suspend_complete(pa_context *c, int success, void *userdata) { +    static int n = 0; + +    n++; + +    if (!success) { +        fprintf(stderr, "Failure to suspend: %s\n", pa_strerror(pa_context_errno(c))); +        quit(1); +        return; +    } + +    if (n >= 2) +        start_child(); +} + +static void resume_complete(pa_context *c, int success, void *userdata) { +    static int n = 0; + +    n++; + +    if (!success) { +        fprintf(stderr, "Failure to resume: %s\n", pa_strerror(pa_context_errno(c))); +        quit(1); +        return; +    } + +    if (n >= 2) +        drain(); /* drain and quit */ +} + +static void context_state_callback(pa_context *c, void *userdata) { +    pa_assert(c); + +    switch (pa_context_get_state(c)) { +        case PA_CONTEXT_CONNECTING: +        case PA_CONTEXT_AUTHORIZING: +        case PA_CONTEXT_SETTING_NAME: +            break; + +        case PA_CONTEXT_READY: +            if (pa_context_is_local(c)) { +                pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL)); +                pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL)); +            } else +                start_child(); + +            break; + +        case PA_CONTEXT_TERMINATED: +            quit(0); +            break; + +        case PA_CONTEXT_FAILED: +        default: +            fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); + +            pa_context_unref(context); +            context = NULL; + +            if (child_pid == (pid_t) -1) +                /* not started yet, then we do it now */ +                start_child(); +            else if (dead) +                /* already started, and dead, so let's quit */ +                quit(1); + +            break; +    } +} + +static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) { +    fprintf(stderr, "Got SIGINT, exiting.\n"); +    quit(0); +} + +static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) { +    int status = 0; +    pid_t p; + +    p = waitpid(-1, &status, WNOHANG); + +    if (p != child_pid) +        return; + +    dead = 1; + +    if (WIFEXITED(status)) +        child_ret = WEXITSTATUS(status); +    else if (WIFSIGNALED(status)) { +        fprintf(stderr, "WARNING: Child process terminated by signal %u\n", WTERMSIG(status)); +        child_ret = 1; +    } + +    if (context) { +        if (pa_context_is_local(context)) { +            /* A context is around, so let's resume */ +            pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL)); +            pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL)); +        } else +            drain(); +    } else +        /* Hmm, no context here, so let's terminate right away */ +        quit(0); +} + +static void help(const char *argv0) { + +    printf("%s [options] ... \n\n" +           "  -h, --help                            Show this help\n" +           "      --version                         Show version\n" +           "  -s, --server=SERVER                   The name of the server to connect to\n\n", +           argv0); +} + +enum { +    ARG_VERSION = 256 +}; + +int main(int argc, char *argv[]) { +    pa_mainloop* m = NULL; +    int c, ret = 1; +    char *server = NULL, *bn; + +    static const struct option long_options[] = { +        {"server",      1, NULL, 's'}, +        {"version",     0, NULL, ARG_VERSION}, +        {"help",        0, NULL, 'h'}, +        {NULL,          0, NULL, 0} +    }; + +    if (!(bn = strrchr(argv[0], '/'))) +        bn = argv[0]; +    else +        bn++; + +    while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) { +        switch (c) { +            case 'h' : +                help(bn); +                ret = 0; +                goto quit; + +            case ARG_VERSION: +                printf("pasuspender "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version()); +                ret = 0; +                goto quit; + +            case 's': +                pa_xfree(server); +                server = pa_xstrdup(optarg); +                break; + +            default: +                goto quit; +        } +    } + +    child_argv = argv + optind; +    child_argc = argc - optind; + +    if (child_argc <= 0) { +        help(bn); +        ret = 0; +        goto quit; +    } + +    if (!(m = pa_mainloop_new())) { +        fprintf(stderr, "pa_mainloop_new() failed.\n"); +        goto quit; +    } + +    pa_assert_se(mainloop_api = pa_mainloop_get_api(m)); +    pa_assert_se(pa_signal_init(mainloop_api) == 0); +    pa_signal_new(SIGINT, sigint_callback, NULL); +    pa_signal_new(SIGCHLD, sigchld_callback, NULL); +#ifdef SIGPIPE +    signal(SIGPIPE, SIG_IGN); +#endif + +    if (!(context = pa_context_new(mainloop_api, bn))) { +        fprintf(stderr, "pa_context_new() failed.\n"); +        goto quit; +    } + +    pa_context_set_state_callback(context, context_state_callback, NULL); +    pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL); + +    if (pa_mainloop_run(m, &ret) < 0) { +        fprintf(stderr, "pa_mainloop_run() failed.\n"); +        goto quit; +    } + +quit: +    if (context) +        pa_context_unref(context); + +    if (m) { +        pa_signal_done(); +        pa_mainloop_free(m); +    } + +    pa_xfree(server); + +    if (!dead) +        kill(child_pid, SIGTERM); + +    return ret == 0 ? child_ret : ret; +}  | 
