summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore56
-rw-r--r--src/Makefile.am252
-rw-r--r--src/daemon/caps.c85
-rw-r--r--src/daemon/caps.h9
-rw-r--r--src/daemon/cmdline.c58
-rw-r--r--src/daemon/cmdline.h2
-rw-r--r--src/daemon/cpulimit.c19
-rw-r--r--src/daemon/cpulimit.h2
-rw-r--r--src/daemon/daemon-conf.c329
-rw-r--r--src/daemon/daemon-conf.h49
-rw-r--r--src/daemon/daemon.conf.in99
-rwxr-xr-xsrc/daemon/default.pa.in34
-rw-r--r--src/daemon/dumpmodules.c9
-rw-r--r--src/daemon/dumpmodules.h2
-rwxr-xr-xsrc/daemon/esdcompat.in2
-rw-r--r--src/daemon/ltdl-bind-now.c47
-rw-r--r--src/daemon/ltdl-bind-now.h2
-rw-r--r--src/daemon/main.c482
-rw-r--r--src/daemon/org.pulseaudio.policy50
-rw-r--r--src/daemon/polkit.c165
-rw-r--r--src/daemon/polkit.h (renamed from src/pulsecore/core-def.h)10
-rwxr-xr-xsrc/depmod.py3
-rw-r--r--src/map-file256
-rw-r--r--src/modules/.gitignore1
-rw-r--r--src/modules/alsa-util.c743
-rw-r--r--src/modules/alsa-util.h60
-rw-r--r--src/modules/bt-proximity-helper.c202
-rw-r--r--src/modules/dbus-util.c44
-rw-r--r--src/modules/dbus-util.h2
-rw-r--r--src/modules/gconf/gconf-helper.c2
-rw-r--r--src/modules/gconf/module-gconf.c151
-rw-r--r--src/modules/ladspa.h38
-rw-r--r--src/modules/module-alsa-sink.c1173
-rw-r--r--src/modules/module-alsa-source.c988
-rw-r--r--src/modules/module-always-sink.c178
-rw-r--r--src/modules/module-bt-proximity.c490
-rw-r--r--src/modules/module-cli.c13
-rw-r--r--src/modules/module-combine.c612
-rw-r--r--src/modules/module-console-kit.c334
-rw-r--r--src/modules/module-default-device-restore.c172
-rw-r--r--src/modules/module-defs.h.m44
-rw-r--r--src/modules/module-detect.c14
-rw-r--r--src/modules/module-device-restore.c348
-rw-r--r--src/modules/module-esound-compat-spawnfd.c15
-rw-r--r--src/modules/module-esound-compat-spawnpid.c11
-rw-r--r--src/modules/module-esound-sink.c64
-rw-r--r--src/modules/module-hal-detect.c143
-rw-r--r--src/modules/module-jack-sink.c56
-rw-r--r--src/modules/module-jack-source.c53
-rw-r--r--src/modules/module-ladspa-sink.c358
-rw-r--r--src/modules/module-lirc.c13
-rw-r--r--src/modules/module-match.c36
-rw-r--r--src/modules/module-mmkbd-evdev.c15
-rw-r--r--src/modules/module-native-protocol-fd.c11
-rw-r--r--src/modules/module-null-sink.c185
-rw-r--r--src/modules/module-oss.c164
-rw-r--r--src/modules/module-pipe-sink.c128
-rw-r--r--src/modules/module-pipe-source.c59
-rw-r--r--src/modules/module-position-event-sounds.c165
-rw-r--r--src/modules/module-protocol-stub.c98
-rw-r--r--src/modules/module-remap-sink.c253
-rw-r--r--src/modules/module-rescue-streams.c25
-rw-r--r--src/modules/module-sine.c92
-rw-r--r--src/modules/module-solaris.c3
-rw-r--r--src/modules/module-suspend-on-idle.c111
-rw-r--r--src/modules/module-tunnel.c1740
-rw-r--r--src/modules/module-volume-restore.c195
-rw-r--r--src/modules/module-waveout.c3
-rw-r--r--src/modules/module-x11-bell.c68
-rw-r--r--src/modules/module-x11-publish.c67
-rw-r--r--src/modules/module-x11-xsmp.c122
-rw-r--r--src/modules/module-zeroconf-discover.c438
-rw-r--r--src/modules/module-zeroconf-publish.c124
-rw-r--r--src/modules/oss-util.c40
-rw-r--r--src/modules/oss-util.h6
-rw-r--r--src/modules/rtp/module-rtp-recv.c176
-rw-r--r--src/modules/rtp/module-rtp-send.c35
-rw-r--r--src/modules/rtp/rtp.c58
-rw-r--r--src/modules/rtp/rtp.h4
-rw-r--r--src/modules/rtp/sap.c6
-rw-r--r--src/modules/rtp/sap.h6
-rw-r--r--src/modules/rtp/sdp.c8
-rw-r--r--src/modules/rtp/sdp.h2
-rw-r--r--src/pulse/.gitignore1
-rw-r--r--src/pulse/browser.c19
-rw-r--r--src/pulse/browser.h2
-rw-r--r--src/pulse/cdecl.h20
-rw-r--r--src/pulse/channelmap.c35
-rw-r--r--src/pulse/channelmap.h17
-rw-r--r--src/pulse/client-conf-x11.c9
-rw-r--r--src/pulse/client-conf-x11.h2
-rw-r--r--src/pulse/client-conf.c35
-rw-r--r--src/pulse/client-conf.h6
-rw-r--r--src/pulse/client.conf.in31
-rw-r--r--src/pulse/context.c464
-rw-r--r--src/pulse/context.h40
-rw-r--r--src/pulse/def.h247
-rw-r--r--src/pulse/error.c2
-rw-r--r--src/pulse/error.h2
-rw-r--r--src/pulse/gccmacro.h (renamed from src/pulsecore/gccmacro.h)24
-rw-r--r--src/pulse/glib-mainloop.c2
-rw-r--r--src/pulse/glib-mainloop.h2
-rw-r--r--src/pulse/internal.h72
-rw-r--r--src/pulse/introspect.c129
-rw-r--r--src/pulse/introspect.h299
-rw-r--r--src/pulse/mainloop-api.c9
-rw-r--r--src/pulse/mainloop-api.h2
-rw-r--r--src/pulse/mainloop-signal.c37
-rw-r--r--src/pulse/mainloop-signal.h18
-rw-r--r--src/pulse/mainloop.c2
-rw-r--r--src/pulse/mainloop.h2
-rw-r--r--src/pulse/operation.c48
-rw-r--r--src/pulse/operation.h2
-rw-r--r--src/pulse/proplist.c321
-rw-r--r--src/pulse/proplist.h217
-rw-r--r--src/pulse/pulseaudio.h2
-rw-r--r--src/pulse/sample.c34
-rw-r--r--src/pulse/sample.h36
-rw-r--r--src/pulse/scache.c133
-rw-r--r--src/pulse/scache.h33
-rw-r--r--src/pulse/simple.c2
-rw-r--r--src/pulse/simple.h10
-rw-r--r--src/pulse/stream.c1407
-rw-r--r--src/pulse/stream.h161
-rw-r--r--src/pulse/subscribe.c8
-rw-r--r--src/pulse/subscribe.h2
-rw-r--r--src/pulse/thread-mainloop.c4
-rw-r--r--src/pulse/thread-mainloop.h2
-rw-r--r--src/pulse/timeval.c22
-rw-r--r--src/pulse/timeval.h18
-rw-r--r--src/pulse/utf8.c10
-rw-r--r--src/pulse/utf8.h3
-rw-r--r--src/pulse/util.c26
-rw-r--r--src/pulse/util.h3
-rw-r--r--src/pulse/version.h.in15
-rw-r--r--src/pulse/volume.c6
-rw-r--r--src/pulse/volume.h24
-rw-r--r--src/pulse/xmalloc.c9
-rw-r--r--src/pulse/xmalloc.h2
-rw-r--r--src/pulsecore/asyncmsgq.c38
-rw-r--r--src/pulsecore/asyncmsgq.h18
-rw-r--r--src/pulsecore/asyncq.c146
-rw-r--r--src/pulsecore/asyncq.h23
-rw-r--r--src/pulsecore/atomic.h250
-rw-r--r--src/pulsecore/authkey-prop.c10
-rw-r--r--src/pulsecore/authkey-prop.h2
-rw-r--r--src/pulsecore/authkey.c14
-rw-r--r--src/pulsecore/authkey.h2
-rw-r--r--src/pulsecore/autoload.c14
-rw-r--r--src/pulsecore/autoload.h4
-rw-r--r--src/pulsecore/avahi-wrap.c2
-rw-r--r--src/pulsecore/avahi-wrap.h2
-rw-r--r--src/pulsecore/cli-command.c356
-rw-r--r--src/pulsecore/cli-command.h13
-rw-r--r--src/pulsecore/cli-text.c238
-rw-r--r--src/pulsecore/cli-text.h2
-rw-r--r--src/pulsecore/cli.c20
-rw-r--r--src/pulsecore/cli.h2
-rw-r--r--src/pulsecore/client.c35
-rw-r--r--src/pulsecore/client.h10
-rw-r--r--src/pulsecore/conf-parser.c11
-rw-r--r--src/pulsecore/conf-parser.h2
-rw-r--r--src/pulsecore/core-error.c4
-rw-r--r--src/pulsecore/core-error.h2
-rw-r--r--src/pulsecore/core-scache.c121
-rw-r--r--src/pulsecore/core-scache.h19
-rw-r--r--src/pulsecore/core-subscribe.c2
-rw-r--r--src/pulsecore/core-subscribe.h2
-rw-r--r--src/pulsecore/core-util.c788
-rw-r--r--src/pulsecore/core-util.h86
-rw-r--r--src/pulsecore/core.c26
-rw-r--r--src/pulsecore/core.h29
-rw-r--r--src/pulsecore/creds.h2
-rw-r--r--src/pulsecore/dllmain.c2
-rw-r--r--src/pulsecore/dynarray.c8
-rw-r--r--src/pulsecore/dynarray.h2
-rw-r--r--src/pulsecore/endianmacros.h28
-rw-r--r--src/pulsecore/envelope.c781
-rw-r--r--src/pulsecore/envelope.h53
-rw-r--r--src/pulsecore/esound.h2
-rw-r--r--src/pulsecore/fdsem.c102
-rw-r--r--src/pulsecore/fdsem.h10
-rw-r--r--src/pulsecore/ffmpeg/avcodec.h2
-rw-r--r--src/pulsecore/flist.c2
-rw-r--r--src/pulsecore/flist.h4
-rw-r--r--src/pulsecore/hashmap.c46
-rw-r--r--src/pulsecore/hashmap.h9
-rw-r--r--src/pulsecore/hook-list.c39
-rw-r--r--src/pulsecore/hook-list.h21
-rw-r--r--src/pulsecore/idxset.c18
-rw-r--r--src/pulsecore/idxset.h2
-rw-r--r--src/pulsecore/inet_ntop.c3
-rw-r--r--src/pulsecore/inet_pton.c3
-rw-r--r--src/pulsecore/iochannel.c57
-rw-r--r--src/pulsecore/iochannel.h4
-rw-r--r--src/pulsecore/ioline.c52
-rw-r--r--src/pulsecore/ioline.h6
-rw-r--r--src/pulsecore/ipacl.c2
-rw-r--r--src/pulsecore/ipacl.h2
-rw-r--r--src/pulsecore/llist.h2
-rw-r--r--src/pulsecore/log.c40
-rw-r--r--src/pulsecore/log.h4
-rw-r--r--src/pulsecore/ltdl-helper.c12
-rw-r--r--src/pulsecore/ltdl-helper.h2
-rw-r--r--src/pulsecore/macro.h135
-rw-r--r--src/pulsecore/mcalign.c11
-rw-r--r--src/pulsecore/mcalign.h5
-rw-r--r--src/pulsecore/memblock.c120
-rw-r--r--src/pulsecore/memblock.h17
-rw-r--r--src/pulsecore/memblockq.c491
-rw-r--r--src/pulsecore/memblockq.h45
-rw-r--r--src/pulsecore/memchunk.c38
-rw-r--r--src/pulsecore/memchunk.h5
-rw-r--r--src/pulsecore/modargs.c30
-rw-r--r--src/pulsecore/modargs.h5
-rw-r--r--src/pulsecore/modinfo.c11
-rw-r--r--src/pulsecore/modinfo.h4
-rw-r--r--src/pulsecore/module.c32
-rw-r--r--src/pulsecore/module.h29
-rw-r--r--src/pulsecore/msgobject.c2
-rw-r--r--src/pulsecore/msgobject.h2
-rw-r--r--src/pulsecore/mutex-posix.c24
-rw-r--r--src/pulsecore/mutex-win32.c2
-rw-r--r--src/pulsecore/mutex.h3
-rw-r--r--src/pulsecore/namereg.c18
-rw-r--r--src/pulsecore/namereg.h7
-rw-r--r--src/pulsecore/native-common.h44
-rw-r--r--src/pulsecore/object.c6
-rw-r--r--src/pulsecore/object.h2
-rw-r--r--src/pulsecore/once.c6
-rw-r--r--src/pulsecore/once.h14
-rw-r--r--src/pulsecore/packet.c2
-rw-r--r--src/pulsecore/packet.h2
-rw-r--r--src/pulsecore/parseaddr.c13
-rw-r--r--src/pulsecore/parseaddr.h2
-rw-r--r--src/pulsecore/pdispatch.c18
-rw-r--r--src/pulsecore/pdispatch.h2
-rw-r--r--src/pulsecore/pid.c110
-rw-r--r--src/pulsecore/pid.h8
-rw-r--r--src/pulsecore/pipe.c2
-rw-r--r--src/pulsecore/pipe.h2
-rw-r--r--src/pulsecore/play-memblockq.c134
-rw-r--r--src/pulsecore/play-memblockq.h11
-rw-r--r--src/pulsecore/play-memchunk.c160
-rw-r--r--src/pulsecore/play-memchunk.h7
-rw-r--r--src/pulsecore/poll.c2
-rw-r--r--src/pulsecore/poll.h2
-rw-r--r--src/pulsecore/proplist-util.c118
-rw-r--r--src/pulsecore/proplist-util.h29
-rw-r--r--src/pulsecore/props.c12
-rw-r--r--src/pulsecore/props.h2
-rw-r--r--src/pulsecore/protocol-cli.c10
-rw-r--r--src/pulsecore/protocol-cli.h2
-rw-r--r--src/pulsecore/protocol-esound.c191
-rw-r--r--src/pulsecore/protocol-esound.h2
-rw-r--r--src/pulsecore/protocol-http.c14
-rw-r--r--src/pulsecore/protocol-http.h2
-rw-r--r--src/pulsecore/protocol-native.c1567
-rw-r--r--src/pulsecore/protocol-native.h2
-rw-r--r--src/pulsecore/protocol-simple.c184
-rw-r--r--src/pulsecore/protocol-simple.h2
-rw-r--r--src/pulsecore/pstream-util.c4
-rw-r--r--src/pulsecore/pstream-util.h2
-rw-r--r--src/pulsecore/pstream.c55
-rw-r--r--src/pulsecore/pstream.h15
-rw-r--r--src/pulsecore/queue.c16
-rw-r--r--src/pulsecore/queue.h2
-rw-r--r--src/pulsecore/random.c8
-rw-r--r--src/pulsecore/random.h2
-rw-r--r--src/pulsecore/refcnt.h5
-rw-r--r--src/pulsecore/resampler.c776
-rw-r--r--src/pulsecore/resampler.h18
-rw-r--r--src/pulsecore/rtclock.c23
-rw-r--r--src/pulsecore/rtclock.h4
-rw-r--r--src/pulsecore/rtpoll.c143
-rw-r--r--src/pulsecore/rtpoll.h10
-rw-r--r--src/pulsecore/rtsig.c2
-rw-r--r--src/pulsecore/rtsig.h2
-rw-r--r--src/pulsecore/sample-util.c791
-rw-r--r--src/pulsecore/sample-util.h31
-rw-r--r--src/pulsecore/sconv-s16be.c17
-rw-r--r--src/pulsecore/sconv-s16be.h22
-rw-r--r--src/pulsecore/sconv-s16le.c133
-rw-r--r--src/pulsecore/sconv-s16le.h22
-rw-r--r--src/pulsecore/sconv.c14
-rw-r--r--src/pulsecore/sconv.h2
-rw-r--r--src/pulsecore/semaphore-posix.c2
-rw-r--r--src/pulsecore/semaphore-win32.c2
-rw-r--r--src/pulsecore/semaphore.h2
-rw-r--r--src/pulsecore/shm.c68
-rw-r--r--src/pulsecore/shm.h10
-rw-r--r--src/pulsecore/shmasyncq.c220
-rw-r--r--src/pulsecore/shmasyncq.h60
-rw-r--r--src/pulsecore/sink-input.c977
-rw-r--r--src/pulsecore/sink-input.h170
-rw-r--r--src/pulsecore/sink.c1072
-rw-r--r--src/pulsecore/sink.h150
-rw-r--r--src/pulsecore/sioman.c2
-rw-r--r--src/pulsecore/sioman.h2
-rw-r--r--src/pulsecore/socket-client.c66
-rw-r--r--src/pulsecore/socket-client.h10
-rw-r--r--src/pulsecore/socket-server.c23
-rw-r--r--src/pulsecore/socket-server.h6
-rw-r--r--src/pulsecore/socket-util.c51
-rw-r--r--src/pulsecore/socket-util.h8
-rw-r--r--src/pulsecore/sound-file-stream.c238
-rw-r--r--src/pulsecore/sound-file-stream.h2
-rw-r--r--src/pulsecore/sound-file.c14
-rw-r--r--src/pulsecore/sound-file.h2
-rw-r--r--src/pulsecore/source-output.c467
-rw-r--r--src/pulsecore/source-output.h109
-rw-r--r--src/pulsecore/source.c698
-rw-r--r--src/pulsecore/source.h126
-rw-r--r--src/pulsecore/speex/arch.h62
-rw-r--r--src/pulsecore/speex/fixed_generic.h8
-rw-r--r--src/pulsecore/speex/resample.c123
-rw-r--r--src/pulsecore/speex/speex_resampler.h116
-rw-r--r--src/pulsecore/speexwrap.h4
-rw-r--r--src/pulsecore/start-child.c160
-rw-r--r--src/pulsecore/start-child.h30
-rw-r--r--src/pulsecore/strbuf.c18
-rw-r--r--src/pulsecore/strbuf.h4
-rw-r--r--src/pulsecore/strlist.c21
-rw-r--r--src/pulsecore/strlist.h5
-rw-r--r--src/pulsecore/tagstruct.c134
-rw-r--r--src/pulsecore/tagstruct.h15
-rw-r--r--src/pulsecore/thread-mq.c41
-rw-r--r--src/pulsecore/thread-mq.h7
-rw-r--r--src/pulsecore/thread-posix.c4
-rw-r--r--src/pulsecore/thread-win32.c2
-rw-r--r--src/pulsecore/thread.h2
-rw-r--r--src/pulsecore/time-smoother.c245
-rw-r--r--src/pulsecore/time-smoother.h14
-rw-r--r--src/pulsecore/tokenizer.c8
-rw-r--r--src/pulsecore/tokenizer.h2
-rw-r--r--src/pulsecore/x11prop.c2
-rw-r--r--src/pulsecore/x11prop.h2
-rw-r--r--src/pulsecore/x11wrap.c53
-rw-r--r--src/pulsecore/x11wrap.h12
-rw-r--r--src/tests/asyncmsgq-test.c4
-rw-r--r--src/tests/asyncq-test.c6
-rw-r--r--src/tests/channelmap-test.c9
-rw-r--r--src/tests/close-test.c20
-rw-r--r--src/tests/cpulimit-test.c4
-rw-r--r--src/tests/envelope-test.c246
-rw-r--r--src/tests/flist-test.c2
-rw-r--r--src/tests/get-binary-name-test.c2
-rw-r--r--src/tests/hook-list-test.c12
-rw-r--r--src/tests/interpol-test.c30
-rw-r--r--src/tests/ipacl-test.c2
-rw-r--r--src/tests/mainloop-test.c4
-rw-r--r--src/tests/mcalign-test.c5
-rw-r--r--src/tests/memblock-test.c2
-rw-r--r--src/tests/memblockq-test.c71
-rw-r--r--src/tests/mix-test.c259
-rw-r--r--src/tests/pacat-simple.c4
-rw-r--r--src/tests/parec-simple.c4
-rw-r--r--src/tests/proplist-test.c60
-rw-r--r--src/tests/queue-test.c8
-rw-r--r--src/tests/remix-test.c89
-rw-r--r--src/tests/resampler-test.c33
-rw-r--r--src/tests/rtpoll-test.c6
-rw-r--r--src/tests/rtstutter.c117
-rw-r--r--src/tests/sig2str-test.c4
-rw-r--r--src/tests/smoother-test.c20
-rw-r--r--src/tests/stripnul.c70
-rw-r--r--src/tests/strlist-test.c3
-rw-r--r--src/tests/sync-playback.c2
-rw-r--r--src/tests/thread-mainloop-test.c6
-rw-r--r--src/tests/thread-test.c2
-rw-r--r--src/tests/utf8-test.c2
-rw-r--r--src/tests/voltest.c4
-rw-r--r--src/utils/pabrowse.c2
-rw-r--r--src/utils/pacat.c199
-rw-r--r--src/utils/pacmd.c17
-rw-r--r--src/utils/pactl.c138
-rwxr-xr-xsrc/utils/padsp2
-rw-r--r--src/utils/padsp.c147
-rw-r--r--src/utils/paplay.c6
-rw-r--r--src/utils/pasuspender.c34
-rw-r--r--src/utils/pax11publish.c2
381 files changed, 25479 insertions, 8124 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..f3ed2e2e
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,56 @@
+*.lo
+*.o
+*.la
+.deps
+.libs
+Makefile
+Makefile.in
+asyncmsgq-test
+asyncq-test
+bt-proximity-helper
+channelmap-test
+client.conf
+close-test
+cpulimit-test
+cpulimit-test2
+daemon.conf
+default.pa
+envelope-test
+esdcompat
+flist-test
+gconf-helper
+get-binary-name-test
+hook-list-test
+interpol-test
+ipacl-test
+mainloop-test
+mainloop-test-glib
+mcalign-test
+memblock-test
+memblockq-test
+mix-test
+pabrowse
+pacat
+pacat-simple
+pacmd
+pactl
+paplay
+parec-simple
+pasuspender
+pax11publish
+proplist-test
+pulseaudio
+queue-test
+remix-test
+resampler-test
+rtpoll-test
+rtstutter
+sig2str-test
+smoother-test
+stripnul
+strlist-test
+sync-playback
+thread-mainloop-test
+thread-test
+utf8-test
+voltest
diff --git a/src/Makefile.am b/src/Makefile.am
index e17e5ece..9cce6ed4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,3 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
# Copyright 2004-2006 Lennart Poettering
@@ -55,6 +53,8 @@ AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\"
AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\"
AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\"
AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\"
AM_CFLAGS += -DAO_REQUIRE_CAS
# This cool debug trap works on i386/gcc only
@@ -64,7 +64,7 @@ AM_LIBADD = $(PTHREAD_LIBS)
AM_LDADD = $(PTHREAD_LIBS)
# Only required on some platforms but defined for all to avoid errors
-AM_LDFLAGS = -Wl,-no-undefined
+AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections
if STATIC_BINS
BINLDFLAGS = -static
@@ -101,7 +101,9 @@ EXTRA_DIST = \
daemon/esdcompat.in \
utils/padsp \
modules/module-defs.h.m4 \
- daemon/pulseaudio-module-xsmp.desktop
+ daemon/pulseaudio-module-xsmp.desktop \
+ map-file \
+ daemon/org.pulseaudio.policy
pulseconf_DATA = \
default.pa \
@@ -129,8 +131,7 @@ pulseaudio_SOURCES = \
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
+ daemon/main.c
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
@@ -150,6 +151,17 @@ else
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
endif
+if HAVE_POLKIT
+
+policy_DATA = daemon/org.pulseaudio.policy
+
+pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h
+pulseaudio_CFLAGS += $(POLKIT_CFLAGS)
+pulseaudio_LDADD += $(POLKIT_LIBS)
+
+
+endif
+
###################################
# Utility programs #
###################################
@@ -184,22 +196,22 @@ paplay_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pactl_SOURCES = utils/pactl.c
+pactl_SOURCES = utils/pactl.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pasuspender_SOURCES = utils/pasuspender.c
+pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h
+pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pacmd_CFLAGS = $(AM_CFLAGS)
pacmd_LDADD = $(AM_LDADD) libpulse.la
pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pax11publish_SOURCES = utils/pax11publish.c
+pax11publish_SOURCES = utils/pax11publish.c pulsecore/x11prop.c pulsecore/x11prop.h pulse/client-conf.c pulse/client-conf.h pulsecore/authkey.h pulsecore/authkey.c pulsecore/random.h pulsecore/random.c pulsecore/conf-parser.c pulsecore/conf-parser.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
pax11publish_LDADD = $(AM_LDADD) libpulse.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
@@ -219,6 +231,7 @@ noinst_PROGRAMS = \
pacat-simple \
parec-simple \
strlist-test \
+ close-test \
voltest \
memblockq-test \
sync-playback \
@@ -238,7 +251,13 @@ noinst_PROGRAMS = \
rtpoll-test \
sig2str-test \
resampler-test \
- smoother-test
+ smoother-test \
+ mix-test \
+ remix-test \
+ envelope-test \
+ proplist-test \
+ rtstutter \
+ stripnul
if HAVE_SIGXCPU
noinst_PROGRAMS += \
@@ -258,7 +277,7 @@ mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c
thread_mainloop_test_CFLAGS = $(AM_CFLAGS)
-thread_mainloop_test_LDADD = $(AM_LDADD) libpulse.la
+thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la
thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
utf8_test_SOURCES = tests/utf8-test.c
@@ -338,6 +357,11 @@ strlist_test_CFLAGS = $(AM_CFLAGS)
strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+close_test_SOURCES = tests/close-test.c
+close_test_CFLAGS = $(AM_CFLAGS)
+close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
voltest_SOURCES = tests/voltest.c
voltest_CFLAGS = $(AM_CFLAGS)
voltest_LDADD = $(AM_LDADD) libpulse.la
@@ -374,7 +398,7 @@ sync_playback_CFLAGS = $(AM_CFLAGS)
sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
interpol_test_SOURCES = tests/interpol-test.c
-interpol_test_LDADD = $(AM_LDADD) libpulse.la
+interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore.la
interpol_test_CFLAGS = $(AM_CFLAGS)
interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
@@ -388,11 +412,41 @@ resampler_test_LDADD = $(AM_LDADD) libpulsecore.la
resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+mix_test_SOURCES = tests/mix-test.c
+mix_test_LDADD = $(AM_LDADD) libpulsecore.la
+mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+remix_test_SOURCES = tests/remix-test.c
+remix_test_LDADD = $(AM_LDADD) libpulsecore.la
+remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
smoother_test_SOURCES = tests/smoother-test.c
smoother_test_LDADD = $(AM_LDADD) libpulsecore.la
smoother_test_CFLAGS = $(AM_CFLAGS)
smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+envelope_test_SOURCES = tests/envelope-test.c
+envelope_test_LDADD = $(AM_LDADD) libpulsecore.la
+envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+proplist_test_SOURCES = tests/proplist-test.c
+proplist_test_LDADD = $(AM_LDADD) libpulsecore.la
+proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+rtstutter_SOURCES = tests/rtstutter.c
+rtstutter_LDADD = $(AM_LDADD) libpulsecore.la
+rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+stripnul_SOURCES = tests/stripnul.c
+stripnul_LDADD = $(AM_LDADD) libpulsecore.la
+stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
###################################
# Client library #
###################################
@@ -420,7 +474,9 @@ pulseinclude_HEADERS = \
pulse/util.h \
pulse/version.h \
pulse/volume.h \
- pulse/xmalloc.h
+ pulse/xmalloc.h \
+ pulse/proplist.h \
+ pulse/gccmacro.h
if HAVE_AVAHI
pulseinclude_HEADERS += \
@@ -470,7 +526,8 @@ libpulse_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
+ pulse/xmalloc.c pulse/xmalloc.h \
+ pulse/proplist.c pulse/proplist.h
# Internal stuff that is shared with libpulsecore
libpulse_la_SOURCES += \
@@ -478,7 +535,6 @@ libpulse_la_SOURCES += \
pulsecore/conf-parser.c pulsecore/conf-parser.h \
pulsecore/core-util.c pulsecore/core-util.h \
pulsecore/dynarray.c pulsecore/dynarray.h \
- pulsecore/gccmacro.h \
pulsecore/hashmap.c pulsecore/hashmap.h \
pulsecore/idxset.c pulsecore/idxset.h \
pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
@@ -511,6 +567,9 @@ libpulse_la_SOURCES += \
pulsecore/object.c pulsecore/object.h \
pulsecore/msgobject.c pulsecore/msgobject.h \
pulsecore/once.c pulsecore/once.h \
+ pulsecore/rtclock.c pulsecore/rtclock.h \
+ pulsecore/time-smoother.c pulsecore/time-smoother.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
@@ -525,7 +584,7 @@ libpulse_la_SOURCES += \
endif
libpulse_la_CFLAGS = $(AM_CFLAGS)
-libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO)
+libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LIBICONV)
if HAVE_X11
@@ -538,20 +597,33 @@ libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
libpulse_la_LIBADD += $(LIBASYNCNS_LIBS)
endif
-libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
+libpulse_simple_la_SOURCES = \
+ pulse/simple.c pulse/simple.h \
+ pulsecore/log.c pulsecore/log.h \
+ pulsecore/core-util.c pulsecore/core-util.h \
+ pulsecore/core-error.c pulsecore/core-error.h \
+ pulsecore/once.c pulsecore/once.h \
+ $(PA_THREAD_OBJS)
+
libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la
-libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
+libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
+libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS)
-libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO)
+libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
+libpulse_mainloop_glib_la_SOURCES = \
+ pulse/glib-mainloop.h pulse/glib-mainloop.c \
+ pulsecore/log.c pulsecore/log.h \
+ pulsecore/core-util.c pulsecore/core-util.h \
+ pulsecore/core-error.c pulsecore/core-error.h \
+ pulsecore/once.c pulsecore/once.h \
+ $(PA_THREAD_OBJS)
libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS)
-libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO)
+libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
###################################
# OSS emulation #
@@ -565,7 +637,7 @@ bin_SCRIPTS += utils/padsp
endif
-libpulsedsp_la_SOURCES = utils/padsp.c
+libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
libpulsedsp_la_CFLAGS = $(AM_CFLAGS)
libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la
libpulsedsp_la_LDFLAGS = -avoid-version
@@ -579,7 +651,7 @@ noinst_LTLIBRARIES = libspeex-resampler-fixed.la libspeex-resampler-float.la lib
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_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfl -DOUTSIDE_SPEEX -DFLOATING_POINT
libspeex_resampler_float_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h
libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS)
@@ -597,7 +669,6 @@ noinst_HEADERS = \
pulsecore/cli-text.h \
pulsecore/client.h \
pulsecore/core.h \
- pulsecore/core-def.h \
pulsecore/core-scache.h \
pulsecore/core-subscribe.h \
pulsecore/conf-parser.h \
@@ -656,7 +727,8 @@ libpulsecore_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
+ pulse/xmalloc.c pulse/xmalloc.h \
+ pulse/proplist.c pulse/proplist.h
# Pure core stuff (some are shared in libpulse though).
libpulsecore_la_SOURCES += \
@@ -723,6 +795,9 @@ libpulsecore_la_SOURCES += \
pulsecore/macro.h \
pulsecore/once.c pulsecore/once.h \
pulsecore/time-smoother.c pulsecore/time-smoother.h \
+ pulsecore/start-child.c pulsecore/start-child.h \
+ pulsecore/envelope.c pulsecore/envelope.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
@@ -849,7 +924,7 @@ libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la
libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h
libpstream_util_la_LDFLAGS = -avoid-version
-libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la
+libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la
libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h
libpdispatch_la_LDFLAGS = -avoid-version
@@ -944,7 +1019,9 @@ modlibexec_LTLIBRARIES += \
module-null-sink.la \
module-detect.la \
module-volume-restore.la \
+ module-device-restore.la \
module-default-device-restore.la \
+ module-always-sink.la \
module-rescue-streams.la \
module-suspend-on-idle.la \
module-http-protocol-tcp.la \
@@ -955,9 +1032,11 @@ modlibexec_LTLIBRARIES += \
module-combine.la \
module-remap-sink.la \
module-ladspa-sink.la \
- module-esound-sink.la
-# module-tunnel-sink.la
-# module-tunnel-source.la
+ module-esound-sink.la \
+ module-tunnel-sink.la \
+ module-tunnel-source.la \
+ module-position-event-sounds.la
+
# See comment at librtp.la above
if !OS_IS_WIN32
@@ -1019,7 +1098,8 @@ endif
if HAVE_AVAHI
modlibexec_LTLIBRARIES += \
- module-zeroconf-publish.la
+ module-zeroconf-publish.la \
+ module-zeroconf-discover.la
endif
if HAVE_LIRC
@@ -1038,11 +1118,13 @@ modlibexec_LTLIBRARIES += \
module-jack-source.la
endif
+pulselibexec_PROGRAMS =
+
if HAVE_GCONF
modlibexec_LTLIBRARIES += \
module-gconf.la
-pulselibexec_PROGRAMS = \
+pulselibexec_PROGRAMS += \
gconf-helper
endif
@@ -1057,6 +1139,20 @@ modlibexec_LTLIBRARIES += \
module-hal-detect.la
endif
+if HAVE_DBUS
+modlibexec_LTLIBRARIES += \
+ libdbus-util.la \
+ module-console-kit.la
+endif
+
+if HAVE_BLUEZ
+modlibexec_LTLIBRARIES += \
+ module-bt-proximity.la
+
+pulselibexec_PROGRAMS += \
+ bt-proximity-helper
+endif
+
# These are generated by a M4 script
SYMDEF_FILES = \
@@ -1084,6 +1180,7 @@ SYMDEF_FILES = \
modules/module-null-sink-symdef.h \
modules/module-esound-sink-symdef.h \
modules/module-zeroconf-publish-symdef.h \
+ modules/module-zeroconf-discover-symdef.h \
modules/module-lirc-symdef.h \
modules/module-mmkbd-evdev-symdef.h \
modules/module-http-protocol-tcp-symdef.h \
@@ -1102,19 +1199,24 @@ SYMDEF_FILES = \
modules/module-jack-sink-symdef.h \
modules/module-jack-source-symdef.h \
modules/module-volume-restore-symdef.h \
+ modules/module-device-restore-symdef.h \
modules/module-default-device-restore-symdef.h \
+ modules/module-always-sink-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
+ modules/module-bt-proximity-symdef.h \
+ modules/gconf/module-gconf-symdef.h \
+ modules/module-position-event-sounds-symdef.h \
+ modules/module-console-kit-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
BUILT_SOURCES += $(SYMDEF_FILES)
$(SYMDEF_FILES): modules/module-defs.h.m4
- -mkdir modules
- -mkdir modules/gconf
- -mkdir modules/rtp
+ $(MKDIR_P) modules
+ $(MKDIR_P) modules/gconf
+ $(MKDIR_P) modules/rtp
$(M4) -Dfname="$@" $< > $@
# Simple protocol
@@ -1196,7 +1298,7 @@ module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_esound_sink_la_SOURCES = modules/module-esound-sink.c
module_esound_sink_la_LDFLAGS = -module -avoid-version
-module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la
+module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la libsocket-util.la
# Pipes
@@ -1229,7 +1331,7 @@ 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_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS)
module_ladspa_sink_la_LDFLAGS = -module -avoid-version
module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la
@@ -1237,14 +1339,14 @@ 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
@@ -1261,7 +1363,7 @@ module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EX
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
+module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la
# OSS
@@ -1303,6 +1405,11 @@ module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
+module_zeroconf_discover_la_LDFLAGS = -module -avoid-version
+module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
# LIRC
module_lirc_la_SOURCES = modules/module-lirc.c
@@ -1336,12 +1443,30 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version
module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
+# Position event sounds in space
+module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c
+module_position_event_sounds_la_LDFLAGS = -module -avoid-version
+module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_position_event_sounds_CFLAGS = $(AM_CFLAGS)
+
+# Device volume restore module
+module_device_restore_la_SOURCES = modules/module-device-restore.c
+module_device_restore_la_LDFLAGS = -module -avoid-version
+module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm
+module_device_restore_la_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)
+# Always Sink module
+module_always_sink_la_SOURCES = modules/module-always-sink.c
+module_always_sink_la_LDFLAGS = -module -avoid-version
+module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_always_sink_la_CFLAGS = $(AM_CFLAGS)
+
# Rescue streams module
module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c
module_rescue_streams_la_LDFLAGS = -module -avoid-version
@@ -1377,17 +1502,22 @@ module_jack_source_la_LDFLAGS = -module -avoid-version
module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
-# HAL
+# HAL/D-Bus
libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
libdbus_util_la_LDFLAGS = -avoid-version
-libdbus_util_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la
-libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la
+libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
module_hal_detect_la_SOURCES = modules/module-hal-detect.c
module_hal_detect_la_LDFLAGS = -module -avoid-version
module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+module_console_kit_la_SOURCES = modules/module-console-kit.c
+module_console_kit_la_LDFLAGS = -module -avoid-version
+module_console_kit_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
# GConf support
module_gconf_la_SOURCES = modules/gconf/module-gconf.c
module_gconf_la_LDFLAGS = -module -avoid-version
@@ -1399,6 +1529,17 @@ gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la
gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+# Bluetooth proximity
+module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c
+module_bt_proximity_la_LDFLAGS = -module -avoid-version
+module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\"
+
+bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c
+bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
+bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
+bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
###################################
# Some minor stuff #
###################################
@@ -1435,10 +1576,12 @@ daemon.conf: daemon/daemon.conf.in Makefile
install-exec-hook:
chown root $(DESTDIR)$(bindir)/pulseaudio ; true
chmod u+s $(DESTDIR)$(bindir)/pulseaudio
+ -chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper
ln -sf pacat $(DESTDIR)$(bindir)/parec
rm -f $(DESTDIR)$(modlibexecdir)/*.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.la
+ rm -f $(DESTDIR)$(modlibexecdir)/*.la
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
@@ -1452,4 +1595,13 @@ update-speex:
update-ffmpeg:
wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+# Automatically generate linker version script. We use the same one for all public .sos
+update-map-file:
+ ( echo "PULSE_0 {" ; \
+ echo "global:" ; \
+ ctags -I PA_GCC_PURE,PA_GCC_CONST -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \
+ echo "local:" ; \
+ echo "*;" ; \
+ echo "};" ) > $(srcdir)/map-file
+
.PHONY: utils/padsp
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index 5b4008a5..ae07119c 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -63,13 +61,16 @@ void pa_drop_root(void) {
pa_log_info("Dropping root priviliges.");
#if defined(HAVE_SETRESUID)
- setresuid(uid, uid, uid);
+ pa_assert_se(setresuid(uid, uid, uid) >= 0);
#elif defined(HAVE_SETREUID)
- setreuid(uid, uid);
+ pa_assert_se(setreuid(uid, uid) >= 0);
#else
- setuid(uid);
- seteuid(uid);
+ pa_assert_se(setuid(uid) >= 0);
+ pa_assert_se(seteuid(uid) >= 0);
#endif
+
+ pa_assert_se(getuid() == uid);
+ pa_assert_se(geteuid() == uid);
}
#else
@@ -82,69 +83,65 @@ void pa_drop_root(void) {
#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H)
/* Limit permitted capabilities set to CAPSYS_NICE */
-int pa_limit_caps(void) {
- int r = -1;
+void pa_limit_caps(void) {
cap_t caps;
cap_value_t nice_cap = CAP_SYS_NICE;
- caps = cap_init();
- 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);
+ pa_assert_se(caps = cap_init());
+ pa_assert_se(cap_clear(caps) == 0);
+ pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0);
+ pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0);
if (cap_set_proc(caps) < 0)
- goto fail;
-
- if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)
- goto fail;
+ /* Hmm, so we couldn't limit our caps, which probably means we
+ * hadn't any in the first place, so let's just make sure of
+ * that */
+ pa_drop_caps();
+ else
+ pa_log_info("Limited capabilities successfully to CAP_SYS_NICE.");
- pa_log_info("Dropped capabilities successfully.");
+ pa_assert_se(cap_free(caps) == 0);
- r = 1;
-
-fail:
- cap_free(caps);
-
- return r;
+ pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0);
}
/* Drop all capabilities, effectively becoming a normal user */
-int pa_drop_caps(void) {
+void pa_drop_caps(void) {
cap_t caps;
- int r = -1;
-
- caps = cap_init();
- pa_assert(caps);
- cap_clear(caps);
+ pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0);
- prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
+ pa_assert_se(caps = cap_init());
+ pa_assert_se(cap_clear(caps) == 0);
+ pa_assert_se(cap_set_proc(caps) == 0);
+ pa_assert_se(cap_free(caps) == 0);
- if (cap_set_proc(caps) < 0) {
- pa_log("Failed to drop capabilities: %s", pa_cstrerror(errno));
- goto fail;
- }
+ pa_assert_se(!pa_have_caps());
+}
- r = 0;
+pa_bool_t pa_have_caps(void) {
+ cap_t caps;
+ cap_flag_value_t flag = CAP_CLEAR;
-fail:
- cap_free(caps);
+ pa_assert_se(caps = cap_get_proc());
+ pa_assert_se(cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0);
+ pa_assert_se(cap_free(caps) == 0);
- return r;
+ return flag == CAP_SET;
}
#else
/* NOOPs in case capabilities are not available. */
-int pa_limit_caps(void) {
- return 0;
+void pa_limit_caps(void) {
}
-int pa_drop_caps(void) {
+void pa_drop_caps(void) {
pa_drop_root();
- return 0;
}
-#endif
+pa_bool_t pa_have_caps(void) {
+ return FALSE;
+}
+#endif
diff --git a/src/daemon/caps.h b/src/daemon/caps.h
index 4cd09578..176aa90e 100644
--- a/src/daemon/caps.h
+++ b/src/daemon/caps.h
@@ -1,8 +1,6 @@
#ifndef foocapshfoo
#define foocapshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,8 +22,11 @@
USA.
***/
+#include <pulsecore/macro.h>
+
void pa_drop_root(void);
-int pa_limit_caps(void);
-int pa_drop_caps(void);
+void pa_drop_caps(void);
+void pa_limit_caps(void);
+pa_bool_t pa_have_caps(void);
#endif
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index d14002f6..4b2466ce 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,6 +47,7 @@ enum {
ARG_FAIL,
ARG_LOG_LEVEL,
ARG_HIGH_PRIORITY,
+ ARG_REALTIME,
ARG_DISALLOW_MODULE_LOADING,
ARG_EXIT_IDLE_TIME,
ARG_MODULE_IDLE_TIME,
@@ -65,11 +64,12 @@ enum {
ARG_DISABLE_SHM,
ARG_DUMP_RESAMPLE_METHODS,
ARG_SYSTEM,
- ARG_CLEANUP_SHM
+ ARG_CLEANUP_SHM,
+ ARG_START
};
/* Tabel for getopt_long() */
-static struct option long_options[] = {
+static const struct option long_options[] = {
{"help", 0, 0, ARG_HELP},
{"version", 0, 0, ARG_VERSION},
{"dump-conf", 0, 0, ARG_DUMP_CONF},
@@ -79,6 +79,7 @@ static struct option long_options[] = {
{"verbose", 2, 0, ARG_LOG_LEVEL},
{"log-level", 2, 0, ARG_LOG_LEVEL},
{"high-priority", 2, 0, ARG_HIGH_PRIORITY},
+ {"realtime", 2, 0, ARG_REALTIME},
{"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
{"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME},
{"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
@@ -89,6 +90,7 @@ static struct option long_options[] = {
{"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
{"resample-method", 1, 0, ARG_RESAMPLE_METHOD},
{"kill", 0, 0, ARG_KILL},
+ {"start", 0, 0, ARG_START},
{"use-pid-file", 2, 0, ARG_USE_PID_FILE},
{"check", 0, 0, ARG_CHECK},
{"system", 2, 0, ARG_SYSTEM},
@@ -103,7 +105,7 @@ void pa_cmdline_help(const char *argv0) {
const char *e;
pa_assert(argv0);
-
+
if ((e = strrchr(argv0, '/')))
e++;
else
@@ -117,6 +119,7 @@ void pa_cmdline_help(const char *argv0) {
" --dump-modules Dump list of available modules\n"
" --dump-resample-methods Dump available resample methods\n"
" --cleanup-shm Cleanup stale shared memory segments\n"
+ " --start Start the daemon if it is not running\n"
" -k --kill Kill a running daemon\n"
" --check Check for a running daemon\n\n"
@@ -124,8 +127,12 @@ void pa_cmdline_help(const char *argv0) {
" --system[=BOOL] Run as system-wide instance\n"
" -D, --daemonize[=BOOL] Daemonize after startup\n"
" --fail[=BOOL] Quit when startup fails\n"
- " --high-priority[=BOOL] Try to set high process priority\n"
- " (only available as root)\n"
+ " --high-priority[=BOOL] Try to set high nice level\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_NICE)\n"
+ " --realtime[=BOOL] Try to enable realtime scheduling\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_RTPRIO)\n"
" --disallow-module-loading[=BOOL] Disallow module loading after startup\n"
" --exit-idle-time=SECS Terminate the daemon when idle and this\n"
" time passed\n"
@@ -138,7 +145,7 @@ void pa_cmdline_help(const char *argv0) {
" --log-target={auto,syslog,stderr} Specify the log target\n"
" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
" objects (plugins)\n"
- " --resample-method=[METHOD] Use the specified resampling method\n"
+ " --resample-method=METHOD Use the specified resampling method\n"
" (See --dump-resample-methods for\n"
" possible values)\n"
" --use-pid-file[=BOOL] Create a PID file\n"
@@ -159,7 +166,7 @@ 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;
-
+
pa_assert(conf);
pa_assert(argc > 0);
pa_assert(argv);
@@ -195,12 +202,17 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
case ARG_CLEANUP_SHM:
conf->cmd = PA_CMD_CLEANUP_SHM;
break;
-
+
case 'k':
case ARG_KILL:
conf->cmd = PA_CMD_KILL;
break;
+ case ARG_START:
+ conf->cmd = PA_CMD_START;
+ conf->daemonize = TRUE;
+ break;
+
case ARG_CHECK:
conf->cmd = PA_CMD_CHECK;
break;
@@ -224,14 +236,14 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
case ARG_DAEMONIZE:
case 'D':
- if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--daemonize expects boolean argument");
goto fail;
}
break;
case ARG_FAIL:
- if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->fail = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--fail expects boolean argument");
goto fail;
}
@@ -253,21 +265,28 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case ARG_HIGH_PRIORITY:
- if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--high-priority expects boolean argument");
goto fail;
}
break;
+ case ARG_REALTIME:
+ if ((conf->realtime_scheduling = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+ pa_log("--realtime expects boolean argument");
+ goto fail;
+ }
+ break;
+
case ARG_DISALLOW_MODULE_LOADING:
- if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--disallow-module-loading expects boolean argument");
goto fail;
}
break;
case ARG_USE_PID_FILE:
- if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--use-pid-file expects boolean argument");
goto fail;
}
@@ -280,8 +299,7 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case 'n':
- pa_xfree(conf->default_script_file);
- conf->default_script_file = NULL;
+ conf->load_default_script_file = FALSE;
break;
case ARG_LOG_TARGET:
@@ -311,21 +329,21 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case ARG_SYSTEM:
- if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--system expects boolean argument");
goto fail;
}
break;
case ARG_NO_CPU_LIMIT:
- if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--no-cpu-limit expects boolean argument");
goto fail;
}
break;
case ARG_DISABLE_SHM:
- if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--disable-shm expects boolean argument");
goto fail;
}
diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h
index 18418894..fd72a6d3 100644
--- a/src/daemon/cmdline.h
+++ b/src/daemon/cmdline.h
@@ -1,8 +1,6 @@
#ifndef foocmdlinehfoo
#define foocmdlinehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index a61f43eb..42a71f7e 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -82,7 +80,7 @@ static pa_io_event *io_event = NULL;
static struct sigaction sigaction_prev;
/* Nonzero after pa_cpu_limit_init() */
-static int installed = 0;
+static pa_bool_t installed = FALSE;
/* The current state of operation */
static enum {
@@ -113,6 +111,9 @@ static void write_err(const char *p) {
/* The signal handler, called on every SIGXCPU */
static void signal_handler(int sig) {
+ int saved_errno;
+
+ saved_errno = errno;
pa_assert(sig == SIGXCPU);
if (phase == PHASE_IDLE) {
@@ -148,8 +149,10 @@ static void signal_handler(int sig) {
} else if (phase == PHASE_SOFT) {
write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
- _exit(1); /* Forced exit */
+ abort(); /* Forced exit */
}
+
+ errno = saved_errno;
}
/* Callback for IO events on the FIFO */
@@ -160,7 +163,7 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags
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 +171,7 @@ 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;
-
+
pa_assert(m);
pa_assert(!api);
pa_assert(!io_event);
@@ -205,7 +208,7 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {
return -1;
}
- installed = 1;
+ installed = TRUE;
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
@@ -226,7 +229,7 @@ void pa_cpu_limit_done(void) {
if (installed) {
pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
- installed = 0;
+ installed = FALSE;
}
}
diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h
index 271109b4..cb9a123d 100644
--- a/src/daemon/cpulimit.h
+++ b/src/daemon/cpulimit.h
@@ -1,8 +1,6 @@
#ifndef foocpulimithfoo
#define foocpulimithfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 825101c7..9ac40901 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,8 +28,10 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <sched.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@@ -44,6 +44,8 @@
#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_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
@@ -53,51 +55,71 @@
static const pa_daemon_conf default_conf = {
.cmd = PA_CMD_DAEMON,
- .daemonize = 0,
- .fail = 1,
- .high_priority = 0,
- .disallow_module_loading = 0,
+ .daemonize = FALSE,
+ .fail = TRUE,
+ .high_priority = TRUE,
+ .nice_level = -11,
+ .realtime_scheduling = FALSE,
+ .realtime_priority = 5, /* Half of JACK's default rtprio */
+ .disallow_module_loading = FALSE,
.exit_idle_time = -1,
.module_idle_time = 20,
.scache_idle_time = 20,
.auto_log_target = 1,
.script_commands = NULL,
.dl_search_path = NULL,
+ .load_default_script_file = TRUE,
.default_script_file = NULL,
.log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE,
.resample_method = PA_RESAMPLER_AUTO,
+ .disable_remixing = FALSE,
.config_file = NULL,
- .use_pid_file = 1,
- .system_instance = 0,
- .no_cpu_limit = 0,
- .disable_shm = 0,
+ .use_pid_file = TRUE,
+ .system_instance = FALSE,
+ .no_cpu_limit = FALSE,
+ .disable_shm = FALSE,
.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 = 256, .is_set = 1 },
- .rlimit_stack = { .value = 0, .is_set = 0 }
+ ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
+ .rlimit_data = { .value = 0, .is_set = FALSE },
+ .rlimit_stack = { .value = 0, .is_set = FALSE },
+ .rlimit_core = { .value = 0, .is_set = FALSE },
+ .rlimit_rss = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_NPROC
- , .rlimit_nproc = { .value = 0, .is_set = 0 }
+ ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
+ ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
#ifdef RLIMIT_MEMLOCK
- , .rlimit_memlock = { .value = 16384, .is_set = 1 }
+ ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
+#endif
+ ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_LOCKS
+ ,.rlimit_locks = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_SIGPENDING
+ ,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_NICE
+ ,.rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
+#endif
+#ifdef RLIMIT_RTPRIO
+ ,.rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+ ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
#endif
#endif
};
pa_daemon_conf* pa_daemon_conf_new(void) {
- FILE *f;
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);
-
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}
@@ -185,7 +207,7 @@ 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;
-
+
pa_assert(filename);
pa_assert(lvalue);
pa_assert(rvalue);
@@ -271,7 +293,7 @@ static int parse_sample_rate(const char *filename, unsigned line, const char *lv
pa_assert(rvalue);
pa_assert(data);
- if (pa_atoi(rvalue, &r) < 0 || r > PA_RATE_MAX || r <= 0) {
+ if (pa_atoi(rvalue, &r) < 0 || r > (int32_t) PA_RATE_MAX || r <= 0) {
pa_log("[%s:%u] Invalid sample rate '%s'.", filename, line, rvalue);
return -1;
}
@@ -289,11 +311,11 @@ static int parse_sample_channels(const char *filename, unsigned line, const char
pa_assert(rvalue);
pa_assert(data);
- if (pa_atoi(rvalue, &n) < 0 || n > PA_CHANNELS_MAX || n <= 0) {
+ if (pa_atoi(rvalue, &n) < 0 || n > (int32_t) 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;
}
@@ -334,6 +356,42 @@ static int parse_fragment_size_msec(const char *filename, unsigned line, const c
return 0;
}
+static int parse_nice_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ int32_t level;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &level) < 0 || level < -20 || level > 19) {
+ pa_log("[%s:%u] Invalid nice level '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->nice_level = (int) level;
+ return 0;
+}
+
+static int parse_rtprio(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 rtprio;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) {
+ pa_log("[%s:%u] Invalid realtime priority '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->realtime_priority = (int) rtprio;
+ return 0;
+}
+
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
int r = -1;
FILE *f = NULL;
@@ -342,38 +400,62 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "daemonize", pa_config_parse_bool, NULL },
{ "fail", pa_config_parse_bool, NULL },
{ "high-priority", pa_config_parse_bool, NULL },
+ { "realtime-scheduling", pa_config_parse_bool, NULL },
{ "disallow-module-loading", pa_config_parse_bool, 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 },
{ "exit-idle-time", pa_config_parse_int, NULL },
{ "module-idle-time", pa_config_parse_int, NULL },
{ "scache-idle-time", pa_config_parse_int, NULL },
+ { "realtime-priority", parse_rtprio, 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 },
+ { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
+ { "nice-level", parse_nice_level, NULL },
+ { "disable-remixing", pa_config_parse_bool, NULL },
+ { "load-default-script-file", pa_config_parse_bool, 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-data", parse_rlimit, NULL },
{ "rlimit-stack", parse_rlimit, NULL },
+ { "rlimit-core", parse_rlimit, NULL },
+ { "rlimit-rss", parse_rlimit, NULL },
+ { "rlimit-nofile", parse_rlimit, NULL },
+ { "rlimit-as", parse_rlimit, NULL },
#ifdef RLIMIT_NPROC
{ "rlimit-nproc", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_MEMLOCK
{ "rlimit-memlock", parse_rlimit, NULL },
#endif
+#ifdef RLIMIT_LOCKS
+ { "rlimit-locks", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+ { "rlimit-sigpending", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ { "rlimit-msgqueue", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_NICE
+ { "rlimit-nice", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_RTPRIO
+ { "rlimit-rtprio", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+ { "rlimit-rttime", parse_rlimit, NULL },
+#endif
#endif
{ NULL, NULL, NULL },
};
@@ -381,40 +463,89 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[0].data = &c->daemonize;
table[1].data = &c->fail;
table[2].data = &c->high_priority;
- table[3].data = &c->disallow_module_loading;
- table[4].data = &c->exit_idle_time;
- table[5].data = &c->module_idle_time;
- table[6].data = &c->scache_idle_time;
- table[7].data = &c->dl_search_path;
- table[8].data = &c->default_script_file;
- table[9].data = c;
- table[10].data = c;
- table[11].data = c;
+ table[3].data = &c->realtime_scheduling;
+ table[4].data = &c->disallow_module_loading;
+ table[5].data = &c->use_pid_file;
+ table[6].data = &c->system_instance;
+ table[7].data = &c->no_cpu_limit;
+ table[8].data = &c->disable_shm;
+ table[9].data = &c->exit_idle_time;
+ table[10].data = &c->module_idle_time;
+ table[11].data = &c->scache_idle_time;
table[12].data = c;
- table[13].data = &c->use_pid_file;
- table[14].data = &c->system_instance;
- table[15].data = &c->no_cpu_limit;
- table[16].data = &c->disable_shm;
+ table[13].data = &c->dl_search_path;
+ table[14].data = &c->default_script_file;
+ table[15].data = c;
+ table[16].data = c;
table[17].data = c;
table[18].data = c;
table[19].data = c;
table[20].data = c;
table[21].data = c;
+ table[22].data = c;
+ table[23].data = c;
+ table[24].data = c;
+ table[25].data = &c->disable_remixing;
+ table[26].data = &c->load_default_script_file;
#ifdef HAVE_SYS_RESOURCE_H
- 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;
+ table[27].data = &c->rlimit_fsize;
+ table[28].data = &c->rlimit_data;
+ table[29].data = &c->rlimit_stack;
+ table[30].data = &c->rlimit_as;
+ table[31].data = &c->rlimit_core;
+ table[32].data = &c->rlimit_nofile;
+ table[33].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC
- table[28].data = &c->rlimit_nproc;
+ table[34].data = &c->rlimit_nproc;
#endif
+
#ifdef RLIMIT_MEMLOCK
#ifndef RLIMIT_NPROC
#error "Houston, we have a numbering problem!"
#endif
- table[29].data = &c->rlimit_memlock;
+ table[35].data = &c->rlimit_memlock;
+#endif
+
+#ifdef RLIMIT_LOCKS
+#ifndef RLIMIT_MEMLOCK
+#error "Houston, we have a numbering problem!"
+#endif
+ table[36].data = &c->rlimit_locks;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+#ifndef RLIMIT_LOCKS
+#error "Houston, we have a numbering problem!"
+#endif
+ table[37].data = &c->rlimit_sigpending;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+#ifndef RLIMIT_SIGPENDING
+#error "Houston, we have a numbering problem!"
+#endif
+ table[38].data = &c->rlimit_msgqueue;
+#endif
+
+#ifdef RLIMIT_NICE
+#ifndef RLIMIT_MSGQUEUE
+#error "Houston, we have a numbering problem!"
+#endif
+ table[39].data = &c->rlimit_nice;
+#endif
+
+#ifdef RLIMIT_RTPRIO
+#ifndef RLIMIT_NICE
+#error "Houston, we have a numbering problem!"
+#endif
+ table[40].data = &c->rlimit_rtprio;
+#endif
+
+#ifdef RLIMIT_RTTIME
+#ifndef RLIMIT_RTTIME
+#error "Houston, we have a numbering problem!"
+#endif
+ table[41].data = &c->rlimit_rttime;
#endif
#endif
@@ -423,10 +554,10 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
f = filename ?
fopen(c->config_file = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
+ pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
if (!f && errno != ENOENT) {
- pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
+ pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno));
goto finish;
}
@@ -441,6 +572,7 @@ finish:
int pa_daemon_conf_env(pa_daemon_conf *c) {
char *e;
+ pa_assert(c);
if ((e = getenv(ENV_DL_SEARCH_PATH))) {
pa_xfree(c->dl_search_path);
@@ -454,6 +586,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) {
return 0;
}
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+ else
+ c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+ }
+
+ return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+ FILE *f;
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+ else
+ f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+ } else
+ f = fopen(c->default_script_file, "r");
+
+ return f;
+}
+
+
static const char* const log_level_to_string[] = {
[PA_LOG_DEBUG] = "debug",
[PA_LOG_INFO] = "info",
@@ -474,40 +635,64 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
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);
- pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority);
- pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading);
+ pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
+ pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
+ pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority));
+ pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
+ pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
+ pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
+ pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
+ pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
+ pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+ pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
+ pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
- pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
- pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
+ pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+ pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+ pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
- pa_strbuf_printf(s, "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-remixing = %s\n", pa_yes_no(c->disable_remixing));
pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
#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);
- pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
- pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+ pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+ pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+ 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-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
#ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif
+ pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
#ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif
+#ifdef RLIMIT_LOCKS
+ pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
+#ifdef RLIMIT_NICE
+ pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
+#endif
+#ifdef RLIMIT_RTPRIO
+ pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
+#endif
#endif
return pa_strbuf_tostring_free(s);
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 4d37861d..be2fe1ab 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -1,8 +1,6 @@
#ifndef foodaemonconfhfoo
#define foodaemonconfhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,8 @@
***/
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H
@@ -35,6 +35,7 @@
/* The actual command to execute */
typedef enum pa_daemon_conf_cmd {
PA_CMD_DAEMON, /* the default */
+ PA_CMD_START,
PA_CMD_HELP,
PA_CMD_VERSION,
PA_CMD_DUMP_CONF,
@@ -48,39 +49,62 @@ typedef enum pa_daemon_conf_cmd {
#ifdef HAVE_SYS_RESOURCE_H
typedef struct pa_rlimit {
rlim_t value;
- int is_set;
+ pa_bool_t is_set;
} pa_rlimit;
#endif
/* A structure containing configuration data for the PulseAudio server . */
typedef struct pa_daemon_conf {
pa_daemon_conf_cmd_t cmd;
- int daemonize,
+ pa_bool_t daemonize,
fail,
high_priority,
+ realtime_scheduling,
disallow_module_loading,
- exit_idle_time,
- module_idle_time,
- scache_idle_time,
- auto_log_target,
use_pid_file,
system_instance,
no_cpu_limit,
- disable_shm;
+ disable_shm,
+ disable_remixing,
+ load_default_script_file;
+ int exit_idle_time,
+ module_idle_time,
+ scache_idle_time,
+ auto_log_target,
+ realtime_priority,
+ nice_level,
+ resample_method;
char *script_commands, *dl_search_path, *default_script_file;
pa_log_target_t log_target;
pa_log_level_t log_level;
- int resample_method;
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
- pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
#ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
pa_rlimit rlimit_memlock;
#endif
+#ifdef RLIMIT_LOCKS
+ pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_rlimit rlimit_msgqueue;
+#endif
+#ifdef RLIMIT_NICE
+ pa_rlimit rlimit_nice;
+#endif
+#ifdef RLIMIT_RTPRIO
+ pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_rlimit rlimit_rttime;
+#endif
#endif
unsigned default_n_fragments, default_fragment_size_msec;
@@ -110,4 +134,7 @@ 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);
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 2132bf3d..dfabcfb2 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -1,5 +1,3 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
@@ -17,94 +15,59 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-## Configuration file for the pulseaudio daemon. Default values are
-## commented out. Use either ; or # for commenting
-
-# Extra verbositiy
-; verbose = 0
+## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
+## more information. Default values a commented out. Use either ; or # for
+## commenting.
-## Daemonize after startup
-; daemonize = 0
+; daemonize = no
+; fail = yes
+; disallow-module-loading = no
+; use-pid-file = yes
+; system-instance = no
+; disable-shm = no
-## Quit if startup fails
-; fail = 1
+; high-priority = yes
+; nice-level = -11
-## Renice the daemon to level -15 and try to get SCHED_FIFO
-## scheduling. This a good idea if you hear annyoing noise in the
-## playback. However, this is a certain security issue, since it works
-## when called SUID root only. root is dropped immediately after gaining
-## the nice level and SCHED_FIFO scheduling on startup.
-; high-priority = 0
+; realtime-scheduling = no
+; realtime-priority = 5
-## Disallow module loading after startup
-; disallow-module-loading = 0
-
-## Terminate the daemon after the last client quit and this time
-## passed. Use a negative value to disable this feature.
; exit-idle-time = -1
-
-## Unload autoloaded modules after being idle for this time
; module-idle-time = 20
-
-## Unload autoloaded sample cache entries after being idle for this time
; scache-idle-time = 20
-## The path were to look for dynamic shared objects (DSOs aka
-## plugins). You may specify more than one path seperated by
-## colons.
-; dl-search-path = @PA_DLSEARCHPATH@
+; dl-search-path = (depends on architecture)
-## The default script file to load. Specify an empty string for not
-## loading a default script file. The
+; load-defaul-script-file = yes
; default-script-file = @PA_DEFAULT_CONFIG_FILE@
-## The default log target. Use either "stderr", "syslog" or
-## "auto". The latter is equivalent to "sylog" in case daemonize is
-## true, otherwise to "stderr".
; log-target = auto
+; log-level = notice
-## The resampling algorithm to use. Use one of src-sinc-best-quality,
-## src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold,
-## src-linear, trivial. See the documentation of libsamplerate for an
-## explanation for the different methods. The method 'trivial' is the
-## only algorithm implemented without usage of floating point
-## numbers. If you're tight on CPU consider using this. On the other
-## hand it has the worst quality of all.
-; resample-method = sinc-fastest
-
-## Create a PID file in /tmp/pulseaudio-$USER/pid. Of this is enabled
-## you may use commands like "pulseaudio --kill" or "pulseaudio
-## --check". If you are planning to start more than one pulseaudio
-## process per user, you better disable this option since it
-## effectively disables multiple instances.
-; use-pid-file = 1
+; resample-method = speex-float-3
+; disable-remixing = no
-## Do not install the CPU load limit, even on platforms where it is
-## supported. This option is useful when debugging/profiling
-## PulseAudio to disable disturbing SIGXCPU signals.
-; no-cpu-limit = 0
+; no-cpu-limit = no
-## Run the daemon as system-wide instance, requires root priviliges
-; system-instance = 0
-
-## Resource limits, see getrlimit(2) for more information
-; rlimit-as = -1
-; rlimit-core = -1
-; rlimit-data = -1
; rlimit-fsize = -1
-; rlimit-nofile = 256
+; rlimit-data = -1
; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
; rlimit-nproc = -1
-; rlimit-memlock = 16384
-
-## Disable shared memory data transfer
-; disable-shm = 0
+; rlimit-nofile = 256
+; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
+; rlimit-nice = 31
+; rlimit-rtprio = 9
+; rlimit-rtttime = 1000000
-## 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 597993c4..aad9f5d6 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -37,7 +37,7 @@ load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav
#load-module module-pipe-sink
### Automatically load driver modules depending on the hardware available
-.ifexists @PA_DLSEARCHPATH@/module-hal-detect@PA_SOEXT@
+.ifexists module-hal-detect@PA_SOEXT@
load-module module-hal-detect
.else
### Alternatively use the static hardware detection module (for systems that
@@ -46,7 +46,9 @@ load-module module-detect
.endif
### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
load-module module-esound-protocol-unix
+.endif
load-module module-native-protocol-unix
### Network access (may be configured with paprefs, so leave this commented
@@ -72,27 +74,41 @@ load-module module-default-device-restore
### connected to dies, similar for sources
load-module module-rescue-streams
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
### 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
-
-### Publish connection data in the X11 root window
-.ifexists @PA_DLSEARCHPATH@/module-x11-publish@PA_SOEXT@
-load-module module-x11-publish
-.endif
+#load-module module-x11-bell sample=bell-windowing-system
### 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-module module-x11-xsmp
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+load-module module-console-kit
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
### 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@
+.ifexists module-gconf@PA_SOEXT@
+.nofail
load-module module-gconf
+.fail
+.endif
+
+### Publish connection data in the X11 root window
+.ifexists module-x11-publish@PA_SOEXT@
+.nofail
+load-module module-x11-publish
+.fail
.endif
### Make some devices default
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
index 01547a30..cd6866aa 100644
--- a/src/daemon/dumpmodules.c
+++ b/src/daemon/dumpmodules.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@
static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) {
pa_assert(name);
pa_assert(i);
-
+
printf("%-40s%s\n", name, i->description ? i->description : "n/a");
}
@@ -71,6 +69,7 @@ static void long_info(const char *name, const char *path, pa_modinfo *i) {
printf("Author: %s\n", i->author);
if (i->usage)
printf("Usage: %s\n", i->usage);
+ printf("Load Once: %s\n", pa_yes_no(i->load_once));
}
if (path)
@@ -81,7 +80,7 @@ static void show_info(const char *name, const char *path, void (*info)(const cha
pa_modinfo *i;
pa_assert(name);
-
+
if ((i = pa_modinfo_get_by_name(path ? path : name))) {
info(name, path, i);
pa_modinfo_free(i);
@@ -128,7 +127,7 @@ 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++)
diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h
index ab2ddb64..c49a5eda 100644
--- a/src/daemon/dumpmodules.h
+++ b/src/daemon/dumpmodules.h
@@ -1,8 +1,6 @@
#ifndef foodumpmoduleshfoo
#define foodumpmoduleshfoo
-/* $Id*/
-
/***
This file is part of PulseAudio.
diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in
index 942389d2..66501803 100755
--- a/src/daemon/esdcompat.in
+++ b/src/daemon/esdcompat.in
@@ -1,7 +1,5 @@
#!/bin/sh
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
index f5347db3..b1770674 100644
--- a/src/daemon/ltdl-bind-now.c
+++ b/src/daemon/ltdl-bind-now.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -34,6 +32,11 @@
#include <sys/dl.h>
#endif
+#ifndef HAVE_STRUCT_LT_USER_DLLOADER
+/* Only used with ltdl 2.2 */
+#include <string.h>
+#endif
+
#include <ltdl.h>
#include <pulsecore/macro.h>
@@ -85,7 +88,11 @@ static const char *libtool_get_error(void) {
to set $LT_BIND_NOW before starting the pulsaudio binary.
*/
+#ifndef HAVE_LT_DLADVISE
static lt_module bind_now_open(lt_user_data d, const char *fname) {
+#else
+ static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
+#endif
lt_module m;
pa_assert(fname);
@@ -115,7 +122,7 @@ static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol)
pa_assert(m);
pa_assert(symbol);
-
+
if (!(ptr = dlsym(m, symbol))) {
libtool_set_error(dlerror());
return NULL;
@@ -129,26 +136,54 @@ static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol)
void pa_ltdl_init(void) {
#ifdef PA_BIND_NOW
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
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
};
+# else
+ static const lt_dlvtable *dlopen_loader;
+ static lt_dlvtable bindnow_loader;
+# endif
#endif
-
- pa_assert_se(lt_dlinit() == 0);
+
+ pa_assert_se(lt_dlinit() == 0);
pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE));
+#ifdef HAVE_LT_DLMUTEX_REGISTER
pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0);
+#endif
#ifdef PA_BIND_NOW
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
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.");
+# else
+ /* Already initialised */
+ if ( dlopen_loader != NULL ) return;
+
+ if (!(dlopen_loader = lt_dlloader_find("dlopen"))) {
+ pa_log_warn("Failed to find original dlopen loader.");
+ return;
+ }
+
+ memcpy(&bindnow_loader, dlopen_loader, sizeof(bindnow_loader));
+ bindnow_loader.name = "bind-now-loader";
+ bindnow_loader.module_open = bind_now_open;
+ bindnow_loader.module_close = bind_now_close;
+ bindnow_loader.find_sym = bind_now_find_sym;
+ bindnow_loader.priority = LT_DLLOADER_PREPEND;
+
+ /* Add our BIND_NOW loader as the default module loader. */
+ if (lt_dlloader_add(&bindnow_loader) != 0)
+ pa_log_warn("Failed to add bind-now-loader.");
+# endif
#endif
}
diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h
index e19c7bc1..f95d13b4 100644
--- a/src/daemon/ltdl-bind-now.h
+++ b/src/daemon/ltdl-bind-now.h
@@ -1,8 +1,6 @@
#ifndef foopulsecoreltdlbindnowhfoo
#define foopulsecoreltdlbindnowhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 6c9f6627..14594416 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -94,6 +92,9 @@
#include "dumpmodules.h"
#include "caps.h"
#include "ltdl-bind-now.h"
+#include "polkit.h"
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
#ifdef HAVE_LIBWRAP
/* Only one instance of these variables */
@@ -114,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
MSG msg;
struct timeval tvnext;
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
raise(SIGTERM);
else {
@@ -163,8 +164,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,
}
}
-#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))
-
#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
static int change_user(void) {
@@ -203,6 +202,13 @@ static int change_user(void) {
return -1;
}
+ if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid) < 0) {
+ pa_log("Failed to create '%s': %s", PA_SYSTEM_STATE_PATH, pa_cstrerror(errno));
+ return -1;
+ }
+
+ /* We don't create the config dir here, because we don't need to write to it */
+
if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) {
pa_log("Failed to change group list: %s", pa_cstrerror(errno));
return -1;
@@ -240,13 +246,15 @@ static int change_user(void) {
return -1;
}
- set_env("USER", PA_SYSTEM_USER);
- set_env("LOGNAME", PA_SYSTEM_GROUP);
- set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("USER", PA_SYSTEM_USER);
+ pa_set_env("USERNAME", PA_SYSTEM_USER);
+ pa_set_env("LOGNAME", PA_SYSTEM_USER);
+ pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */
- set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
- set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
+ pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
pa_log_info("Successfully dropped root privileges.");
@@ -262,51 +270,57 @@ static int change_user(void) {
#endif /* HAVE_PWD_H && HAVE_GRP_H */
-static int create_runtime_dir(void) {
- char fn[PATH_MAX];
-
- pa_runtime_path(NULL, fn, sizeof(fn));
-
- /* This function is called only when the daemon is started in
- * per-user mode. We create the runtime directory somewhere in
- * /tmp/ with the current UID/GID */
-
- if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) {
- pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno));
- return -1;
- }
-
- return 0;
-}
-
#ifdef HAVE_SYS_RESOURCE_H
-static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
+static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
struct rlimit rl;
pa_assert(r);
if (!r->is_set)
- return;
+ return 0;
rl.rlim_cur = rl.rlim_max = r->value;
- if (setrlimit(resource, &rl) < 0)
- pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ if (setrlimit(resource, &rl) < 0) {
+ pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ return -1;
+ }
+
+ return 0;
}
static void set_all_rlimits(const pa_daemon_conf *conf) {
- set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
- set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
- set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
- set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+ set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+ set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+ set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
+ set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
#ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
#endif
+ set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#ifdef RLIMIT_LOCKS
+ set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+ set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
+#ifdef RLIMIT_NICE
+ set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
+#endif
+#ifdef RLIMIT_RTPRIO
+ set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+ set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
}
#endif
@@ -317,16 +331,21 @@ int main(int argc, char *argv[]) {
pa_mainloop *mainloop = NULL;
char *s;
int r = 0, retval = 1, d = 0;
- int daemon_pipe[2] = { -1, -1 };
- int suid_root, real_root;
- int valid_pid_file = 0;
+ pa_bool_t suid_root, real_root;
+ pa_bool_t valid_pid_file = FALSE;
gid_t gid = (gid_t) -1;
-
+ pa_bool_t ltdl_init = FALSE;
+ int passed_fd = -1;
+ const char *e;
+#ifdef HAVE_FORK
+ int daemon_pipe[2] = { -1, -1 };
+#endif
#ifdef OS_IS_WIN32
- pa_time_event *timer;
- struct timeval tv;
+ pa_time_event *win32_timer;
+ struct timeval win32_tv;
#endif
-
+ char *lf = NULL;
+ int autospawn_lock_fd = -1;
#if defined(__linux__) && defined(__OPTIMIZE__)
/*
@@ -336,11 +355,14 @@ int main(int argc, char *argv[]) {
*/
if (!getenv("LD_BIND_NOW")) {
- putenv(pa_xstrdup("LD_BIND_NOW=1"));
+ char *rp;
/* 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);
+
+ pa_set_env("LD_BIND_NOW", "1");
+ pa_assert_se(rp = pa_readlink("/proc/self/exe"));
+ pa_assert_se(execv(rp, argv) == 0);
}
#endif
@@ -348,11 +370,11 @@ int main(int argc, char *argv[]) {
real_root = getuid() == 0;
suid_root = !real_root && geteuid() == 0;
#else
- real_root = 0;
- suid_root = 0;
+ real_root = FALSE;
+ suid_root = FALSE;
#endif
- if (suid_root) {
+ if (!real_root) {
/* Drop all capabilities except CAP_SYS_NICE */
pa_limit_caps();
@@ -368,29 +390,24 @@ int main(int argc, char *argv[]) {
* is just too risky tun let PA run as root all the time. */
}
- setlocale(LC_ALL, "");
+ if ((e = getenv("PULSE_PASSED_FD"))) {
+ passed_fd = atoi(e);
- 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;
+ if (passed_fd <= 2)
+ passed_fd = -1;
}
- LTDL_SET_PRELOADED_SYMBOLS();
+ pa_close_all(passed_fd, -1);
- pa_ltdl_init();
+ pa_reset_sigs(-1);
+ pa_unblock_sigs(-1);
-#ifdef OS_IS_WIN32
- {
- WSADATA data;
- WSAStartup(MAKEWORD(2, 0), &data);
- }
-#endif
-
- pa_random_seed();
+ /* At this point, we are a normal user, possibly with CAP_NICE if
+ * we were started SUID. If we are started as normal root, than we
+ * still are normal root. */
+ setlocale(LC_ALL, "");
+ pa_log_set_maximal_level(PA_LOG_INFO);
pa_log_set_ident("pulseaudio");
conf = pa_daemon_conf_new();
@@ -402,24 +419,140 @@ int main(int argc, char *argv[]) {
goto finish;
if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
- pa_log("failed to parse command line.");
+ pa_log("Failed to parse command line.");
goto finish;
}
pa_log_set_maximal_level(conf->log_level);
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
- if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
- pa_raise_priority();
+ pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
- if (suid_root && (conf->cmd != PA_CMD_DAEMON || !conf->high_priority)) {
- pa_drop_caps();
- pa_drop_root();
+ if (!real_root && pa_have_caps()) {
+ pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
+
+ /* Let's better not enable high prio or RT by default */
+
+ if (conf->high_priority && !allow_high_priority) {
+ if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+ pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing high-priority scheduling.");
+ allow_high_priority = TRUE;
+ }
+ }
+
+ if (conf->realtime_scheduling && !allow_realtime) {
+ if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+ pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time scheduling.");
+ allow_realtime = TRUE;
+ }
+ }
+
+#ifdef HAVE_POLKIT
+ if (conf->high_priority && !allow_high_priority) {
+ if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
+ pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
+ allow_high_priority = TRUE;
+ } else
+ pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
+ }
+
+ if (conf->realtime_scheduling && !allow_realtime) {
+ if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
+ pa_log_info("PolicyKit grants us acquire-real-time privilege.");
+ allow_realtime = TRUE;
+ } else
+ pa_log_info("PolicyKit refuses acquire-real-time privilege.");
+ }
+#endif
+
+ if (!allow_high_priority && !allow_realtime) {
+
+ /* OK, there's no further need to keep CAP_NICE. Hence
+ * let's give it up early */
+
+ pa_drop_caps();
+
+ if (conf->high_priority || conf->realtime_scheduling)
+ pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
+ "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+ "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
+ }
}
+#ifdef HAVE_SYS_RESOURCE_H
+ /* Reset resource limits. If we are run as root (for system mode)
+ * this might end up increasing the limits, which is intended
+ * behaviour. For all other cases, i.e. started as normal user, or
+ * SUID root at this point we should have no CAP_SYS_RESOURCE and
+ * increasing the limits thus should fail. Which is, too, intended
+ * behaviour */
+
+ set_all_rlimits(conf);
+#endif
+
+ if (conf->high_priority && !pa_can_high_priority())
+ pa_log_warn("High-priority scheduling enabled in configuration but not allowed by policy.");
+
+ if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
+ pa_raise_priority(conf->nice_level);
+
+ if (pa_have_caps()) {
+ pa_bool_t drop;
+
+ drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling;
+
+#ifdef RLIMIT_RTPRIO
+ if (!drop) {
+ struct rlimit rl;
+ /* At this point we still have CAP_NICE if we were loaded
+ * SUID root. If possible let's acquire RLIMIT_RTPRIO
+ * instead and give CAP_NICE up. */
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+
+ if (rl.rlim_cur >= 9)
+ drop = TRUE;
+ else {
+ rl.rlim_max = rl.rlim_cur = 9;
+
+ if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+ pa_log_info("Successfully increased RLIMIT_RTPRIO");
+ drop = TRUE;
+ } else
+ pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+ }
+ }
+ }
+#endif
+
+ if (drop) {
+ pa_log_info("Giving up CAP_NICE");
+ pa_drop_caps();
+ suid_root = FALSE;
+ }
+ }
+
+ if (conf->realtime_scheduling && !pa_can_realtime())
+ pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy.");
+
+ pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
+
+ LTDL_SET_PRELOADED_SYMBOLS();
+ pa_ltdl_init();
+ ltdl_init = TRUE;
+
if (conf->dl_search_path)
lt_dlsetsearchpath(conf->dl_search_path);
+#ifdef OS_IS_WIN32
+ {
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 0), &data);
+ }
+#endif
+
+ pa_random_seed();
+
switch (conf->cmd) {
case PA_CMD_DUMP_MODULES:
pa_dump_modules(conf, argc-d, argv+d);
@@ -457,10 +590,10 @@ int main(int argc, char *argv[]) {
case PA_CMD_CHECK: {
pid_t pid;
- if (pa_pid_file_check_running(&pid) < 0) {
- pa_log_info("daemon not running");
- } else {
- pa_log_info("daemon running as PID %u", pid);
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+ pa_log_info("Daemon not running");
+ else {
+ pa_log_info("Daemon running as PID %u", pid);
retval = 0;
}
@@ -469,8 +602,8 @@ int main(int argc, char *argv[]) {
}
case PA_CMD_KILL:
- if (pa_pid_file_kill(SIGINT, NULL) < 0)
- pa_log("failed to kill daemon.");
+ if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
+ pa_log("Failed to kill daemon.");
else
retval = 0;
@@ -484,28 +617,37 @@ int main(int argc, char *argv[]) {
goto finish;
default:
- pa_assert(conf->cmd == PA_CMD_DAEMON);
+ pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
}
- if (real_root && !conf->system_instance) {
+ if (real_root && !conf->system_instance)
pa_log_warn("This program is not intended to be run as root (unless --system is specified).");
- } else if (!real_root && conf->system_instance) {
+ else if (!real_root && conf->system_instance) {
pa_log("Root priviliges required.");
goto finish;
}
+ if (conf->cmd == PA_CMD_START) {
+ /* If we shall start PA only when it is not running yet, we
+ * first take the autospawn lock to make things
+ * synchronous. */
+
+ lf = pa_runtime_path(AUTOSPAWN_LOCK);
+ autospawn_lock_fd = pa_lock_lockfile(lf);
+ }
+
if (conf->daemonize) {
pid_t child;
int tty_fd;
if (pa_stdio_acquire() < 0) {
- pa_log("failed to acquire stdio.");
+ pa_log("Failed to acquire stdio.");
goto finish;
}
#ifdef HAVE_FORK
if (pipe(daemon_pipe) < 0) {
- pa_log("failed to create pipe.");
+ pa_log("pipe failed: %s", pa_cstrerror(errno));
goto finish;
}
@@ -515,24 +657,36 @@ int main(int argc, char *argv[]) {
}
if (child != 0) {
+ ssize_t n;
/* Father */
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)) {
- pa_log("read() failed: %s", pa_cstrerror(errno));
+ if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+ if (n < 0)
+ pa_log("read() failed: %s", pa_cstrerror(errno));
+
retval = 1;
}
if (retval)
- pa_log("daemon startup failed.");
+ pa_log("Daemon startup failed.");
else
- pa_log_info("daemon startup successful.");
+ pa_log_info("Daemon startup successful.");
goto finish;
}
+ if (autospawn_lock_fd >= 0) {
+ /* The lock file is unlocked from the parent, so we need
+ * to close it in the child */
+
+ pa_close(autospawn_lock_fd);
+ autospawn_lock_fd = -1;
+ }
+
pa_assert_se(pa_close(daemon_pipe[0]) == 0);
daemon_pipe[0] = -1;
#endif
@@ -552,9 +706,9 @@ int main(int argc, char *argv[]) {
pa_close(1);
pa_close(2);
- open("/dev/null", O_RDONLY);
- open("/dev/null", O_WRONLY);
- open("/dev/null", O_WRONLY);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 1);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
#else
FreeConsole();
#endif
@@ -577,38 +731,54 @@ int main(int argc, char *argv[]) {
#endif
}
+ pa_set_env("PULSE_INTERNAL", "1");
pa_assert_se(chdir("/") == 0);
umask(0022);
- if (conf->system_instance) {
+ if (conf->system_instance)
if (change_user() < 0)
goto finish;
- } else if (create_runtime_dir() < 0)
+
+ pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+
+ pa_log_info("This is PulseAudio " PACKAGE_VERSION);
+ pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+ if (!(s = pa_get_runtime_dir()))
goto finish;
+ pa_log_info("Using runtime directory %s.", s);
+ pa_xfree(s);
+ if (!(s = pa_get_state_dir()))
+ pa_log_info("Using state directory %s.", s);
+ pa_xfree(s);
+
+ pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode()));
if (conf->use_pid_file) {
- if (pa_pid_file_create() < 0) {
+ int z;
+
+ if ((z = pa_pid_file_create("pulseaudio")) != 0) {
+
+ if (conf->cmd == PA_CMD_START && z > 0) {
+ /* If we are already running and with are run in
+ * --start mode, then let's return this as success. */
+
+ pa_log_info("z=%i rock!", z);
+
+ retval = 0;
+ goto finish;
+ }
+
pa_log("pa_pid_file_create() failed.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
goto finish;
}
- valid_pid_file = 1;
+ valid_pid_file = TRUE;
}
-#ifdef HAVE_SYS_RESOURCE_H
- set_all_rlimits(conf);
-#endif
-
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
- 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
@@ -626,21 +796,21 @@ int main(int argc, char *argv[]) {
goto finish;
}
- c->is_system_instance = !!conf->system_instance;
- 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;
+ c->realtime_priority = conf->realtime_priority;
+ c->realtime_scheduling = !!conf->realtime_scheduling;
+ c->disable_remixing = !!conf->disable_remixing;
+ c->running_as_daemon = !!conf->daemonize;
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);
-
#ifdef SIGUSR1
pa_signal_new(SIGUSR1, signal_callback, c);
#endif
@@ -652,72 +822,95 @@ int main(int argc, char *argv[]) {
#endif
#ifdef OS_IS_WIN32
- pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));
+ win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
- if (conf->daemonize)
- c->running_as_daemon = 1;
-
oil_init();
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)
- r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
+ if (conf->load_default_script_file) {
+ FILE *f;
+
+ if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+ r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+ fclose(f);
+ }
+ }
if (r >= 0)
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
pa_xfree(s);
+ /* We completed the initial module loading, so let's disable it
+ * from now on, if requested */
+ c->disallow_module_loading = !!conf->disallow_module_loading;
+
if (r < 0 && conf->fail) {
- pa_log("failed to initialize daemon.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else if (!c->modules || pa_idxset_size(c->modules) == 0) {
- pa_log("daemon startup without any loaded modules, refusing to work.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else {
+ pa_log("Failed to initialize daemon.");
+ goto finish;
+ }
+
+ if (!c->modules || pa_idxset_size(c->modules) == 0) {
+ pa_log("Daemon startup without any loaded modules, refusing to work.");
+ goto finish;
+ }
+
+ if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+ pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
+ goto finish;
+ }
- retval = 0;
#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+ if (daemon_pipe[1] >= 0) {
+ int ok = 0;
+ pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
+ pa_close(daemon_pipe[1]);
+ daemon_pipe[1] = -1;
+ }
#endif
- 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);
- retval = 1;
- } else {
- pa_log_info("Daemon startup complete.");
- if (pa_mainloop_run(mainloop, &retval) < 0)
- retval = 1;
- pa_log_info("Daemon shutdown initiated.");
- }
- }
+ pa_log_info("Daemon startup complete.");
+
+ retval = 0;
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ goto finish;
+
+ pa_log_info("Daemon shutdown initiated.");
+
+finish:
+
+ if (autospawn_lock_fd >= 0)
+ pa_unlock_lockfile(lf, autospawn_lock_fd);
+
+ if (lf)
+ pa_xfree(lf);
#ifdef OS_IS_WIN32
- pa_mainloop_get_api(mainloop)->time_free(timer);
+ if (win32_timer)
+ pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif
- pa_core_unref(c);
+ if (c) {
+ pa_core_unref(c);
+ pa_log_info("Daemon terminated.");
+ }
if (!conf->no_cpu_limit)
pa_cpu_limit_done();
pa_signal_done();
- pa_log_info("Daemon terminated.");
+#ifdef HAVE_FORK
+ if (daemon_pipe[1] >= 0)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-finish:
+ pa_close_pipe(daemon_pipe);
+#endif
if (mainloop)
pa_mainloop_free(mainloop);
@@ -728,13 +921,12 @@ finish:
if (valid_pid_file)
pa_pid_file_remove();
- pa_close_pipe(daemon_pipe);
-
#ifdef OS_IS_WIN32
WSACleanup();
#endif
- pa_ltdl_done();
+ if (ltdl_init)
+ pa_ltdl_done();
#ifdef HAVE_DBUS
dbus_shutdown();
diff --git a/src/daemon/org.pulseaudio.policy b/src/daemon/org.pulseaudio.policy
new file mode 100644
index 00000000..6cdeec68
--- /dev/null
+++ b/src/daemon/org.pulseaudio.policy
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?><!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<!--
+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.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.
+-->
+
+<policyconfig>
+ <vendor>The PulseAudio Project</vendor>
+ <vendor_url>http://pulseaudio.org/</vendor_url>
+ <icon_name>audio-card</icon_name>
+
+ <action id="org.pulseaudio.acquire-real-time">
+ <description>Real-time scheduling for the PulseAudio daemon</description>
+ <message>System policy prevents PulseAudio from acquiring real-time scheduling.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>no</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.pulseaudio.acquire-high-priority">
+ <description>High-priority scheduling (negative Unix nice level) for the PulseAudio daemon</description>
+ <message>System policy prevents PulseAudio from acquiring high-priority scheduling.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>no</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c
new file mode 100644
index 00000000..256e3199
--- /dev/null
+++ b/src/daemon/polkit.c
@@ -0,0 +1,165 @@
+/***
+ 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 <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <dbus/dbus.h>
+#include <polkit-dbus/polkit-dbus.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "polkit.h"
+
+int pa_polkit_check(const char *action_id) {
+ int ret = -1;
+ DBusError dbus_error;
+ DBusConnection *bus = NULL;
+ PolKitCaller *caller = NULL;
+ PolKitAction *action = NULL;
+ PolKitContext *context = NULL;
+ PolKitError *polkit_error = NULL;
+ PolKitSession *session = NULL;
+ PolKitResult polkit_result;
+
+ dbus_error_init(&dbus_error);
+
+ if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error))) {
+ pa_log_error("Cannot connect to system bus: %s", dbus_error.message);
+ goto finish;
+ }
+
+ if (!(caller = polkit_caller_new_from_pid(bus, getpid(), &dbus_error))) {
+ pa_log_error("Cannot get caller from PID: %s", dbus_error.message);
+ goto finish;
+ }
+
+ /* This function is called when PulseAudio is called SUID root. We
+ * want to authenticate the real user that called us and not the
+ * effective user we gained through being SUID root. Hence we
+ * overwrite the UID caller data here explicitly, just for
+ * paranoia. In fact PolicyKit should fill in the UID here anyway
+ * -- an not the EUID or any other user id. */
+
+ if (!(polkit_caller_set_uid(caller, getuid()))) {
+ pa_log_error("Cannot set UID on caller object.");
+ goto finish;
+ }
+
+ if (!(polkit_caller_get_ck_session(caller, &session))) {
+ pa_log_error("Failed to get CK session.");
+ goto finish;
+ }
+
+ /* We need to overwrite the UID in both the caller and the session
+ * object */
+
+ if (!(polkit_session_set_uid(session, getuid()))) {
+ pa_log_error("Cannot set UID on session object.");
+ goto finish;
+ }
+
+ if (!(action = polkit_action_new())) {
+ pa_log_error("Cannot allocate PolKitAction.");
+ goto finish;
+ }
+
+ if (!polkit_action_set_action_id(action, action_id)) {
+ pa_log_error("Cannot set action_id");
+ goto finish;
+ }
+
+ if (!(context = polkit_context_new())) {
+ pa_log_error("Cannot allocate PolKitContext.");
+ goto finish;
+ }
+
+ if (!polkit_context_init(context, &polkit_error)) {
+ pa_log_error("Cannot initialize PolKitContext: %s", polkit_error_get_error_message(polkit_error));
+ goto finish;
+ }
+
+ for (;;) {
+
+ polkit_result = polkit_context_is_caller_authorized(context, action, caller, TRUE, &polkit_error);
+
+ if (polkit_error_is_set(polkit_error)) {
+ pa_log_error("Could not determine whether caller is authorized: %s", polkit_error_get_error_message(polkit_error));
+ goto finish;
+ }
+
+ if (polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT
+ ) {
+
+ if (polkit_auth_obtain(action_id, 0, getpid(), &dbus_error)) {
+ polkit_result = POLKIT_RESULT_YES;
+ break;
+ }
+
+ if (dbus_error_is_set(&dbus_error)) {
+ pa_log_error("Cannot obtain auth: %s", dbus_error.message);
+ goto finish;
+ }
+ }
+
+ break;
+ }
+
+ if (polkit_result != POLKIT_RESULT_YES && polkit_result != POLKIT_RESULT_NO)
+ pa_log_warn("PolicyKit responded with '%s'", polkit_result_to_string_representation(polkit_result));
+
+ ret = polkit_result == POLKIT_RESULT_YES;
+
+finish:
+
+ if (caller)
+ polkit_caller_unref(caller);
+
+ if (action)
+ polkit_action_unref(action);
+
+ if (context)
+ polkit_context_unref(context);
+
+ if (bus)
+ dbus_connection_unref(bus);
+
+ dbus_error_free(&dbus_error);
+
+ if (polkit_error)
+ polkit_error_free(polkit_error);
+
+ return ret;
+}
diff --git a/src/pulsecore/core-def.h b/src/daemon/polkit.h
index 4bc05137..0d65ec52 100644
--- a/src/pulsecore/core-def.h
+++ b/src/daemon/polkit.h
@@ -1,12 +1,10 @@
-#ifndef foocoredefhfoo
-#define foocoredefhfoo
-
-/* $Id$ */
+#ifndef foopolkithfoo
+#define foopolkithfoo
/***
This file is part of PulseAudio.
- Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ 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
@@ -24,6 +22,6 @@
USA.
***/
-/* FIXME: Remove this shit */
+int pa_polkit_check(const char *action);
#endif
diff --git a/src/depmod.py b/src/depmod.py
index a20bc7c0..6cb3cb21 100755
--- a/src/depmod.py
+++ b/src/depmod.py
@@ -1,6 +1,5 @@
#!/usr/bin/python
-# $Id$
-#
+
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/map-file b/src/map-file
new file mode 100644
index 00000000..8d1c582e
--- /dev/null
+++ b/src/map-file
@@ -0,0 +1,256 @@
+PULSE_0 {
+global:
+pa_browser_new;
+pa_browser_new_full;
+pa_browser_ref;
+pa_browser_set_callback;
+pa_browser_set_error_callback;
+pa_browser_unref;
+pa_bytes_per_second;
+pa_bytes_snprint;
+pa_bytes_to_usec;
+pa_channel_map_equal;
+pa_channel_map_init;
+pa_channel_map_init_auto;
+pa_channel_map_init_extend;
+pa_channel_map_init_mono;
+pa_channel_map_init_stereo;
+pa_channel_map_parse;
+pa_channel_map_snprint;
+pa_channel_map_valid;
+pa_channel_position_to_pretty_string;
+pa_channel_position_to_string;
+pa_context_add_autoload;
+pa_context_connect;
+pa_context_disconnect;
+pa_context_drain;
+pa_context_errno;
+pa_context_exit_daemon;
+pa_context_get_autoload_info_by_index;
+pa_context_get_autoload_info_by_name;
+pa_context_get_autoload_info_list;
+pa_context_get_client_info;
+pa_context_get_client_info_list;
+pa_context_get_index;
+pa_context_get_module_info;
+pa_context_get_module_info_list;
+pa_context_get_protocol_version;
+pa_context_get_sample_info_by_index;
+pa_context_get_sample_info_by_name;
+pa_context_get_sample_info_list;
+pa_context_get_server;
+pa_context_get_server_info;
+pa_context_get_server_protocol_version;
+pa_context_get_sink_info_by_index;
+pa_context_get_sink_info_by_name;
+pa_context_get_sink_info_list;
+pa_context_get_sink_input_info;
+pa_context_get_sink_input_info_list;
+pa_context_get_source_info_by_index;
+pa_context_get_source_info_by_name;
+pa_context_get_source_info_list;
+pa_context_get_source_output_info;
+pa_context_get_source_output_info_list;
+pa_context_get_state;
+pa_context_is_local;
+pa_context_is_pending;
+pa_context_kill_client;
+pa_context_kill_sink_input;
+pa_context_kill_source_output;
+pa_context_load_module;
+pa_context_move_sink_input_by_index;
+pa_context_move_sink_input_by_name;
+pa_context_move_source_output_by_index;
+pa_context_move_source_output_by_name;
+pa_context_new;
+pa_context_new_with_proplist;
+pa_context_play_sample;
+pa_context_play_sample_with_proplist;
+pa_context_proplist_remove;
+pa_context_proplist_update;
+pa_context_ref;
+pa_context_remove_autoload_by_index;
+pa_context_remove_autoload_by_name;
+pa_context_remove_sample;
+pa_context_set_default_sink;
+pa_context_set_default_source;
+pa_context_set_name;
+pa_context_set_sink_input_mute;
+pa_context_set_sink_input_volume;
+pa_context_set_sink_mute_by_index;
+pa_context_set_sink_mute_by_name;
+pa_context_set_sink_volume_by_index;
+pa_context_set_sink_volume_by_name;
+pa_context_set_source_mute_by_index;
+pa_context_set_source_mute_by_name;
+pa_context_set_source_volume_by_index;
+pa_context_set_source_volume_by_name;
+pa_context_set_state_callback;
+pa_context_set_subscribe_callback;
+pa_context_stat;
+pa_context_subscribe;
+pa_context_suspend_sink_by_index;
+pa_context_suspend_sink_by_name;
+pa_context_suspend_source_by_index;
+pa_context_suspend_source_by_name;
+pa_context_unload_module;
+pa_context_unref;
+pa_cvolume_avg;
+pa_cvolume_channels_equal_to;
+pa_cvolume_equal;
+pa_cvolume_set;
+pa_cvolume_snprint;
+pa_cvolume_valid;
+pa_frame_size;
+pa_get_binary_name;
+pa_get_fqdn;
+pa_get_home_dir;
+pa_get_host_name;
+pa_get_library_version;
+pa_gettimeofday;
+pa_get_user_name;
+pa_glib_mainloop_free;
+pa_glib_mainloop_get_api;
+pa_glib_mainloop_new;
+pa_locale_to_utf8;
+pa_mainloop_api_once;
+pa_mainloop_dispatch;
+pa_mainloop_free;
+pa_mainloop_get_api;
+pa_mainloop_get_retval;
+pa_mainloop_iterate;
+pa_mainloop_new;
+pa_mainloop_poll;
+pa_mainloop_prepare;
+pa_mainloop_quit;
+pa_mainloop_run;
+pa_mainloop_set_poll_func;
+pa_mainloop_wakeup;
+pa_msleep;
+pa_operation_cancel;
+pa_operation_get_state;
+pa_operation_ref;
+pa_operation_unref;
+pa_parse_sample_format;
+pa_path_get_filename;
+pa_proplist_free;
+pa_proplist_contains;
+pa_proplist_clear;
+pa_proplist_copy;
+pa_proplist_get;
+pa_proplist_gets;
+pa_proplist_iterate;
+pa_proplist_update;
+pa_proplist_new;
+pa_proplist_set;
+pa_proplist_sets;
+pa_proplist_setf;
+pa_proplist_unset;
+pa_proplist_unset_many;
+pa_proplist_to_string;
+pa_sample_format_to_string;
+pa_sample_size;
+pa_sample_spec_equal;
+pa_sample_spec_snprint;
+pa_sample_spec_valid;
+pa_signal_done;
+pa_signal_free;
+pa_signal_init;
+pa_signal_new;
+pa_signal_set_destroy;
+pa_simple_drain;
+pa_simple_flush;
+pa_simple_free;
+pa_simple_get_latency;
+pa_simple_new;
+pa_simple_read;
+pa_simple_write;
+pa_stream_connect_playback;
+pa_stream_connect_record;
+pa_stream_connect_upload;
+pa_stream_cork;
+pa_stream_disconnect;
+pa_stream_drain;
+pa_stream_drop;
+pa_stream_finish_upload;
+pa_stream_flush;
+pa_stream_get_buffer_attr;
+pa_stream_get_channel_map;
+pa_stream_get_context;
+pa_stream_get_device_index;
+pa_stream_get_device_name;
+pa_stream_get_index;
+pa_stream_get_latency;
+pa_stream_get_monitor_stream;
+pa_stream_get_sample_spec;
+pa_stream_get_state;
+pa_stream_get_time;
+pa_stream_get_timing_info;
+pa_stream_is_corked;
+pa_stream_is_suspended;
+pa_stream_new;
+pa_stream_new_with_proplist;
+pa_stream_peek;
+pa_stream_prebuf;
+pa_stream_proplist_remove;
+pa_stream_proplist_update;
+pa_stream_readable_size;
+pa_stream_ref;
+pa_stream_set_buffer_attr;
+pa_stream_set_latency_update_callback;
+pa_stream_set_moved_callback;
+pa_stream_set_monitor_stream;
+pa_stream_set_name;
+pa_stream_set_overflow_callback;
+pa_stream_set_read_callback;
+pa_stream_set_started_callback;
+pa_stream_set_state_callback;
+pa_stream_set_suspended_callback;
+pa_stream_set_underflow_callback;
+pa_stream_set_write_callback;
+pa_stream_trigger;
+pa_stream_unref;
+pa_stream_update_sample_rate;
+pa_stream_update_timing_info;
+pa_stream_writable_size;
+pa_stream_write;
+pa_strerror;
+pa_sw_cvolume_multiply;
+pa_sw_volume_from_dB;
+pa_sw_volume_from_linear;
+pa_sw_volume_multiply;
+pa_sw_volume_to_dB;
+pa_sw_volume_to_linear;
+pa_threaded_mainloop_accept;
+pa_threaded_mainloop_free;
+pa_threaded_mainloop_get_api;
+pa_threaded_mainloop_get_retval;
+pa_threaded_mainloop_in_thread;
+pa_threaded_mainloop_lock;
+pa_threaded_mainloop_new;
+pa_threaded_mainloop_signal;
+pa_threaded_mainloop_start;
+pa_threaded_mainloop_stop;
+pa_threaded_mainloop_unlock;
+pa_threaded_mainloop_wait;
+pa_timeval_add;
+pa_timeval_age;
+pa_timeval_cmp;
+pa_timeval_diff;
+pa_timeval_load;
+pa_timeval_store;
+pa_timeval_sub;
+pa_usec_to_bytes;
+pa_utf8_filter;
+pa_utf8_to_locale;
+pa_utf8_valid;
+pa_xfree;
+pa_xmalloc;
+pa_xmalloc0;
+pa_xmemdup;
+pa_xrealloc;
+pa_xstrdup;
+pa_xstrndup;
+local:
+*;
+};
diff --git a/src/modules/.gitignore b/src/modules/.gitignore
new file mode 100644
index 00000000..2d2d942d
--- /dev/null
+++ b/src/modules/.gitignore
@@ -0,0 +1 @@
+module-*-symdef.h
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index 906de58d..5d52cbc9 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,6 +25,7 @@
#endif
#include <sys/types.h>
+#include <limits.h>
#include <asoundlib.h>
#include <pulse/sample.h>
@@ -34,6 +33,8 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
#include "alsa-util.h"
@@ -227,15 +228,19 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
[PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
+ [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
+ [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
};
static const pa_sample_format_t try_order[] = {
- PA_SAMPLE_S16NE,
- PA_SAMPLE_S16RE,
PA_SAMPLE_FLOAT32NE,
PA_SAMPLE_FLOAT32RE,
- PA_SAMPLE_ULAW,
+ PA_SAMPLE_S32NE,
+ PA_SAMPLE_S32RE,
+ PA_SAMPLE_S16NE,
+ PA_SAMPLE_S16RE,
PA_SAMPLE_ALAW,
+ PA_SAMPLE_ULAW,
PA_SAMPLE_U8,
PA_SAMPLE_INVALID
};
@@ -256,6 +261,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
*f = PA_SAMPLE_S16LE;
else if (*f == PA_SAMPLE_S16LE)
*f = PA_SAMPLE_S16BE;
+ else if (*f == PA_SAMPLE_S32BE)
+ *f = PA_SAMPLE_S32LE;
+ else if (*f == PA_SAMPLE_S32LE)
+ *f = PA_SAMPLE_S32BE;
else
goto try_auto;
@@ -276,13 +285,27 @@ 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 *use_mmap) {
+int pa_alsa_set_hw_params(
+ snd_pcm_t *pcm_handle,
+ pa_sample_spec *ss,
+ uint32_t *periods,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number) {
+
int ret = -1;
+ snd_pcm_uframes_t _period_size = *period_size;
+ unsigned int _periods = *periods;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
+ pa_bool_t _use_mmap = use_mmap && *use_mmap;
+ pa_bool_t _use_tsched = use_tsched && *use_tsched;
+ int dir;
pa_assert(pcm_handle);
pa_assert(ss);
@@ -291,13 +314,13 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
snd_pcm_hw_params_alloca(&hwparams);
- buffer_size = *periods * *period_size;
+ if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
+ goto finish;
- if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
- (ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
+ if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
goto finish;
- if (use_mmap && *use_mmap) {
+ if (_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 */
@@ -305,58 +328,95 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
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;
+ _use_mmap = FALSE;
}
} else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
+ if (!_use_mmap)
+ _use_tsched = FALSE;
+
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
goto finish;
if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
- goto finish;
+ /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
+ _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
+ tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
+
+ if (require_exact_channel_number) {
+ if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
+ goto finish;
+ } else {
+ if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+ goto finish;
+ }
+
+ if (_use_tsched) {
+ _period_size = tsched_size;
+ _periods = 1;
+
+ pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
+ pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
+ }
+
+ buffer_size = _periods * _period_size;
- if ((*period_size > 0 && (ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL)) < 0) ||
- (*periods > 0 && (ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0))
+ if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
goto finish;
+ if (_periods > 0) {
+ dir = 1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+ dir = -1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
+ goto finish;
+ }
+ }
+
+ if (_period_size > 0)
+ if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+ goto finish;
+
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
goto finish;
- if (ss->rate != r) {
+ if (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)
- ss->rate = r;
- }
-
- if (ss->channels != c) {
+ if (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) {
+ if (ss->format != 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;
- }
if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0 ||
- (ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
+ if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
+ (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0)
goto finish;
- pa_assert(buffer_size > 0);
- pa_assert(*period_size > 0);
- *periods = buffer_size / *period_size;
- pa_assert(*periods > 0);
+ /* If the sample rate deviates too much, we need to resample */
+ if (r < ss->rate*.95 || r > ss->rate*1.05)
+ ss->rate = r;
+ ss->channels = c;
+ ss->format = f;
+
+ pa_assert(_periods > 0);
+ pa_assert(_period_size > 0);
+
+ *periods = _periods;
+ *period_size = _period_size;
+
+ if (use_mmap)
+ *use_mmap = _use_mmap;
+
+ if (use_tsched)
+ *use_tsched = _use_tsched;
ret = 0;
@@ -365,7 +425,7 @@ finish:
return ret;
}
-int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
+int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
snd_pcm_sw_params_t *swparams;
int err;
@@ -388,6 +448,11 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
return err;
}
+ if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
+ pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", 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;
@@ -396,6 +461,220 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
return 0;
}
+struct device_info {
+ pa_channel_map map;
+ const char *name;
+};
+
+static const struct device_info device_table[] = {
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+
+ {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+
+ {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_LFE }}, "surround41" },
+
+ {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+
+ {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+
+ {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+
+ {{ 0, { 0 }}, NULL }
+};
+
+static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+ pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
+ unsigned i;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ memset(in_a, 0, sizeof(in_a));
+
+ for (i = 0; i < a->channels; i++)
+ in_a[a->map[i]] = TRUE;
+
+ for (i = 0; i < b->channels; i++)
+ if (!in_a[b->map[i]])
+ return FALSE;
+
+ return TRUE;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched) {
+
+ int i;
+ int direction = 1;
+ int err;
+ char *d;
+ snd_pcm_t *pcm_handle;
+
+ pa_assert(dev_id);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+
+ /* First we try to find a device string with a superset of the
+ * requested channel map and open it without the plug: prefix. We
+ * iterate through our device table from top to bottom and take
+ * the first that matches. If we didn't find a working device that
+ * way, we iterate backwards, and check all devices that do not
+ * provide a superset of the requested channel map.*/
+
+ for (i = 0;; i += direction) {
+ pa_sample_spec try_ss;
+
+ if (i < 0) {
+ pa_assert(direction == -1);
+
+ /* OK, so we iterated backwards, and now are at the
+ * beginning of our list. */
+
+ break;
+
+ } else if (!device_table[i].name) {
+ pa_assert(direction == 1);
+
+ /* OK, so we are at the end of our list. at iterated
+ * forwards. */
+
+ i--;
+ direction = -1;
+ }
+
+ if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
+ continue;
+
+ d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ pa_log_debug("Trying %s...", d);
+
+ if ((err = snd_pcm_open(&pcm_handle, d, mode,
+ SND_PCM_NONBLOCK|
+ SND_PCM_NO_AUTO_RESAMPLE|
+ SND_PCM_NO_AUTO_CHANNELS|
+ SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ continue;
+ }
+
+ try_ss.channels = device_table[i].map.channels;
+ try_ss.rate = ss->rate;
+ try_ss.format = ss->format;
+
+ if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
+ *ss = try_ss;
+ *map = device_table[i].map;
+ pa_assert(map->channels == ss->channels);
+ *dev = d;
+ return pcm_handle;
+ }
+
+ /* OK, we didn't find any good device, so let's try the raw plughw: stuff */
+
+ d = pa_sprintf_malloc("plughw:%s", dev_id);
+ pa_log_debug("Trying %s as last resort...", d);
+ pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
+ pa_xfree(d);
+
+ return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+ const char *device,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched) {
+
+ int err;
+ char *d;
+ snd_pcm_t *pcm_handle;
+
+ pa_assert(device);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+
+ d = pa_xstrdup(device);
+
+ for (;;) {
+
+ if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK|
+ SND_PCM_NO_AUTO_RESAMPLE|
+ SND_PCM_NO_AUTO_CHANNELS|
+ SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ return NULL;
+ }
+
+ if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+
+ if (err == -EPERM) {
+ /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
+
+ if (pa_startswith(d, "hw:")) {
+ char *t = pa_sprintf_malloc("plughw:%s", d+3);
+ pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", d, t);
+ pa_xfree(d);
+ d = t;
+
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
+ pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ snd_pcm_close(pcm_handle);
+ return NULL;
+ }
+ }
+
+ *dev = d;
+
+ if (ss->channels != map->channels)
+ pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+
+ return pcm_handle;
+ }
+}
+
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
int err;
@@ -403,7 +682,7 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
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));
+ pa_log_info("Unable to attach to mixer %s: %s", dev, snd_strerror(err));
return -1;
}
@@ -417,6 +696,8 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
return -1;
}
+ pa_log_info("Successfully attached to mixer '%s'", dev);
+
return 0;
}
@@ -432,7 +713,7 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const
snd_mixer_selem_id_set_name(sid, name);
if (!(elem = snd_mixer_find_selem(mixer, sid))) {
- pa_log_warn("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
if (fallback) {
snd_mixer_selem_id_set_name(sid, fallback);
@@ -447,3 +728,391 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const
return elem;
}
+
+static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
+ [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
+
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
+ [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
+
+ [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
+
+ [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
+};
+
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback) {
+ unsigned i;
+ pa_bool_t alsa_channel_used[SND_MIXER_SCHN_LAST];
+ pa_bool_t mono_used = FALSE;
+
+ pa_assert(elem);
+ pa_assert(channel_map);
+ pa_assert(mixer_map);
+
+ memset(&alsa_channel_used, 0, sizeof(alsa_channel_used));
+
+ if (channel_map->channels > 1 &&
+ ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) ||
+ (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) {
+ pa_log_info("ALSA device lacks independant volume controls for each channel, falling back to software volume control.");
+ return -1;
+ }
+
+ for (i = 0; i < channel_map->channels; i++) {
+ snd_mixer_selem_channel_id_t id;
+ pa_bool_t is_mono;
+
+ is_mono = channel_map->map[i] == PA_CHANNEL_POSITION_MONO;
+ id = alsa_channel_ids[channel_map->map[i]];
+
+ if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) {
+ pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer. Falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) {
+ pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) ||
+ (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) {
+
+ pa_log_info("ALSA device lacks separate volumes control for channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if (is_mono) {
+ mixer_map[i] = SND_MIXER_SCHN_MONO;
+ mono_used = TRUE;
+ } else {
+ mixer_map[i] = id;
+ alsa_channel_used[id] = TRUE;
+ }
+ }
+
+ pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels);
+
+ return 0;
+}
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) {
+ long min, max, v;
+
+ pa_assert(elem);
+
+ /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+ * raw volume levels and fix them to 75% */
+
+ if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0)
+ return;
+
+ if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0)
+ return;
+
+ if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0)
+ return;
+
+ v = min + ((max - min) * 3) / 4; /* 75% */
+
+ if (v <= min)
+ v = max;
+
+ snd_mixer_selem_set_playback_volume_all(elem, v);
+}
+
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) {
+ long min, max, v;
+
+ pa_assert(elem);
+
+ /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+ * raw volume levels and fix them to 75% */
+
+ if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0)
+ return;
+
+ if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0)
+ return;
+
+ if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0)
+ return;
+
+ v = min + ((max - min) * 3) / 4; /* 75% */
+
+ if (v <= min)
+ v = max;
+
+ snd_mixer_selem_set_capture_volume_all(elem, v);
+}
+
+void pa_alsa_dump(snd_pcm_t *pcm) {
+ int err;
+ snd_output_t *out;
+
+ pa_assert(pcm);
+
+ pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+ if ((err = snd_pcm_dump(pcm, out)) < 0)
+ pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+ else {
+ char *s = NULL;
+ snd_output_buffer_string(out, &s);
+ pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+ }
+
+ pa_assert_se(snd_output_close(out) == 0);
+}
+
+void pa_alsa_dump_status(snd_pcm_t *pcm) {
+ int err;
+ snd_output_t *out;
+ snd_pcm_status_t *status;
+
+ pa_assert(pcm);
+
+ snd_pcm_status_alloca(&status);
+
+ pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+ pa_assert_se(snd_pcm_status(pcm, status) == 0);
+
+ if ((err = snd_pcm_status_dump(status, out)) < 0)
+ pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+ else {
+ char *s = NULL;
+ snd_output_buffer_string(out, &s);
+ pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+ }
+
+ pa_assert_se(snd_output_close(out) == 0);
+}
+
+static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap);
+
+ va_end(ap);
+}
+
+static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0);
+
+void pa_alsa_redirect_errors_inc(void) {
+ /* This is not really thread safe, but we do our best */
+
+ if (pa_atomic_inc(&n_error_handler_installed) == 0)
+ snd_lib_error_set_handler(alsa_error_handler);
+}
+
+void pa_alsa_redirect_errors_dec(void) {
+ int r;
+
+ pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1);
+
+ if (r == 1)
+ snd_lib_error_set_handler(NULL);
+}
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+
+ static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
+ [SND_PCM_CLASS_GENERIC] = "generic",
+ [SND_PCM_CLASS_MULTI] = "multi",
+ [SND_PCM_CLASS_MODEM] = "modem",
+ [SND_PCM_CLASS_DIGITIZER] = "digitizer"
+ };
+ static const char * const class_table[SND_PCM_CLASS_LAST+1] = {
+ [SND_PCM_CLASS_GENERIC] = "sound",
+ [SND_PCM_CLASS_MULTI] = NULL,
+ [SND_PCM_CLASS_MODEM] = "modem",
+ [SND_PCM_CLASS_DIGITIZER] = NULL
+ };
+ static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = {
+ [SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix",
+ [SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix"
+ };
+
+ snd_pcm_class_t class;
+ snd_pcm_subclass_t subclass;
+ const char *n, *id, *sdn;
+ char *cn = NULL, *lcn = NULL;
+ int card;
+
+ pa_assert(p);
+ pa_assert(pcm_info);
+
+ pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa");
+
+ class = snd_pcm_info_get_class(pcm_info);
+ if (class <= SND_PCM_CLASS_LAST) {
+ if (class_table[class])
+ pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]);
+ if (alsa_class_table[class])
+ pa_proplist_sets(p, "alsa.class", alsa_class_table[class]);
+ }
+ subclass = snd_pcm_info_get_subclass(pcm_info);
+ if (subclass <= SND_PCM_SUBCLASS_LAST)
+ if (alsa_subclass_table[subclass])
+ pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]);
+
+ if ((n = snd_pcm_info_get_name(pcm_info)))
+ pa_proplist_sets(p, "alsa.name", n);
+
+ if ((id = snd_pcm_info_get_id(pcm_info)))
+ pa_proplist_sets(p, "alsa.id", id);
+
+ pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info));
+ if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info)))
+ pa_proplist_sets(p, "alsa.subdevice_name", sdn);
+
+ pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
+
+ if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
+ pa_proplist_setf(p, "alsa.card", "%i", card);
+
+ if (snd_card_get_name(card, &cn) >= 0)
+ pa_proplist_sets(p, "alsa.card_name", cn);
+
+ if (snd_card_get_longname(card, &lcn) >= 0)
+ pa_proplist_sets(p, "alsa.long_card_name", lcn);
+ }
+
+ if (cn && n)
+ pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n);
+ else if (cn)
+ pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
+ else if (n)
+ pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
+
+ free(lcn);
+ free(cn);
+}
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
+ snd_pcm_state_t state;
+ int err;
+
+ pa_assert(pcm);
+
+ 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");
+
+ state = snd_pcm_state(pcm);
+ pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
+
+ /* Try to recover from this error */
+
+ switch (state) {
+
+ case SND_PCM_STATE_XRUN:
+ if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+
+ default:
+
+ snd_pcm_drop(pcm);
+
+ if ((err = snd_pcm_prepare(pcm)) < 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
+ int n, err;
+ struct pollfd *pollfd;
+ pa_rtpoll_item *item;
+
+ pa_assert(pcm);
+
+ if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
+ return NULL;
+ }
+
+ item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n);
+ pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+
+ if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) {
+ pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
+ pa_rtpoll_item_free(item);
+ return NULL;
+ }
+
+ return item;
+}
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
index 6f1f927e..4de8bcd2 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa-util.h
@@ -1,8 +1,6 @@
#ifndef fooalsautilhfoo
#define fooalsautilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,8 +27,10 @@
#include <pulse/sample.h>
#include <pulse/mainloop-api.h>
-
#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/rtpoll.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
@@ -38,10 +38,60 @@ 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_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_set_hw_params(
+ snd_pcm_t *pcm_handle,
+ pa_sample_spec *ss,
+ uint32_t *periods,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number);
+
+int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback);
+snd_pcm_t *pa_alsa_open_by_device_id(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched);
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+ const char *device,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched);
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem);
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem);
+
+void pa_alsa_dump(snd_pcm_t *pcm);
+void pa_alsa_dump_status(snd_pcm_t *pcm);
+
+void pa_alsa_redirect_errors_inc(void);
+void pa_alsa_redirect_errors_dec(void);
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
+
#endif
diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c
new file mode 100644
index 00000000..3767f01c
--- /dev/null
+++ b/src/modules/bt-proximity-helper.c
@@ -0,0 +1,202 @@
+/*
+ * Small SUID helper that allows us to ping a BT device. Borrows
+ * heavily from bluez-utils' l2ping, which is licensed as GPL2+
+ * and comes with a copyright like this:
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define PING_STRING "PulseAudio"
+#define IDENT 200
+#define TIMEOUT 4
+#define INTERVAL 2
+
+static void update_status(int found) {
+ static int status = -1;
+
+ if (!found && status != 0)
+ printf("-");
+ if (found && status <= 0)
+ printf("+");
+
+ fflush(stdout);
+ status = !!found;
+}
+
+int main(int argc, char *argv[]) {
+ struct sockaddr_l2 addr;
+ union {
+ l2cap_cmd_hdr hdr;
+ uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
+ } packet;
+ int fd = -1;
+ uint8_t id = IDENT;
+ int connected = 0;
+
+ assert(argc == 2);
+
+ for (;;) {
+ fd_set fds;
+ struct timeval end;
+ ssize_t r;
+
+ if (!connected) {
+
+ if (fd >= 0)
+ close(fd);
+
+ if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
+ fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "bind() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(argv[1], &addr.l2_bdaddr);
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "connect() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ connected = 1;
+ }
+
+ assert(connected);
+
+ memset(&packet, 0, sizeof(packet));
+ strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
+ packet.hdr.ident = id;
+ packet.hdr.len = htobs(sizeof(PING_STRING));
+ packet.hdr.code = L2CAP_ECHO_REQ;
+
+ if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "send() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ assert(r == sizeof(packet));
+
+ gettimeofday(&end, NULL);
+ end.tv_sec += TIMEOUT;
+
+ for (;;) {
+ struct timeval now, delta;
+
+ gettimeofday(&now, NULL);
+
+ if (timercmp(&end, &now, <=)) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ timersub(&end, &now, &delta);
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
+ fprintf(stderr, "select() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
+ goto finish;
+ }
+
+ assert(r >= L2CAP_CMD_HDR_SIZE);
+
+ if (packet.hdr.ident != id)
+ continue;
+
+ if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
+
+ if (++id >= 0xFF)
+ id = IDENT;
+
+ update_status(1);
+ sleep(INTERVAL);
+ break;
+ }
+ }
+ }
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ return 1;
+}
diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index ccc658b7..905be13f 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,7 +33,7 @@
struct pa_dbus_connection {
PA_REFCNT_DECLARE;
-
+
pa_core *core;
DBusConnection *connection;
const char *property_name;
@@ -44,7 +42,7 @@ struct pa_dbus_connection {
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);
@@ -56,13 +54,13 @@ static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, voi
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:
@@ -76,9 +74,9 @@ static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
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))
return PA_IO_EVENT_NULL;
@@ -142,14 +140,14 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
pa_assert(c);
ev = c->mainloop->io_new(
- c->mainloop,
+ c->mainloop,
#if HAVE_DBUS_WATCH_GET_UNIX_FD
- dbus_watch_get_unix_fd(watch),
+ dbus_watch_get_unix_fd(watch),
#else
- dbus_watch_get_fd(watch),
+ dbus_watch_get_fd(watch),
#endif
get_watch_flags(watch), handle_io_event, watch);
-
+
dbus_watch_set_data(watch, ev, NULL);
return TRUE;
@@ -162,7 +160,7 @@ static void remove_watch(DBusWatch *watch, void *data) {
pa_assert(watch);
pa_assert(c);
-
+
if ((ev = dbus_watch_get_data(watch)))
c->mainloop->io_free(ev);
}
@@ -174,7 +172,7 @@ static void toggle_watch(DBusWatch *watch, void *data) {
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 */
@@ -210,7 +208,7 @@ static void remove_timeout(DBusTimeout *timeout, void *data) {
pa_assert(timeout);
pa_assert(c);
-
+
if ((ev = dbus_timeout_get_data(timeout)))
c->mainloop->time_free(ev);
}
@@ -222,15 +220,15 @@ static void toggle_timeout(DBusTimeout *timeout, void *data) {
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
c->mainloop->time_restart(ev, NULL);
@@ -240,7 +238,7 @@ static void wakeup_main(void *userdata) {
pa_dbus_connection *c = userdata;
pa_assert(c);
-
+
/* this will wakeup the mainloop and dispatch events, although
* it may not be the cleanest way of accomplishing it */
c->core->mainloop->defer_enable(c->dispatch_event, 1);
@@ -265,7 +263,7 @@ 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;
}
@@ -282,7 +280,7 @@ void pa_dbus_connection_unref(pa_dbus_connection *c) {
* both unload and shutdown */
while (dbus_connection_read_write_dispatch(c->connection, -1));
}
-
+
/* already disconnected, just free */
pa_property_remove(c->core, c->property_name);
c->core->mainloop->defer_free(c->dispatch_event);
@@ -300,7 +298,7 @@ pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
}
pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
-
+
static const char *const prop_name[] = {
[DBUS_BUS_SESSION] = "dbus-connection-session",
[DBUS_BUS_SYSTEM] = "dbus-connection-system",
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index 8dca54fe..2b24ac63 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -1,8 +1,6 @@
#ifndef foodbusutilhfoo
#define foodbusutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
index abd13287..f5016faf 100644
--- a/src/modules/gconf/gconf-helper.c
+++ b/src/modules/gconf/gconf-helper.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
index 24f60b1e..a2a43278 100644
--- a/src/modules/gconf/module-gconf.c
+++ b/src/modules/gconf/module-gconf.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,30 +31,23 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
-#include <dirent.h>
-
-#ifdef HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
+#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/core.h>
#include <pulsecore/llist.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulse/mainloop-api.h>
-#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
#include "module-gconf-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("GConf Adapter")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("GConf Adapter");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
#define MAX_MODULES 10
#define BUF_MAX 2048
@@ -225,8 +216,11 @@ static int handle_event(struct userdata *u) {
int ret = 0;
do {
- if ((opcode = read_byte(u)) < 0)
+ if ((opcode = read_byte(u)) < 0){
+ if (errno == EINTR || errno == EAGAIN)
+ break;
goto fail;
+ }
switch (opcode) {
case '!':
@@ -334,117 +328,6 @@ static void io_event_cb(
}
}
-static int start_client(const char *n, pid_t *pid) {
- pid_t child;
- int pipe_fds[2] = { -1, -1 };
-
- if (pipe(pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- if ((child = fork()) == (pid_t) -1) {
- pa_log("fork() failed: %s", pa_cstrerror(errno));
- goto fail;
- } else if (child != 0) {
-
- /* Parent */
- close(pipe_fds[1]);
-
- if (pid)
- *pid = child;
-
- return pipe_fds[0];
- } else {
-#ifdef __linux__
- DIR* d;
-#endif
- int max_fd, i;
- /* child */
-
- close(pipe_fds[0]);
- dup2(pipe_fds[1], 1);
-
- if (pipe_fds[1] != 1)
- close(pipe_fds[1]);
-
- close(0);
- open("/dev/null", O_RDONLY);
-
- close(2);
- open("/dev/null", O_WRONLY);
-
-#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;
- }
-#endif
-
- 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
- process killed when the daemon dies abnormally. On non-Linux
- machines the client will die as soon as it writes data to
- stdout again (SIGPIPE) */
-
- prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
-#endif
-
-#ifdef SIGPIPE
- /* Make sure that SIGPIPE kills the child process */
- signal(SIGPIPE, SIG_DFL);
-#endif
-
- execl(n, n, NULL);
- _exit(1);
- }
-
-fail:
- if (pipe_fds[0] >= 0)
- close(pipe_fds[0]);
-
- if (pipe_fds[1] >= 0)
- close(pipe_fds[1]);
-
- return -1;
-}
-
int pa__init(pa_module*m) {
struct userdata *u;
int r;
@@ -460,7 +343,7 @@ int pa__init(pa_module*m) {
u->io_event = NULL;
u->buf_fill = 0;
- if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0)
+ if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &u->pid)) < 0)
goto fail;
u->io_event = m->core->mainloop->io_new(
@@ -493,20 +376,20 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
+ if (u->pid != (pid_t) -1) {
+ kill(u->pid, SIGTERM);
+ waitpid(u->pid, NULL, 0);
+ }
+
if (u->io_event)
m->core->mainloop->io_free(u->io_event);
if (u->fd >= 0)
- close(u->fd);
+ pa_close(u->fd);
- if (u->pid != (pid_t) -1) {
- kill(u->pid, SIGTERM);
- waitpid(u->pid, NULL, 0);
- }
if (u->module_infos)
pa_hashmap_free(u->module_infos, module_info_free, u);
pa_xfree(u);
}
-
diff --git a/src/modules/ladspa.h b/src/modules/ladspa.h
index 5c30a8a4..b1a9c4e5 100644
--- a/src/modules/ladspa.h
+++ b/src/modules/ladspa.h
@@ -3,17 +3,17 @@
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
@@ -32,7 +32,7 @@ extern "C" {
/*****************************************************************************/
-/* Overview:
+/* 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
@@ -76,7 +76,7 @@ extern "C" {
/* 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).
+ 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. */
@@ -85,8 +85,8 @@ typedef float LADSPA_Data;
/*****************************************************************************/
-/* Special Plugin Properties:
-
+/* Special Plugin Properties:
+
Optional features of the plugin type are encapsulated in the
LADSPA_Properties type. This is assembled by ORing individual
properties together. */
@@ -122,7 +122,7 @@ typedef int LADSPA_Properties;
(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
@@ -137,7 +137,7 @@ typedef int LADSPA_Properties;
/*****************************************************************************/
-/* Plugin Ports:
+/* Plugin Ports:
Plugins have `ports' that are inputs or outputs for audio or
data. Ports can communicate arrays of LADSPA_Data (for audio
@@ -172,23 +172,23 @@ typedef int LADSPA_PortDescriptor;
/*****************************************************************************/
-/* Plugin Port Range Hints:
+/* 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
@@ -353,7 +353,7 @@ typedef struct _LADSPA_PortRangeHint {
/*****************************************************************************/
-/* Plugin Handles:
+/* 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
@@ -364,13 +364,13 @@ typedef void * LADSPA_Handle;
/*****************************************************************************/
-/* Descriptor for a Type of Plugin:
+/* 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 {
+typedef struct _LADSPA_Descriptor {
/* This numeric identifier indicates the plugin type
uniquely. Plugin programmers may reserve ranges of IDs from a
@@ -430,7 +430,7 @@ typedef struct _LADSPA_Descriptor {
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.
+ instantiation fails.
Note that instance initialisation should generally occur in
activate() rather than here. */
@@ -551,7 +551,7 @@ typedef struct _LADSPA_Descriptor {
/* 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. */
@@ -589,7 +589,7 @@ typedef struct _LADSPA_Descriptor {
const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
/* Datatype corresponding to the ladspa_descriptor() function. */
-typedef const LADSPA_Descriptor *
+typedef const LADSPA_Descriptor *
(*LADSPA_Descriptor_Function)(unsigned long Index);
/**********************************************************************/
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index a09247fe..6765775a 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -32,6 +30,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulse/timeval.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -46,25 +45,55 @@
#include <pulsecore/core-error.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/time-smoother.h>
#include "alsa-util.h"
#include "module-alsa-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"device=<ALSA device> "
+ "device_id=<ALSA card index> "
"format=<sample format> "
- "channels=<number of channels> "
"rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map> "
- "mmap=<enable memory mapping?>")
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<lower fill watermark> "
+ "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "mixer_reset",
+ NULL
+};
#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
struct userdata {
pa_core *core;
@@ -81,224 +110,404 @@ struct userdata {
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
+ long hw_dB_max, hw_dB_min;
+ pa_bool_t hw_dB_supported;
- size_t frame_size, fragment_size, hwbuf_size;
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
unsigned nfragments;
pa_memchunk memchunk;
char *device_name;
- int use_mmap;
+ pa_bool_t use_mmap, use_tsched;
- int first;
+ pa_bool_t first, after_rewind;
pa_rtpoll_item *alsa_rtpoll_item;
-};
-static const char* const valid_modargs[] = {
- "device",
- "sink_name",
- "format",
- "channels",
- "rate",
- "fragments",
- "fragment_size",
- "channel_map",
- "mmap",
- NULL
+ snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+ pa_smoother *smoother;
+ int64_t frame_index;
+ uint64_t since_start;
+
+ snd_pcm_sframes_t hwbuf_unused_frames;
};
-static int mmap_write(struct userdata *u) {
+static void fix_tsched_watermark(struct userdata *u) {
+ size_t max_use;
+ size_t min_sleep, min_wakeup;
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+ min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
+ min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+
+ if (min_sleep > max_use/2)
+ min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec);
+ if (min_sleep < u->frame_size)
+ min_sleep = u->frame_size;
+
+ if (min_wakeup > max_use/2)
+ min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec);
+ if (min_wakeup < u->frame_size)
+ min_wakeup = u->frame_size;
+
+ if (u->tsched_watermark > max_use-min_sleep)
+ u->tsched_watermark = max_use-min_sleep;
+
+ if (u->tsched_watermark < min_wakeup)
+ u->tsched_watermark = min_wakeup;
+}
+
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+ pa_usec_t usec, wm;
+
+ pa_assert(sleep_usec);
+ pa_assert(process_usec);
+
+ pa_assert(u);
+
+ usec = pa_sink_get_requested_latency_within_thread(u->sink);
+
+ if (usec == (pa_usec_t) -1)
+ usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
+
+/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+ wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
+
+ if (usec >= wm) {
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
+ } else
+ *process_usec = *sleep_usec = usec / 2;
+
+/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+ pa_assert(u);
+ pa_assert(call);
+ pa_assert(err < 0);
+
+ pa_log_debug("%s: %s", call, snd_strerror(err));
+
+ pa_assert(err != -EAGAIN);
+
+ if (err == -EPIPE)
+ pa_log_debug("%s: Buffer underrun!", call);
+
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+ u->first = TRUE;
+ u->since_start = 0;
+ return 0;
+ }
+
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
+}
+
+static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
+ size_t left_to_play;
+
+ if (n*u->frame_size < u->hwbuf_size)
+ left_to_play = u->hwbuf_size - (n*u->frame_size);
+ else
+ left_to_play = 0;
+
+ if (left_to_play > 0) {
+/* pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */
+ } else if (!u->first && !u->after_rewind) {
+ pa_log_info("Underrun!");
+
+ if (u->use_tsched) {
+ size_t old_watermark = u->tsched_watermark;
+
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
+
+ if (old_watermark != u->tsched_watermark)
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ }
+ }
+
+ return left_to_play;
+}
+
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_play;
pa_assert(u);
pa_sink_assert_ref(u->sink);
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
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;
+ int r;
- if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+ snd_pcm_hwsync(u->pcm_handle);
- if (n == -EPIPE) {
- pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
- u->first = 1;
- }
+ /* First we determine how many samples are missing to fill the
+ * buffer up to 100% */
- if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0)
- continue;
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
- if (err == -EAGAIN)
- return work_done;
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+ continue;
- pa_log("snd_pcm_avail_update: %s", snd_strerror(err));
- return -1;
+ return r;
}
-/* pa_log("Got request for %i samples", (int) n); */
+ left_to_play = check_left_to_play(u, n);
- if (n <= 0)
- return work_done;
+ if (u->use_tsched)
- frames = n;
+ /* We won't fill up the playback buffer before at least
+ * half the sleep time is over because otherwise we might
+ * ask for more data from the clients then they expect. We
+ * need to guarantee that clients only have to keep around
+ * a single hw buffer length. */
- if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) {
+ if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
- if (err == -EPIPE) {
- pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
- u->first = 1;
- }
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ break;
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
- continue;
+ n -= u->hwbuf_unused_frames;
- if (err == -EAGAIN)
- return work_done;
+/* pa_log_debug("Filling up"); */
- pa_log("Failed to write data to DSP: %s", snd_strerror(err));
- return -1;
- }
+ for (;;) {
+ pa_memchunk chunk;
+ void *p;
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
- /* Check these are multiples of 8 bit */
- pa_assert((areas[0].first & 7) == 0);
- pa_assert((areas[0].step & 7)== 0);
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- /* We assume a single interleaved memory buffer */
- pa_assert((areas[0].first >> 3) == 0);
- pa_assert((areas[0].step >> 3) == u->frame_size);
+ if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
- p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+ continue;
- 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;
+ return r;
+ }
- pa_sink_render_into_full(u->sink, &chunk);
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
- /* FIXME: Maybe we can do something to keep this memory block
- * a little bit longer around? */
- pa_memblock_unref_fixed(chunk.memblock);
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) {
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
- if (err == -EPIPE) {
- pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
- u->first = 1;
- }
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
- continue;
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
- if (err == -EAGAIN)
- return work_done;
+ pa_sink_render_into_full(u->sink, &chunk);
- pa_log("Failed to write data to DSP: %s", snd_strerror(err));
- return -1;
- }
+ /* FIXME: Maybe we can do something to keep this memory block
+ * a little bit longer around? */
+ pa_memblock_unref_fixed(chunk.memblock);
- work_done = 1;
+ if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
- if (frames >= (snd_pcm_uframes_t) n)
- return work_done;
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+ continue;
+
+ return r;
+ }
-/* pa_log("wrote %i samples", (int) frames); */
+ work_done = 1;
+
+ u->frame_index += frames;
+ u->since_start += frames * u->frame_size;
+
+/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+ if (frames >= (snd_pcm_uframes_t) n)
+ break;
+
+ n -= frames;
+ }
}
+
+ *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+ return work_done;
}
-static int unix_write(struct userdata *u) {
- snd_pcm_status_t *status;
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
int work_done = 0;
-
- snd_pcm_status_alloca(&status);
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_play;
pa_assert(u);
pa_sink_assert_ref(u->sink);
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
for (;;) {
- void *p;
- snd_pcm_sframes_t t;
- ssize_t l;
- int err;
+ snd_pcm_sframes_t n;
+ int r;
- if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) {
- pa_log("Failed to query DSP status data: %s", snd_strerror(err));
- return -1;
+ snd_pcm_hwsync(u->pcm_handle);
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+ continue;
+
+ return r;
}
- if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)
- pa_log_debug("Buffer underrun!");
+ left_to_play = check_left_to_play(u, n);
- l = snd_pcm_status_get_avail(status) * u->frame_size;
+ if (u->use_tsched)
-/* pa_log("%u bytes to write", l); */
+ /* We won't fill up the playback buffer before at least
+ * half the sleep time is over because otherwise we might
+ * ask for more data from the clients then they expect. We
+ * need to guarantee that clients only have to keep around
+ * a single hw buffer length. */
- if (l <= 0)
- return work_done;
+ if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
- if (u->memchunk.length <= 0)
- pa_sink_render(u->sink, l, &u->memchunk);
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ break;
- pa_assert(u->memchunk.length > 0);
+ n -= u->hwbuf_unused_frames;
- 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);
+ for (;;) {
+ snd_pcm_sframes_t frames;
+ void *p;
-/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- pa_assert(t != 0);
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, n * u->frame_size, &u->memchunk);
- if (t < 0) {
+ pa_assert(u->memchunk.length > 0);
- if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)
- continue;
+ frames = u->memchunk.length / u->frame_size;
+
+ if (frames > n)
+ frames = n;
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(frames != 0);
+
+ if (PA_UNLIKELY(frames < 0)) {
- 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;
+ if ((r = try_recover(u, "snd_pcm_writei", n)) == 0)
+ continue;
+
+ return r;
}
- }
- u->memchunk.index += t * u->frame_size;
- u->memchunk.length -= t * u->frame_size;
+ u->memchunk.index += frames * u->frame_size;
+ u->memchunk.length -= frames * u->frame_size;
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
- work_done = 1;
+ work_done = 1;
- if (t * u->frame_size >= (unsigned) l)
- return work_done;
+ u->frame_index += frames;
+ u->since_start += frames * u->frame_size;
+
+/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+ if (frames >= n)
+ break;
+
+ n -= frames;
+ }
}
+
+ *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+ return work_done;
}
-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;
+static void update_smoother(struct userdata *u) {
+ snd_pcm_sframes_t delay = 0;
+ int64_t frames;
int err;
+ pa_usec_t now1, now2;
+/* struct timeval timestamp; */
+ snd_pcm_status_t *status;
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);
+ /* Let's update the time smoother */
+
+ snd_pcm_hwsync(u->pcm_handle);
+ snd_pcm_avail_update(u->pcm_handle);
- if (frames > 0)
- r = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
+/* if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */
+/* pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */
+/* return; */
+/* } */
+
+/* delay = snd_pcm_status_get_delay(status); */
+
+ if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+ pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+ return;
+ }
+
+ frames = u->frame_index - delay;
+
+/* pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */
+
+/* snd_pcm_status_get_tstamp(status, &timestamp); */
+/* pa_rtclock_from_wallclock(&timestamp); */
+/* now1 = pa_timeval_load(&timestamp); */
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
+ pa_smoother_put(u->smoother, now1, now2);
+}
+
+static pa_usec_t sink_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
+ int64_t delay;
+ pa_usec_t now1, now2;
+
+ pa_assert(u);
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_smoother_get(u->smoother, now1);
+
+ delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;
+
+ if (delay > 0)
+ r = (pa_usec_t) delay;
if (u->memchunk.memblock)
r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
@@ -307,28 +516,14 @@ static pa_usec_t sink_get_latency(struct userdata *u) {
}
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));
+ if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
return -1;
- }
return 0;
}
@@ -337,6 +532,8 @@ static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
/* Let's suspend */
snd_pcm_drain(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
@@ -352,9 +549,66 @@ static int suspend(struct userdata *u) {
return 0;
}
+static int update_sw_params(struct userdata *u) {
+ snd_pcm_uframes_t avail_min;
+ int err;
+
+ pa_assert(u);
+
+ /* Use the full buffer if noone asked us for anything specific */
+ u->hwbuf_unused_frames = 0;
+
+ if (u->use_tsched) {
+ pa_usec_t latency;
+
+ if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
+ size_t b;
+
+ pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+ b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
+
+ /* We need at least one sample in our buffer */
+
+ if (PA_UNLIKELY(b < u->frame_size))
+ b = u->frame_size;
+
+ u->hwbuf_unused_frames =
+ PA_LIKELY(b < u->hwbuf_size) ?
+ ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+ fix_tsched_watermark(u);
+ }
+ }
+
+ pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+
+ /* We need at last one frame in the used part of the buffer */
+ avail_min = u->hwbuf_unused_frames + 1;
+
+ if (u->use_tsched) {
+ pa_usec_t sleep_usec, process_usec;
+
+ hw_sleep_time(u, &sleep_usec, &process_usec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+ }
+
+ pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ return err;
+ }
+
+ pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size);
+
+ return 0;
+}
+
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
- int err, b;
+ int err;
+ pa_bool_t b, d;
unsigned nfrags;
snd_pcm_uframes_t period_size;
@@ -373,13 +627,14 @@ static int unsuspend(struct userdata *u) {
nfrags = u->nfragments;
period_size = u->fragment_size / u->frame_size;
b = u->use_mmap;
+ d = u->use_tsched;
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
goto fail;
}
- if (b != u->use_mmap) {
+ if (b != u->use_mmap || d != u->use_tsched) {
pa_log_warn("Resume failed, couldn't get original access mode.");
goto fail;
}
@@ -394,17 +649,16 @@ static int unsuspend(struct userdata *u) {
goto fail;
}
- if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
- pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ if (update_sw_params(u) < 0)
goto fail;
- }
if (build_pollfd(u) < 0)
goto fail;
/* FIXME: We need to reload the volume somehow */
- u->first = 1;
+ u->first = TRUE;
+ u->since_start = 0;
pa_log_info("Resumed successfully...");
@@ -440,7 +694,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
if (suspend(u) < 0)
return -1;
@@ -499,18 +753,24 @@ static int sink_get_volume_cb(pa_sink *s) {
pa_assert(u->mixer_elem);
for (i = 0; i < s->sample_spec.channels; i++) {
- long set_vol, vol;
+ long alsa_vol;
- pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
- if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, i, &vol)) < 0)
- goto fail;
+ if (u->hw_dB_supported) {
+
+ if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+ continue;
+ }
- set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ u->hw_dB_supported = FALSE;
+ }
- /* Try to avoid superfluous volume changes */
- if (set_vol != vol)
- 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));
+ if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
+ goto fail;
+
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
@@ -518,8 +778,6 @@ static int sink_get_volume_cb(pa_sink *s) {
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
- s->get_volume = NULL;
- s->set_volume = NULL;
return -1;
}
@@ -535,17 +793,34 @@ static int sink_set_volume_cb(pa_sink *s) {
long alsa_vol;
pa_volume_t vol;
- pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
+
+ vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
- vol = s->volume.values[i];
+ if (u->hw_dB_supported) {
+ alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
- if (vol > PA_VOLUME_NORM)
- vol = PA_VOLUME_NORM;
+ if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+ if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+ continue;
+ }
+
+ u->hw_dB_supported = FALSE;
+
+ }
alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
- if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, i, alsa_vol)) < 0)
+ if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
+
+ if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
@@ -553,8 +828,6 @@ static int sink_set_volume_cb(pa_sink *s) {
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_volume = NULL;
- s->set_volume = NULL;
return -1;
}
@@ -567,9 +840,6 @@ static int sink_get_mute_cb(pa_sink *s) {
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_mute = NULL;
- s->set_mute = NULL;
return -1;
}
@@ -587,12 +857,90 @@ static int sink_set_mute_cb(pa_sink *s) {
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));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ snd_pcm_sframes_t before;
+ pa_assert(u);
+
+ if (!u->pcm_handle)
+ return;
+
+ before = u->hwbuf_unused_frames;
+ update_sw_params(u);
+
+ /* Let's check whether we now use only a smaller part of the
+ buffer then before. If so, we need to make sure that subsequent
+ rewinds are relative to the new maxium fill level and not to the
+ current fill level. Thus, let's do a full rewind once, to clear
+ things up. */
+
+ if (u->hwbuf_unused_frames > before) {
+ pa_log_debug("Requesting rewind due to latency change.");
+ pa_sink_request_rewind(s, 0);
+ }
+}
- s->get_mute = NULL;
- s->set_mute = NULL;
+static int process_rewind(struct userdata *u) {
+ snd_pcm_sframes_t unused;
+ size_t rewind_nbytes, unused_nbytes, limit_nbytes;
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ pa_assert(rewind_nbytes > 0);
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+ snd_pcm_hwsync(u->pcm_handle);
+ if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+ pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused));
return -1;
}
+ unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size;
+
+ if (u->hwbuf_size > unused_nbytes)
+ limit_nbytes = u->hwbuf_size - unused_nbytes;
+ else
+ limit_nbytes = 0;
+
+ if (rewind_nbytes > limit_nbytes)
+ rewind_nbytes = limit_nbytes;
+
+ if (rewind_nbytes > 0) {
+ snd_pcm_sframes_t in_frames, out_frames;
+
+ pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes);
+
+ in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size;
+ pa_log_debug("before: %lu", (unsigned long) in_frames);
+ if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) {
+ pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames));
+ return -1;
+ }
+ pa_log_debug("after: %lu", (unsigned long) out_frames);
+
+ rewind_nbytes = out_frames * u->frame_size;
+
+ if (rewind_nbytes <= 0)
+ pa_log_info("Tried rewind, but was apparently not possible.");
+ else {
+ u->frame_index -= out_frames;
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+
+ u->after_rewind = TRUE;
+ }
+ } else
+ pa_log_debug("Mhmm, actually there is nothing to rewind.");
+
return 0;
}
@@ -603,8 +951,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -612,25 +960,77 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
+/* pa_log_debug("loop"); */
+
/* Render some data and write it to the dsp */
- if (PA_SINK_OPENED(u->sink->thread_info.state)) {
- int work_done = 0;
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ int work_done;
+ pa_usec_t sleep_usec;
- if (u->use_mmap) {
- if ((work_done = mmap_write(u)) < 0)
- goto fail;
- } else {
- if ((work_done = unix_write(u)) < 0)
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ if (process_rewind(u) < 0)
goto fail;
+
+ if (u->use_mmap)
+ work_done = mmap_write(u, &sleep_usec);
+ else
+ work_done = unix_write(u, &sleep_usec);
+
+ if (work_done < 0)
+ goto fail;
+
+/* pa_log_debug("work_done = %i", work_done); */
+
+ if (work_done) {
+
+ if (u->first) {
+ pa_log_info("Starting playback.");
+ snd_pcm_start(u->pcm_handle);
+
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ }
+
+ update_smoother(u);
}
- if (work_done && u->first) {
- pa_log_info("Starting playback.");
- snd_pcm_start(u->pcm_handle);
- u->first = 0;
- continue;
+ if (u->use_tsched) {
+ pa_usec_t cusec;
+
+ if (u->since_start <= u->hwbuf_size) {
+
+ /* USB devices on ALSA seem to hit a buffer
+ * underrun during the first iterations much
+ * quicker then we calculate here, probably due to
+ * the transport latency. To accomodate for that
+ * we artificially decrease the sleep time until
+ * we have filled the buffer at least once
+ * completely.*/
+
+ /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/
+ sleep_usec /= 2;
+ }
+
+ /* OK, the playback buffer is now full, let's
+ * calculate when to wake up next */
+/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+ /* Convert from the sound card time domain to the
+ * system time domain */
+ cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+ /* We don't trust the conversion, so we wake up whatever comes first */
+ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
}
- }
+
+ u->first = FALSE;
+ u->after_rewind = FALSE;
+
+ } else if (u->use_tsched)
+
+ /* OK, we're in an invalid state, let's disable our timers */
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
@@ -640,7 +1040,7 @@ static void thread_func(void *userdata) {
goto finish;
/* Tell ALSA about this and process its response */
- if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
struct pollfd *pollfd;
unsigned short revents = 0;
int err;
@@ -654,43 +1054,15 @@ static void thread_func(void *userdata) {
}
if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+ if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+ goto fail;
- 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;
- }
+ u->first = TRUE;
+ u->since_start = 0;
}
+
+ if (revents && u->use_tsched)
+ pa_log_debug("Wakeup from ALSA! (%i)", revents);
}
}
@@ -708,24 +1080,27 @@ int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u = NULL;
- char *dev;
+ const char *dev_id;
pa_sample_spec ss;
pa_channel_map map;
- uint32_t nfrags, frag_size;
- snd_pcm_uframes_t period_size;
+ uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+ snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- char *t;
const char *name;
char *name_buf = NULL;
- int namereg_fail;
- int use_mmap = 1, b;
+ pa_bool_t namereg_fail;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+ pa_usec_t usec;
+ pa_sink_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
+ pa_alsa_redirect_errors_inc();
+
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
@@ -740,117 +1115,180 @@ int pa__init(pa_module*m) {
frame_size = pa_frame_size(&ss);
nfrags = m->core->default_n_fragments;
- frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
if (frag_size <= 0)
frag_size = frame_size;
+ tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+ tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
- if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) {
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+ pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
pa_log("Failed to parse buffer metrics");
goto fail;
}
- period_size = frag_size/frame_size;
+
+ hwbuf_size = frag_size * nfrags;
+ period_frames = frag_size/frame_size;
+ tsched_frames = tsched_size/frame_size;
if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
pa_log("Failed to parse mmap argument.");
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ pa_log("Failed to parse timer_scheduling argument.");
+ goto fail;
+ }
+
+ if (use_tsched && !pa_rtclock_hrtimer()) {
+ pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+ use_tsched = FALSE;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+ pa_log("Failed to parse mixer_reset argument.");
+ goto fail;
+ }
+
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->use_mmap = use_mmap;
- u->first = 1;
- pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->use_tsched = use_tsched;
+ u->first = TRUE;
+ u->since_start = 0;
+ u->after_rewind = FALSE;
u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->alsa_rtpoll_item = NULL;
- pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
- snd_config_update_free_global();
-
- dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+ u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
+ usec = pa_rtclock_usec();
+ pa_smoother_set_time_offset(u->smoother, usec);
+ pa_smoother_pause(u->smoother, usec);
- for (;;) {
+ snd_config_update_free_global();
- 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;
+ d = use_tsched;
- b = use_mmap;
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+ if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
- if (err == -EPERM) {
- /* Hmm, some hw is very exotic, so we retry with plughw, if hw didn't work */
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
- 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;
+ goto fail;
- snd_pcm_close(u->pcm_handle);
- u->pcm_handle = NULL;
- continue;
- }
- }
+ } else {
- pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
- pa_xfree(dev);
+ if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
goto fail;
- }
- break;
}
- u->device_name = dev;
+ pa_assert(u->device_name);
+ pa_log_info("Successfully opened device %s.", u->device_name);
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;
+ u->use_mmap = use_mmap = FALSE;
+ }
+
+ if (use_tsched && (!b || !d)) {
+ pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ u->use_tsched = use_tsched = FALSE;
}
if (u->use_mmap)
pa_log_info("Successfully enabled mmap() mode.");
+ if (u->use_tsched)
+ pa_log_info("Successfully enabled timer-based scheduling mode.");
+
if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
pa_log("Error fetching PCM info: %s", snd_strerror(err));
goto fail;
}
- 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)
pa_log_warn("Error opening mixer: %s", snd_strerror(err));
else {
+ pa_bool_t found = FALSE;
+
+ if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+ found = TRUE;
+ else {
+ snd_pcm_info_t *info;
+
+ snd_pcm_info_alloca(&info);
+
+ if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+ char *md;
+ int card;
- if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
- !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM"))) {
+ if ((card = snd_pcm_info_get_card(info)) >= 0) {
+ md = pa_sprintf_malloc("hw:%i", card);
+
+ if (strcmp(u->device_name, md))
+ if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+ found = TRUE;
+ pa_xfree(md);
+ }
+ }
+ }
+
+ if (found)
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM")))
+ found = FALSE;
+
+ if (!found) {
snd_mixer_close(u->mixer_handle);
u->mixer_handle = NULL;
}
}
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = 1;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("alsa_output.%s", dev);
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
+ namereg_fail = FALSE;
}
- u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, name);
+ data.namereg_fail = namereg_fail;
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+
+ pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
pa_xfree(name_buf);
if (!u->sink) {
@@ -859,59 +1297,124 @@ int pa__init(pa_module*m) {
}
u->sink->parent.process_msg = sink_process_msg;
+ u->sink->update_requested_latency = sink_update_requested_latency_cb;
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->fragment_size = frag_size = period_frames * frame_size;
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
+ u->hwbuf_unused_frames = 0;
+ u->tsched_watermark = tsched_watermark;
+ u->frame_index = 0;
+ u->hw_dB_supported = FALSE;
+ u->hw_dB_min = u->hw_dB_max = 0;
+ u->hw_volume_min = u->hw_volume_max = 0;
+
+ if (use_tsched)
+ fix_tsched_watermark(u);
+
+ u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0;
+ u->sink->thread_info.max_request = u->hwbuf_size;
+
+ pa_sink_set_latency_range(u->sink,
+ !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+ pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+ pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+ nfrags, (long unsigned) u->fragment_size,
+ (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
- pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size);
+ if (use_tsched)
+ pa_log_info("Time scheduling watermark is %0.2fms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
+
+ if (update_sw_params(u) < 0)
+ goto fail;
pa_memchunk_reset(&u->memchunk);
if (u->mixer_handle) {
- /* Initialize mixer code */
-
pa_assert(u->mixer_elem);
- if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
- int i;
+ if (snd_mixer_selem_has_playback_volume(u->mixer_elem))
- for (i = 0; i < ss.channels; i++)
- if (!snd_mixer_selem_has_playback_channel(u->mixer_elem, i))
- break;
+ if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 &&
+ snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
- if (i == ss.channels) {
- 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);
- }
+ pa_bool_t suitable = TRUE;
+
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+ if (u->hw_volume_min > u->hw_volume_max) {
+
+ pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+ suitable = FALSE;
+
+ } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+
+ } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+ /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */
+
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+ /* Let's see if this thing actually is useful for muting */
+ if (u->hw_dB_min > -6000) {
+ pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+ suitable = FALSE;
+ } else if (u->hw_dB_max < 0) {
+
+ pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+ pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else {
+
+ if (u->hw_dB_max > 0) {
+ /* dB > 0 means overamplification, and clipping, we don't want that here */
+ pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100);
+ u->hw_dB_max = 0;
+ }
+
+ u->hw_dB_supported = TRUE;
+ }
+ }
+
+ if (suitable) {
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ } else if (mixer_reset) {
+ pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+ pa_alsa_0dB_playback(u->mixer_elem);
+ } else
+ pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
+ }
if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
u->sink->get_mute = sink_get_mute_cb;
u->sink->set_mute = sink_set_mute_cb;
+ u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
}
u->mixer_fdl = pa_alsa_fdlist_new();
if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
- pa_log("failed to initialise file descriptor monitoring");
+ pa_log("Failed to initialize file descriptor monitoring");
goto fail;
}
@@ -920,16 +1423,29 @@ int pa__init(pa_module*m) {
} else
u->mixer_fdl = NULL;
+ pa_alsa_dump(u->pcm_handle);
+
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_volume)
- u->sink->get_volume(u->sink);
- if (u->sink->get_mute)
- u->sink->get_mute(u->sink);
+ if (data.volume_is_set) {
+ if (u->sink->set_volume)
+ u->sink->set_volume(u->sink);
+ } else {
+ if (u->sink->get_volume)
+ u->sink->get_volume(u->sink);
+ }
+
+ if (data.muted_is_set) {
+ if (u->sink->set_mute)
+ u->sink->set_mute(u->sink);
+ } else {
+ if (u->sink->get_mute)
+ u->sink->get_mute(u->sink);
+ }
pa_sink_put(u->sink);
@@ -952,8 +1468,10 @@ void pa__done(pa_module*m) {
pa_assert(m);
- if (!(u = m->userdata))
+ if (!(u = m->userdata)) {
+ pa_alsa_redirect_errors_dec();
return;
+ }
if (u->sink)
pa_sink_unlink(u->sink);
@@ -988,8 +1506,13 @@ void pa__done(pa_module*m) {
snd_pcm_close(u->pcm_handle);
}
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
pa_xfree(u->device_name);
pa_xfree(u);
snd_config_update_free_global();
+
+ pa_alsa_redirect_errors_dec();
}
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index d840cac3..1cc467d9 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -32,6 +30,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
@@ -47,25 +46,55 @@
#include <pulsecore/core-error.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
#include "alsa-util.h"
#include "module-alsa-source-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source_name=<name for the source> "
"device=<ALSA device> "
+ "device_id=<ALSA card index> "
"format=<sample format> "
- "channels=<number of channels> "
"rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map> "
- "mmap=<enable memory mapping?>")
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<upper fill watermark> "
+ "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
+
+static const char* const valid_modargs[] = {
+ "source_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "mixer_reset",
+ NULL
+};
#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
struct userdata {
pa_core *core;
@@ -82,241 +111,364 @@ struct userdata {
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
+ long hw_dB_max, hw_dB_min;
+ pa_bool_t hw_dB_supported;
- size_t frame_size, fragment_size, hwbuf_size;
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
unsigned nfragments;
char *device_name;
- int use_mmap;
+ pa_bool_t use_mmap, use_tsched;
pa_rtpoll_item *alsa_rtpoll_item;
-};
-static const char* const valid_modargs[] = {
- "device",
- "source_name",
- "channels",
- "rate",
- "format",
- "fragments",
- "fragment_size",
- "channel_map",
- "mmap",
- NULL
+ snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+ pa_smoother *smoother;
+ int64_t frame_index;
+
+ snd_pcm_sframes_t hwbuf_unused_frames;
};
-static int mmap_read(struct userdata *u) {
+static void fix_tsched_watermark(struct userdata *u) {
+ size_t max_use;
+ size_t min_sleep, min_wakeup;
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+ min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
+ min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+
+ if (min_sleep > max_use/2)
+ min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec);
+ if (min_sleep < u->frame_size)
+ min_sleep = u->frame_size;
+
+ if (min_wakeup > max_use/2)
+ min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec);
+ if (min_wakeup < u->frame_size)
+ min_wakeup = u->frame_size;
+
+ if (u->tsched_watermark > max_use-min_sleep)
+ u->tsched_watermark = max_use-min_sleep;
+
+ if (u->tsched_watermark < min_wakeup)
+ u->tsched_watermark = min_wakeup;
+}
+
+static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+ pa_usec_t wm, usec;
+
+ pa_assert(u);
+
+ usec = pa_source_get_requested_latency_within_thread(u->source);
+
+ if (usec == (pa_usec_t) -1)
+ usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
+
+/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+ wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+
+ if (usec >= wm) {
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
+ } else
+ *process_usec = *sleep_usec = usec /= 2;
+
+/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+
+ return usec;
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+ pa_assert(u);
+ pa_assert(call);
+ pa_assert(err < 0);
+
+ pa_log_debug("%s: %s", call, snd_strerror(err));
+
+ pa_assert(err != -EAGAIN);
+
+ if (err == -EPIPE)
+ pa_log_debug("%s: Buffer overrun!", call);
+
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+ snd_pcm_start(u->pcm_handle);
+ return 0;
+ }
+
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
+}
+
+static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
+ size_t left_to_record;
+
+ if (n*u->frame_size < u->hwbuf_size)
+ left_to_record = u->hwbuf_size - (n*u->frame_size);
+ else
+ left_to_record = 0;
+
+ if (left_to_record > 0) {
+/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
+ } else {
+ pa_log_info("Overrun!");
+
+ if (u->use_tsched) {
+ size_t old_watermark = u->tsched_watermark;
+
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
+
+ if (old_watermark != u->tsched_watermark)
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+ }
+ }
+
+ return left_to_record;
+}
+
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_record;
pa_assert(u);
pa_source_assert_ref(u->source);
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
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;
+ int r;
- if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+ snd_pcm_hwsync(u->pcm_handle);
- if (n == -EPIPE)
- pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
- if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0)
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
continue;
- if (err == -EAGAIN)
- return work_done;
-
- pa_log("snd_pcm_avail_update: %s", snd_strerror(err));
- return -1;
+ return r;
}
-/* pa_log("Got request for %i samples", (int) n); */
+ left_to_record = check_left_to_record(u, n);
- if (n <= 0)
- return work_done;
+ if (u->use_tsched)
+ if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
- frames = n;
+ if (PA_UNLIKELY(n <= 0))
+ break;
- if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) {
+ for (;;) {
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+ pa_memchunk chunk;
+ void *p;
- if (err == -EPIPE)
- pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
+/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
- continue;
+ if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
- if (err == -EAGAIN)
- return work_done;
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+ continue;
- pa_log("Failed to write data to DSP: %s", snd_strerror(err));
- return -1;
- }
+ return r;
+ }
- /* Check these are multiples of 8 bit */
- pa_assert((areas[0].first & 7) == 0);
- pa_assert((areas[0].step & 7)== 0);
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
- /* We assume a single interleaved memory buffer */
- pa_assert((areas[0].first >> 3) == 0);
- pa_assert((areas[0].step >> 3) == u->frame_size);
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == 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;
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
- pa_source_post(u->source, &chunk);
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
- /* FIXME: Maybe we can do something to keep this memory block
- * a little bit longer around? */
- pa_memblock_unref_fixed(chunk.memblock);
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref_fixed(chunk.memblock);
- if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) {
+ if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
- if (err == -EPIPE)
- pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+ continue;
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
- continue;
+ return r;
+ }
- if (err == -EAGAIN)
- return work_done;
+ work_done = 1;
- pa_log("Failed to write data to DSP: %s", snd_strerror(err));
- return -1;
- }
+ u->frame_index += frames;
- work_done = 1;
+/* pa_log_debug("read %lu frames", (unsigned long) frames); */
-/* pa_log("wrote %i samples", (int) frames); */
+ if (frames >= (snd_pcm_uframes_t) n)
+ break;
+
+ n -= frames;
+ }
}
+
+ *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+ return work_done;
}
-static int unix_read(struct userdata *u) {
- snd_pcm_status_t *status;
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
int work_done = 0;
-
- snd_pcm_status_alloca(&status);
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_record;
pa_assert(u);
pa_source_assert_ref(u->source);
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
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;
+ snd_pcm_sframes_t n;
+ int r;
+
+ snd_pcm_hwsync(u->pcm_handle);
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+ continue;
+
+ return r;
}
- if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)
- pa_log_debug("Buffer overrun!");
+ left_to_record = check_left_to_record(u, n);
- l = snd_pcm_status_get_avail(status) * u->frame_size;
+ if (u->use_tsched)
+ if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
- if (l <= 0)
+ if (PA_UNLIKELY(n <= 0))
return work_done;
- chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+ for (;;) {
+ void *p;
+ snd_pcm_sframes_t frames;
+ pa_memchunk chunk;
- k = pa_memblock_get_length(chunk.memblock);
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
- if (k > l)
- k = l;
+ frames = pa_memblock_get_length(chunk.memblock) / u->frame_size;
- k = (k/u->frame_size)*u->frame_size;
+ if (frames > n)
+ frames = n;
- 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_debug("%lu frames to read", (unsigned long) n); */
-/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */
+ p = pa_memblock_acquire(chunk.memblock);
+ frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames);
+ pa_memblock_release(chunk.memblock);
- pa_assert(t != 0);
+ pa_assert(frames != 0);
- if (t < 0) {
- pa_memblock_unref(chunk.memblock);
+ if (PA_UNLIKELY(frames < 0)) {
+ pa_memblock_unref(chunk.memblock);
- if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)
- continue;
+ if ((r = try_recover(u, "snd_pcm_readi", n)) == 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;
+ return r;
}
+
+ chunk.index = 0;
+ chunk.length = frames * u->frame_size;
+
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ work_done = 1;
+
+ u->frame_index += frames;
+
+/* pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+ if (frames >= n)
+ break;
+
+ n -= frames;
}
+ }
- chunk.index = 0;
- chunk.length = t * u->frame_size;
+ *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+ return work_done;
+}
- pa_source_post(u->source, &chunk);
- pa_memblock_unref(chunk.memblock);
+static void update_smoother(struct userdata *u) {
+ snd_pcm_sframes_t delay = 0;
+ int64_t frames;
+ int err;
+ pa_usec_t now1, now2;
- work_done = 1;
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
- if (t * u->frame_size >= (unsigned) l)
- return work_done;
+ /* Let's update the time smoother */
+
+ snd_pcm_hwsync(u->pcm_handle);
+ snd_pcm_avail_update(u->pcm_handle);
+
+ if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+ pa_log_warn("Failed to get delay: %s", snd_strerror(err));
+ return;
}
+
+ frames = u->frame_index + delay;
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
+
+ pa_smoother_put(u->smoother, now1, now2);
}
static pa_usec_t source_get_latency(struct userdata *u) {
pa_usec_t r = 0;
- snd_pcm_status_t *status;
- snd_pcm_sframes_t frames = 0;
- int err;
-
- snd_pcm_status_alloca(&status);
+ int64_t delay;
+ pa_usec_t now1, now2;
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);
+ now1 = pa_rtclock_usec();
+ now2 = pa_smoother_get(u->smoother, now1);
- if (frames > 0)
- r = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
+ delay = (int64_t) now2 - pa_bytes_to_usec(u->frame_index * u->frame_size, &u->source->sample_spec);
+
+ if (delay > 0)
+ r = (pa_usec_t) delay;
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));
+ if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
return -1;
- }
return 0;
}
@@ -325,6 +477,8 @@ static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
/* Let's suspend */
snd_pcm_close(u->pcm_handle);
u->pcm_handle = NULL;
@@ -339,9 +493,63 @@ static int suspend(struct userdata *u) {
return 0;
}
+static int update_sw_params(struct userdata *u) {
+ snd_pcm_uframes_t avail_min;
+ int err;
+
+ pa_assert(u);
+
+ /* Use the full buffer if noone asked us for anything specific */
+ u->hwbuf_unused_frames = 0;
+
+ if (u->use_tsched) {
+ pa_usec_t latency;
+
+ if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {
+ size_t b;
+
+ pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+ b = pa_usec_to_bytes(latency, &u->source->sample_spec);
+
+ /* We need at least one sample in our buffer */
+
+ if (PA_UNLIKELY(b < u->frame_size))
+ b = u->frame_size;
+
+ u->hwbuf_unused_frames =
+ PA_LIKELY(b < u->hwbuf_size) ?
+ ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+ fix_tsched_watermark(u);
+ }
+ }
+
+ pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+
+ avail_min = 1;
+
+ if (u->use_tsched) {
+ pa_usec_t sleep_usec, process_usec;
+
+ hw_sleep_time(u, &sleep_usec, &process_usec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+ }
+
+ pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ return err;
+ }
+
+ return 0;
+}
+
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
- int err, b;
+ int err;
+ pa_bool_t b, d;
unsigned nfrags;
snd_pcm_uframes_t period_size;
@@ -360,13 +568,14 @@ static int unsuspend(struct userdata *u) {
nfrags = u->nfragments;
period_size = u->fragment_size / u->frame_size;
b = u->use_mmap;
+ d = u->use_tsched;
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
goto fail;
}
- if (b != u->use_mmap) {
+ if (b != u->use_mmap || d != u->use_tsched) {
pa_log_warn("Resume failed, couldn't get original access mode.");
goto fail;
}
@@ -381,18 +590,17 @@ static int unsuspend(struct userdata *u) {
goto fail;
}
- if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
- pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ if (update_sw_params(u) < 0)
goto fail;
- }
if (build_pollfd(u) < 0)
goto fail;
- snd_pcm_start(u->pcm_handle);
-
/* FIXME: We need to reload the volume somehow */
+ snd_pcm_start(u->pcm_handle);
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+
pa_log_info("Resumed successfully...");
return 0;
@@ -427,7 +635,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED:
- pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if (suspend(u) < 0)
return -1;
@@ -488,18 +696,24 @@ static int source_get_volume_cb(pa_source *s) {
pa_assert(u->mixer_elem);
for (i = 0; i < s->sample_spec.channels; i++) {
- long set_vol, vol;
+ long alsa_vol;
- pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i]));
- if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, i, &vol)) < 0)
- goto fail;
+ if (u->hw_dB_supported) {
+
+ if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+ continue;
+ }
- set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ u->hw_dB_supported = FALSE;
+ }
+
+ if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
+ goto fail;
- /* Try to avoid superfluous volume changes */
- if (set_vol != vol)
- 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));
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
@@ -507,8 +721,6 @@ static int source_get_volume_cb(pa_source *s) {
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
- s->get_volume = NULL;
- s->set_volume = NULL;
return -1;
}
@@ -524,17 +736,34 @@ static int source_set_volume_cb(pa_source *s) {
long alsa_vol;
pa_volume_t vol;
- pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i]));
+
+ vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
+
+ if (u->hw_dB_supported) {
+ alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
- vol = s->volume.values[i];
- if (vol > PA_VOLUME_NORM)
- vol = PA_VOLUME_NORM;
+ if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+ if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+ continue;
+ }
+
+ u->hw_dB_supported = FALSE;
+ }
alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
- if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, alsa_vol)) < 0)
+ if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
+
+ if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
@@ -542,8 +771,6 @@ static int source_set_volume_cb(pa_source *s) {
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_volume = NULL;
- s->set_volume = NULL;
return -1;
}
@@ -556,9 +783,6 @@ static int source_get_mute_cb(pa_source *s) {
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_mute = NULL;
- s->set_mute = NULL;
return -1;
}
@@ -576,15 +800,22 @@ static int source_set_mute_cb(pa_source *s) {
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_mute = NULL;
- s->set_mute = NULL;
return -1;
}
return 0;
}
+static void source_update_requested_latency_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ if (!u->pcm_handle)
+ return;
+
+ update_sw_params(u);
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
@@ -592,8 +823,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -601,18 +832,47 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
+/* pa_log_debug("loop"); */
+
/* Read some data and pass it to the sources */
- if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+ int work_done = 0;
+ pa_usec_t sleep_usec;
- if (u->use_mmap) {
- if (mmap_read(u) < 0)
- goto fail;
+ if (u->use_mmap)
+ work_done = mmap_read(u, &sleep_usec);
+ else
+ work_done = unix_read(u, &sleep_usec);
- } else {
- if (unix_read(u) < 0)
- goto fail;
+ if (work_done < 0)
+ goto fail;
+
+/* pa_log_debug("work_done = %i", work_done); */
+
+ if (work_done)
+ update_smoother(u);
+
+ if (u->use_tsched) {
+ pa_usec_t cusec;
+
+ /* OK, the capture buffer is now empty, let's
+ * calculate when to wake up next */
+
+/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+ /* Convert from the sound card time domain to the
+ * system time domain */
+ cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+ /* We don't trust the conversion, so we wake up whatever comes first */
+ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
}
- }
+ } else if (u->use_tsched)
+
+ /* OK, we're in an invalid state, let's disable our timers */
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
@@ -622,7 +882,7 @@ static void thread_func(void *userdata) {
goto finish;
/* Tell ALSA about this and process its response */
- if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
struct pollfd *pollfd;
unsigned short revents = 0;
int err;
@@ -636,43 +896,14 @@ static void thread_func(void *userdata) {
}
if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+ if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+ goto fail;
- 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;
- }
+ snd_pcm_start(u->pcm_handle);
}
+
+ if (revents && u->use_tsched)
+ pa_log_debug("Wakeup from ALSA! (%i)", revents);
}
}
@@ -690,24 +921,26 @@ int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u = NULL;
- char *dev;
+ const char *dev_id;
pa_sample_spec ss;
pa_channel_map map;
- unsigned nfrags, frag_size;
- snd_pcm_uframes_t period_size;
+ uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+ snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- char *t;
const char *name;
char *name_buf = NULL;
- int namereg_fail;
- int use_mmap = 1, b;
+ pa_bool_t namereg_fail;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+ pa_source_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
+ pa_alsa_redirect_errors_inc();
+
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
@@ -722,115 +955,173 @@ int pa__init(pa_module*m) {
frame_size = pa_frame_size(&ss);
nfrags = m->core->default_n_fragments;
- frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
if (frag_size <= 0)
frag_size = frame_size;
+ tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+ tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
- if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) {
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+ pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
pa_log("Failed to parse buffer metrics");
goto fail;
}
- period_size = frag_size/frame_size;
+
+ hwbuf_size = frag_size * nfrags;
+ period_frames = frag_size/frame_size;
+ tsched_frames = tsched_size/frame_size;
if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
pa_log("Failed to parse mmap argument.");
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ pa_log("Failed to parse timer_scheduling argument.");
+ goto fail;
+ }
+
+ if (use_tsched && !pa_rtclock_hrtimer()) {
+ pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+ use_tsched = FALSE;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+ pa_log("Failed to parse mixer_reset argument.");
+ goto fail;
+ }
+
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->use_mmap = use_mmap;
- pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->use_tsched = use_tsched;
u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->alsa_rtpoll_item = NULL;
- pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+
+ u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
snd_config_update_free_global();
- dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+ b = use_mmap;
+ d = use_tsched;
- for (;;) {
+ if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
- 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);
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
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;
- }
- }
+ } else {
- pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
- pa_xfree(dev);
+ if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
goto fail;
- }
-
- break;
}
- u->device_name = dev;
+ pa_assert(u->device_name);
+ pa_log_info("Successfully opened device %s.", u->device_name);
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;
+ u->use_mmap = use_mmap = FALSE;
+ }
+
+ if (use_tsched && (!b || !d)) {
+ pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ u->use_tsched = use_tsched = FALSE;
}
if (u->use_mmap)
pa_log_info("Successfully enabled mmap() mode.");
+ if (u->use_tsched)
+ pa_log_info("Successfully enabled timer-based scheduling mode.");
+
if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
pa_log("Error fetching PCM info: %s", snd_strerror(err));
goto fail;
}
- 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)
pa_log("Error opening mixer: %s", snd_strerror(err));
else {
+ pa_bool_t found = FALSE;
+
+ if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+ found = TRUE;
+ else {
+ snd_pcm_info_t* info;
+
+ snd_pcm_info_alloca(&info);
+
+ if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+ char *md;
+ int card;
+
+ if ((card = snd_pcm_info_get_card(info)) >= 0) {
+
+ md = pa_sprintf_malloc("hw:%i", card);
+
+ if (strcmp(u->device_name, md))
+ if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+ found = TRUE;
+ pa_xfree(md);
+ }
+ }
+ }
- if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
- !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", NULL))) {
+ if (found)
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic")))
+ found = FALSE;
+
+ if (!found) {
snd_mixer_close(u->mixer_handle);
u->mixer_handle = NULL;
}
}
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
- namereg_fail = 1;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("alsa_input.%s", dev);
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
+ namereg_fail = FALSE;
}
- u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, name);
+ data.namereg_fail = namereg_fail;
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+
+ pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
pa_xfree(name_buf);
if (!u->source) {
@@ -839,54 +1130,110 @@ int pa__init(pa_module*m) {
}
u->source->parent.process_msg = source_process_msg;
+ u->source->update_requested_latency = source_update_requested_latency_cb;
u->source->userdata = u;
- 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->fragment_size = frag_size = period_frames * frame_size;
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
+ u->hwbuf_unused_frames = 0;
+ u->tsched_watermark = tsched_watermark;
+ u->frame_index = 0;
+ u->hw_dB_supported = FALSE;
+ u->hw_dB_min = u->hw_dB_max = 0;
+ u->hw_volume_min = u->hw_volume_max = 0;
+
+ if (use_tsched)
+ fix_tsched_watermark(u);
+
+ pa_source_set_latency_range(u->source,
+ !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+ pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+ pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+ nfrags, (long unsigned) u->fragment_size,
+ (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+ if (use_tsched)
+ pa_log_info("Time scheduling watermark is %0.2fms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
- pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size);
+ if (update_sw_params(u) < 0)
+ goto fail;
if (u->mixer_handle) {
pa_assert(u->mixer_elem);
- if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
- int i;
+ if (snd_mixer_selem_has_capture_volume(u->mixer_elem))
+ if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 &&
+ snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
- for (i = 0;i < ss.channels;i++) {
- if (!snd_mixer_selem_has_capture_channel(u->mixer_elem, i))
- break;
- }
+ pa_bool_t suitable = TRUE;
+
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+ if (u->hw_volume_min > u->hw_volume_max) {
+
+ pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+ suitable = FALSE;
+
+ } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+
+ } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+ /* Let's see if this thing actually is useful for muting */
+ if (u->hw_dB_min > -6000) {
+ pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+ suitable = FALSE;
+ } else if (u->hw_dB_max < 0) {
+
+ pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+ pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else
+ u->hw_dB_supported = TRUE;
+ }
+
+ if (suitable) {
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ } else if (mixer_reset) {
+ pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+ pa_alsa_0dB_capture(u->mixer_elem);
+ } else
+ pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
- if (i == ss.channels) {
- 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_mute = source_get_mute_cb;
u->source->set_mute = source_set_mute_cb;
+ u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
}
u->mixer_fdl = pa_alsa_fdlist_new();
if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
- pa_log("failed to initialise file descriptor monitoring");
+ pa_log("Failed to initialize file descriptor monitoring");
goto fail;
}
@@ -895,15 +1242,28 @@ int pa__init(pa_module*m) {
} else
u->mixer_fdl = NULL;
+ pa_alsa_dump(u->pcm_handle);
+
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_volume)
- u->source->get_volume(u->source);
- if (u->source->get_mute)
- u->source->get_mute(u->source);
+ if (data.volume_is_set) {
+ if (u->source->set_volume)
+ u->source->set_volume(u->source);
+ } else {
+ if (u->source->get_volume)
+ u->source->get_volume(u->source);
+ }
+
+ if (data.muted_is_set) {
+ if (u->source->set_mute)
+ u->source->set_mute(u->source);
+ } else {
+ if (u->source->get_mute)
+ u->source->get_mute(u->source);
+ }
pa_source_put(u->source);
@@ -926,8 +1286,10 @@ void pa__done(pa_module*m) {
pa_assert(m);
- if (!(u = m->userdata))
+ if (!(u = m->userdata)) {
+ pa_alsa_redirect_errors_dec();
return;
+ }
if (u->source)
pa_source_unlink(u->source);
@@ -959,8 +1321,12 @@ void pa__done(pa_module*m) {
snd_pcm_close(u->pcm_handle);
}
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
pa_xfree(u->device_name);
pa_xfree(u);
snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
}
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
new file mode 100644
index 00000000..8b67a36d
--- /dev/null
+++ b/src/modules/module-always-sink.c
@@ -0,0 +1,178 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-always-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Always keeps at least one sink loaded even if it's a null one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "sink_name=<name of sink>");
+
+#define DEFAULT_SINK_NAME "auto_null"
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ NULL,
+};
+
+struct userdata {
+ pa_hook_slot *put_slot, *unlink_slot;
+ pa_module* null_module;
+ pa_bool_t ignore;
+ char *sink_name;
+};
+
+static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) {
+ pa_sink *target;
+ uint32_t idx;
+ char *t;
+
+ pa_assert(c);
+ pa_assert(u);
+ pa_assert(!u->null_module);
+
+ /* Loop through all sinks and check to see if we have *any*
+ * sinks. Ignore the sink passed in (if it's not null) */
+ for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
+ if (!sink || target != sink)
+ break;
+
+ if (target)
+ return;
+
+ pa_log_debug("Autoloading null-sink as no other sinks detected.");
+
+ u->ignore = TRUE;
+
+ t = pa_sprintf_malloc("sink_name=%s", u->sink_name);
+ u->null_module = pa_module_load(c, "module-null-sink", t);
+ pa_xfree(t);
+
+ u->ignore = FALSE;
+
+ if (!u->null_module)
+ pa_log_warn("Unable to load module-null-sink");
+}
+
+static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+
+ /* This is us detecting ourselves on load... just ignore this. */
+ if (u->ignore)
+ return PA_HOOK_OK;
+
+ /* Auto-loaded null-sink not active, so ignoring newly detected sink. */
+ if (!u->null_module)
+ return PA_HOOK_OK;
+
+ /* This is us detecting ourselves on load in a different way... just ignore this too. */
+ if (sink->module == u->null_module)
+ return PA_HOOK_OK;
+
+ pa_log_info("A new sink has been discovered. Unloading null-sink.");
+
+ pa_module_unload_request(u->null_module);
+ u->null_module = NULL;
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+
+ /* First check to see if it's our own null-sink that's been removed... */
+ if (u->null_module && sink->module == u->null_module) {
+ pa_log_debug("Autoloaded null-sink removed");
+ u->null_module = NULL;
+ return PA_HOOK_OK;
+ }
+
+ load_null_sink_if_needed(c, sink, u);
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ return -1;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ u->put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
+ u->unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
+ u->null_module = NULL;
+ u->ignore = FALSE;
+
+ pa_modargs_free(ma);
+
+ load_null_sink_if_needed(m->core, NULL, u);
+
+ return 0;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->put_slot)
+ pa_hook_slot_free(u->put_slot);
+ if (u->unlink_slot)
+ pa_hook_slot_free(u->unlink_slot);
+ if (u->null_module)
+ pa_module_unload_request(u->null_module);
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-bt-proximity.c b/src/modules/module-bt-proximity.c
new file mode 100644
index 00000000..77b95868
--- /dev/null
+++ b/src/modules/module-bt-proximity.c
@@ -0,0 +1,490 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2005-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 <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "dbus-util.h"
+#include "module-bt-proximity-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "sink=<sink name> "
+ "hci=<hci device> "
+);
+
+#define DEFAULT_HCI "hci0"
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "rssi",
+ "hci",
+ NULL,
+};
+
+struct bonding {
+ struct userdata *userdata;
+ char address[18];
+
+ pid_t pid;
+ int fd;
+
+ pa_io_event *io_event;
+
+ enum {
+ UNKNOWN,
+ FOUND,
+ NOT_FOUND
+ } state;
+};
+
+struct userdata {
+ pa_module *module;
+ pa_dbus_connection *dbus_connection;
+
+ char *sink_name;
+ char *hci, *hci_path;
+
+ pa_hashmap *bondings;
+
+ unsigned n_found;
+ unsigned n_unknown;
+
+ pa_bool_t muted;
+};
+
+static void update_volume(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->muted && u->n_found > 0) {
+ pa_sink *s;
+
+ u->muted = FALSE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("Found %u BT devices, unmuting.", u->n_found);
+ pa_sink_set_mute(s, FALSE);
+
+ } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
+ pa_sink *s;
+
+ u->muted = TRUE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("No BT devices found, muting.");
+ pa_sink_set_mute(s, TRUE);
+
+ } else
+ pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
+}
+
+static void bonding_free(struct bonding *b) {
+ pa_assert(b);
+
+ if (b->state == FOUND)
+ pa_assert_se(b->userdata->n_found-- >= 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (b->pid != (pid_t) -1) {
+ kill(b->pid, SIGTERM);
+ waitpid(b->pid, NULL, 0);
+ }
+
+ if (b->fd >= 0)
+ pa_close(b->fd);
+
+ if (b->io_event)
+ b->userdata->module->core->mainloop->io_free(b->io_event);
+
+ pa_xfree(b);
+}
+
+static void io_event_cb(
+ pa_mainloop_api*a,
+ pa_io_event* e,
+ int fd,
+ pa_io_event_flags_t events,
+ void *userdata) {
+
+ struct bonding *b = userdata;
+ char x;
+ ssize_t r;
+
+ pa_assert(b);
+
+ if ((r = read(fd, &x, 1)) <= 0) {
+ pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
+
+ pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
+ bonding_free(b);
+ return;
+ }
+
+ pa_assert_se(r == 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (x == '+') {
+ pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
+
+ b->state = FOUND;
+ b->userdata->n_found++;
+
+ pa_log_info("Device '%s' is alive.", b->address);
+
+ } else {
+ pa_assert(x == '-');
+ pa_assert(b->state == UNKNOWN || b->state == FOUND);
+
+ if (b->state == FOUND)
+ b->userdata->n_found--;
+
+ b->state = NOT_FOUND;
+
+ pa_log_info("Device '%s' is dead.", b->address);
+ }
+
+ update_volume(b->userdata);
+}
+
+static struct bonding* bonding_new(struct userdata *u, const char *a) {
+ struct bonding *b = NULL;
+ DBusMessage *m = NULL, *r = NULL;
+ DBusError e;
+ const char *class;
+
+ pa_assert(u);
+ pa_assert(a);
+
+ pa_return_val_if_fail(strlen(a) == 17, NULL);
+ pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
+
+ if (!r) {
+ pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
+ goto fail;
+ }
+
+ if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
+ goto fail;
+ }
+
+ if (strcmp(class, "phone")) {
+ pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
+ goto fail;
+ }
+
+ b = pa_xnew(struct bonding, 1);
+ b->userdata = u;
+ pa_strlcpy(b->address, a, sizeof(b->address));
+ b->pid = (pid_t) -1;
+ b->fd = -1;
+ b->io_event = NULL;
+ b->state = UNKNOWN;
+ u->n_unknown ++;
+
+ pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
+
+ if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
+ pa_log("Failed to start helper tool.");
+ goto fail;
+ }
+
+ b->io_event = u->module->core->mainloop->io_new(
+ u->module->core->mainloop,
+ b->fd,
+ PA_IO_EVENT_INPUT,
+ io_event_cb,
+ b);
+
+ dbus_message_unref(m);
+ dbus_message_unref(r);
+
+ pa_hashmap_put(u->bondings, b->address, b);
+
+ return b;
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+
+ if (b)
+ bonding_free(b);
+
+ dbus_error_free(&e);
+ return NULL;
+}
+
+static void bonding_remove(struct userdata *u, const char *a) {
+ struct bonding *b;
+ pa_assert(u);
+
+ pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
+
+ pa_log_info("No longer watching device '%s'", b->address);
+ bonding_free(b);
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError e;
+
+ dbus_error_init(&e);
+
+ if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_new(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
+
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_remove(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+finish:
+
+ dbus_error_free(&e);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int add_matches(struct userdata *u, pa_bool_t add) {
+ char *filter1, *filter2;
+ DBusError e;
+ int r = -1;
+
+ pa_assert(u);
+ dbus_error_init(&e);
+
+ filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
+ filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+
+ if (add)
+ pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
+ else
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
+
+ r = 0;
+
+finish:
+ pa_xfree(filter1);
+ pa_xfree(filter2);
+ dbus_error_free(&e);
+
+ return r;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ DBusError e;
+ DBusMessage *msg = NULL, *r = NULL;
+ DBusMessageIter iter, sub;
+
+ pa_assert(m);
+ dbus_error_init(&e);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
+ u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
+ u->n_found = u->n_unknown = 0;
+ u->muted = FALSE;
+
+ u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
+ pa_log("Failed to get D-Bus connection: %s", e.message);
+ goto fail;
+ }
+
+ if (add_matches(u, TRUE) < 0)
+ goto fail;
+
+ pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
+
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
+ pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
+ goto fail;
+ }
+
+ dbus_message_iter_init(r, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ const char *a = NULL;
+
+ dbus_message_iter_get_basic(&sub, &a);
+ bonding_new(u, a);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ dbus_message_unref(r);
+ dbus_message_unref(msg);
+
+ pa_modargs_free(ma);
+
+ if (pa_hashmap_size(u->bondings) == 0)
+ pa_log_warn("Warning: no phone device bonded.");
+
+ update_volume(u);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ dbus_error_free(&e);
+
+ if (msg)
+ dbus_message_unref(msg);
+
+ if (r)
+ dbus_message_unref(r);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->bondings) {
+ struct bonding *b;
+
+ while ((b = pa_hashmap_steal_first(u->bondings)))
+ bonding_free(b);
+
+ pa_hashmap_free(u->bondings, NULL, NULL);
+ }
+
+ if (u->dbus_connection) {
+ add_matches(u, FALSE);
+ pa_dbus_connection_unref(u->dbus_connection);
+ }
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u->hci_path);
+ pa_xfree(u->hci);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
index 84125214..df7783fa 100644
--- a/src/modules/module-cli.c
+++ b/src/modules/module-cli.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,10 +36,11 @@
#include "module-cli-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Command line interface")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Command line interface");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>");
static const char* const valid_modargs[] = {
"exit_on_eof",
@@ -69,7 +68,7 @@ static void eof_and_exit_cb(pa_cli*c, void *userdata) {
int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
- int exit_on_eof = 0;
+ pa_bool_t exit_on_eof = FALSE;
pa_assert(m);
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 665bf9dd..cef7a99a 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -47,31 +45,34 @@
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/time-smoother.h>
#include "module-combine-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Combine multiple sinks to one")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Combine multiple sinks to one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
- "master=<master sink> "
"slaves=<slave sinks> "
"adjust_time=<seconds> "
"resample_method=<method> "
"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 MEMBLOCKQ_MAXLENGTH (1024*1024*16)
#define DEFAULT_ADJUST_TIME 10
+#define REQUEST_LATENCY_USEC (PA_USEC_PER_MSEC * 200)
+
static const char* const valid_modargs[] = {
"sink_name",
- "master",
"slaves",
"adjust_time",
"resample_method",
@@ -90,12 +91,15 @@ struct output {
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_rtpoll_item *inq_rtpoll_item_read, *inq_rtpoll_item_write;
+ pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write;
pa_memblockq *memblockq;
pa_usec_t total_latency;
+ pa_atomic_t max_request;
+
PA_LLIST_FIELDS(struct output);
};
@@ -112,45 +116,48 @@ struct userdata {
uint32_t adjust_time;
pa_bool_t automatic;
- size_t block_size;
- pa_hook_slot *sink_new_slot, *sink_unlink_slot, *sink_state_changed_slot;
+ pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
pa_resample_method_t resample_method;
struct timeval adjust_timestamp;
- struct output *master;
+ pa_usec_t block_usec;
+
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_usec_t timestamp;
pa_bool_t in_null_mode;
+ pa_smoother *smoother;
+ uint64_t counter;
} thread_info;
};
enum {
SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX,
SINK_MESSAGE_REMOVE_OUTPUT,
- SINK_MESSAGE_NEED
+ SINK_MESSAGE_NEED,
+ SINK_MESSAGE_UPDATE_LATENCY,
+ SINK_MESSAGE_UPDATE_MAX_REQUEST
};
enum {
- SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX
+ 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;
+ pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency, avg_total_latency = 0;
uint32_t base_rate;
uint32_t idx;
+ unsigned n = 0;
pa_assert(u);
pa_sink_assert_ref(u->sink);
@@ -158,42 +165,44 @@ static void adjust_rates(struct userdata *u) {
if (pa_idxset_size(u->outputs) <= 0)
return;
- if (!u->master)
- return;
-
- if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
+ if (!PA_SINK_IS_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)))
+ if (!o->sink_input || !PA_SINK_IS_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);
+ o->total_latency = pa_sink_input_get_latency(o->sink_input, &sink_latency);
+ o->total_latency += sink_latency;
if (sink_latency > max_sink_latency)
max_sink_latency = sink_latency;
if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency)
min_total_latency = o->total_latency;
+
+ avg_total_latency += o->total_latency;
+ n++;
}
if (min_total_latency == (pa_usec_t) -1)
return;
+ avg_total_latency /= n;
+
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);
+ pa_log_info("[%s] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC);
+ pa_log_info("[%s] target latency is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC);
base_rate = u->sink->sample_spec.rate;
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)))
+ if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
if (o->total_latency < target_latency)
@@ -202,13 +211,15 @@ static void adjust_rates(struct userdata *u) {
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)) {
- pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->name, base_rate, r);
+ pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
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_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency);
pa_sink_input_set_rate(o->sink_input, r);
}
}
+
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL);
}
static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
@@ -226,6 +237,36 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct time
u->sink->core->mainloop->time_restart(e, &n);
}
+static void process_render_null(struct userdata *u, pa_usec_t now) {
+ size_t ate = 0;
+ pa_assert(u);
+
+ if (u->thread_info.in_null_mode)
+ u->thread_info.timestamp = now;
+
+ while (u->thread_info.timestamp < now + u->block_usec) {
+ pa_memchunk chunk;
+
+ pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ u->thread_info.counter += chunk.length;
+
+/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+ u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+ ate += chunk.length;
+
+ if (ate >= u->sink->thread_info.max_request)
+ break;
+ }
+
+/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+
+ pa_smoother_put(u->thread_info.smoother, now,
+ pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
@@ -233,36 +274,29 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+1);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_rtclock_get(&u->thread_info.timestamp);
+ u->thread_info.timestamp = pa_rtclock_usec();
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 (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) {
+ pa_usec_t 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);
+ now = pa_rtclock_usec();
- if (!u->thread_info.in_null_mode)
- u->thread_info.timestamp = now;
+ if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now)
+ process_render_null(u, 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);
+ 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;
@@ -294,7 +328,7 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length)
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
+ * 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 */
@@ -314,6 +348,8 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length)
/* Render data! */
pa_sink_render(u->sink, length, &chunk);
+ u->thread_info.counter += chunk.length;
+
/* OK, let's send this data to the other threads */
for (j = u->thread_info.active_outputs; j; j = j->next)
@@ -353,27 +389,56 @@ static void request_memblock(struct output *o, size_t length) {
}
/* Called from I/O thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct output *o;
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
/* If necessary, get some new data */
- request_memblock(o, length);
+ request_memblock(o, nbytes);
+
+ if (pa_memblockq_peek(o->memblockq, chunk) < 0)
+ return -1;
+
+ pa_memblockq_drop(o->memblockq, chunk->length);
+ return 0;
+}
- return pa_memblockq_peek(o->memblockq, chunk);
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ pa_assert_se(o = i->userdata);
+
+ pa_memblockq_rewind(o->memblockq, nbytes);
}
/* Called from I/O thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
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);
+ pa_memblockq_set_maxrewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ if (pa_atomic_load(&o->max_request) == (int) nbytes)
+ return;
+
+ pa_atomic_store(&o->max_request, (int) nbytes);
+
+ pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
}
/* Called from I/O thread context */
@@ -384,11 +449,17 @@ static void sink_input_attach_cb(pa_sink_input *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(
+ pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write);
+
+ o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
i->sink->rtpoll,
PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
o->inq);
+
+ o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ i->sink->rtpoll,
+ PA_RTPOLL_EARLY,
+ o->outq);
}
/* Called from I/O thread context */
@@ -399,9 +470,13 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_assert_se(o = i->userdata);
/* 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;
+ pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write);
+
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ o->inq_rtpoll_item_read = NULL;
+
+ pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+ o->outq_rtpoll_item_write = NULL;
}
/* Called from main context */
@@ -415,6 +490,20 @@ static void sink_input_kill_cb(pa_sink_input *i) {
output_free(o);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
/* 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;
@@ -433,12 +522,12 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
case SINK_INPUT_MESSAGE_POST:
- if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
+ if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
pa_memblockq_push_align(o->memblockq, chunk);
else
pa_memblockq_flush(o->memblockq);
- break;
+ return 0;
}
return pa_sink_input_process_msg(obj, code, data, offset, chunk);
@@ -451,11 +540,10 @@ static void disable_output(struct output *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_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
pa_sink_input_unref(o->sink_input);
o->sink_input = NULL;
-
}
/* Called from main context */
@@ -471,7 +559,7 @@ static void enable_output(struct output *o) {
pa_sink_input_put(o->sink_input);
- if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
+ if (o->userdata->sink && PA_SINK_IS_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);
}
}
@@ -487,8 +575,6 @@ static void suspend(struct userdata *u) {
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...");
}
@@ -504,12 +590,10 @@ static void unsuspend(struct userdata *u) {
pa_sink_suspend(o->sink, FALSE);
- if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
enable_output(o);
}
- pick_master(u, NULL);
-
pa_log_info("Resumed successfully...");
}
@@ -525,7 +609,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
switch (state) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+ pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
suspend(u);
break;
@@ -546,7 +630,25 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
return 0;
}
-/* Called from thread context of the master */
+/* Called from IO context */
+static void update_max_request(struct userdata *u) {
+ size_t max_request = 0;
+ struct output *o;
+
+ for (o = u->thread_info.active_outputs; o; o = o->next) {
+ size_t mr = (size_t) pa_atomic_load(&o->max_request);
+
+ if (mr > max_request)
+ max_request = mr;
+ }
+
+ if (max_request <= 0)
+ max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+ pa_sink_set_max_request(u->sink, max_request);
+}
+
+/* Called from thread context of the io thread */
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;
@@ -554,41 +656,47 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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:
+ if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
+ pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec());
+ else
+ pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec());
- /* This code will only be called when running in NULL
- * mode, i.e. when no output is attached. See
- * sink_get_latency_cb() below */
+ break;
- if (u->thread_info.in_null_mode) {
- struct timeval now;
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t x, y, c, *delay = data;
- 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;
- }
- }
+ x = pa_rtclock_usec();
+ y = pa_smoother_get(u->thread_info.smoother, x);
- *((pa_usec_t*) data) = 0;
+ c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
- break;
+ if (y < c)
+ *delay = c - y;
+ else
+ *delay = 0;
+
+ return 0;
+ }
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 */
+ pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write);
- op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
+ op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
op->outq);
+ op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ u->rtpoll,
+ PA_RTPOLL_EARLY,
+ op->inq);
+ update_max_request(u);
return 0;
}
@@ -597,56 +705,48 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_read && op->inq_rtpoll_item_write);
- pa_assert(op->outq_rtpoll_item);
- pa_rtpoll_item_free(op->outq_rtpoll_item);
- op->outq_rtpoll_item = NULL;
+ pa_rtpoll_item_free(op->outq_rtpoll_item_read);
+ op->outq_rtpoll_item_read = NULL;
+ pa_rtpoll_item_free(op->inq_rtpoll_item_write);
+ op->inq_rtpoll_item_write = NULL;
+
+ update_max_request(u);
return 0;
}
case SINK_MESSAGE_NEED:
- render_memblock(u, data, (size_t) offset);
+ render_memblock(u, (struct output*) data, (size_t) offset);
return 0;
- }
-
- return pa_sink_process_msg(o, code, data, offset, chunk);
-}
-/* Called from main context */
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- struct userdata *u;
+ case SINK_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t x, y, latency = (pa_usec_t) offset;
- pa_sink_assert_ref(s);
- pa_assert_se(u = s->userdata);
+ x = pa_rtclock_usec();
+ y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
- if (u->master) {
- /* If we have a master sink, we just return the latency of it
- * and add our own buffering on top */
+ if (y > latency)
+ y -= latency;
+ else
+ y = 0;
- if (!u->master->sink_input)
+ pa_smoother_put(u->thread_info.smoother, x, y);
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;
+ case SINK_MESSAGE_UPDATE_MAX_REQUEST:
- return usec;
+ update_max_request(u);
+ break;
}
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void update_description(struct userdata *u) {
- int first = 1;
+ pa_bool_t first = TRUE;
char *t;
struct output *o;
uint32_t idx;
@@ -664,10 +764,10 @@ static void update_description(struct userdata *u) {
char *e;
if (first) {
- e = pa_sprintf_malloc("%s %s", t, o->sink->description);
- first = 0;
+ e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ first = FALSE;
} else
- e = pa_sprintf_malloc("%s, %s", t, o->sink->description);
+ e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
pa_xfree(t);
t = e;
@@ -677,57 +777,19 @@ static void update_description(struct userdata *u) {
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;
- }
-
- 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;
- }
-
- update_master(u, NULL);
-}
-
static int output_create_sink_input(struct output *o) {
pa_sink_input_new_data data;
- char *t;
pa_assert(o);
if (o->sink_input)
return 0;
- t = pa_sprintf_malloc("Simultaneous output on %s", o->sink->description);
-
pa_sink_input_new_data_init(&data);
data.sink = o->sink;
data.driver = __FILE__;
- data.name = t;
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
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;
@@ -735,25 +797,30 @@ static int output_create_sink_input(struct output *o) {
o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
- pa_xfree(t);
+ pa_sink_input_new_data_done(&data);
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->pop = sink_input_pop_cb;
+ o->sink_input->process_rewind = sink_input_process_rewind_cb;
+ o->sink_input->state_change = sink_input_state_change_cb;
+ o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ o->sink_input->update_max_request = sink_input_update_max_request_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;
+ pa_sink_input_set_requested_latency(o->sink_input, REQUEST_LATENCY_USEC);
return 0;
}
static struct output *output_new(struct userdata *u, pa_sink *sink) {
struct output *o;
+ pa_sink_state_t state;
pa_assert(u);
pa_assert(sink);
@@ -763,8 +830,8 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
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->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL;
+ o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL;
o->sink = sink;
o->sink_input = NULL;
o->memblockq = pa_memblockq_new(
@@ -774,31 +841,39 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
pa_frame_size(&u->sink->sample_spec),
1,
0,
+ 0,
NULL);
+ pa_atomic_store(&o->max_request, 0);
+ PA_LLIST_INIT(struct output, o);
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
- if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
+ state = pa_sink_get_state(u->sink);
+
+ if (state != PA_SINK_INIT)
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);
- o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
+ o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
o->outq);
+ o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ u->rtpoll,
+ PA_RTPOLL_EARLY,
+ o->inq);
}
- if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
+ if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
pa_sink_suspend(sink, FALSE);
- if (PA_SINK_OPENED(pa_sink_get_state(sink)))
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
if (output_create_sink_input(o) < 0)
goto fail;
}
-
update_description(u);
return o;
@@ -828,7 +903,25 @@ fail:
return NULL;
}
-static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) {
+ const char *t;
+
+ pa_sink_assert_ref(s);
+
+ if (!(s->flags & PA_SINK_HARDWARE))
+ return FALSE;
+
+ if (s == u->sink)
+ return FALSE;
+
+ if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
+ if (strcmp(t, "sound"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
pa_core_assert_ref(c);
@@ -836,7 +929,7 @@ static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata
pa_assert(u);
pa_assert(u->automatic);
- if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink)
+ if (!is_suitable_sink(u, s))
return PA_HOOK_OK;
pa_log_info("Configuring new sink: %s", s->name);
@@ -849,27 +942,34 @@ static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata
if (o->sink_input)
pa_sink_input_put(o->sink_input);
- pick_master(u, NULL);
-
return PA_HOOK_OK;
}
-static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+static struct output* find_output(struct userdata *u, pa_sink *s) {
struct output *o;
uint32_t idx;
- pa_assert(c);
- pa_sink_assert_ref(s);
pa_assert(u);
+ pa_assert(s);
- if (s == u->sink)
- return PA_HOOK_OK;
+ if (u->sink == s)
+ return NULL;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
if (o->sink == s)
- break;
+ return o;
+
+ return NULL;
+}
+
+static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+ struct output *o;
+
+ pa_assert(c);
+ pa_sink_assert_ref(s);
+ pa_assert(u);
- if (!o)
+ if (!(o = find_output(u, s)))
return PA_HOOK_OK;
pa_log_info("Unconfiguring sink: %s", s->name);
@@ -881,30 +981,18 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd
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)
+ if (!(o = find_output(u, s)))
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) {
+ if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_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) {
+ if (state == PA_SINK_SUSPENDED && o->sink_input)
disable_output(o);
- pick_master(u, o);
- }
return PA_HOOK_OK;
}
@@ -912,13 +1000,13 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
- const char *master_name, *slaves, *rm;
- pa_sink *master_sink = NULL;
+ const char *slaves, *rm;
int resample_method = PA_RESAMPLER_TRIVIAL;
pa_sample_spec ss;
pa_channel_map map;
struct output *o;
uint32_t idx;
+ pa_sink_new_data data;
pa_assert(m);
@@ -934,109 +1022,85 @@ int pa__init(pa_module*m) {
}
}
- u = pa_xnew(struct userdata, 1);
+ m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->module = m;
- m->userdata = u;
u->sink = NULL;
- u->master = NULL;
u->time_event = NULL;
u->adjust_time = DEFAULT_ADJUST_TIME;
- pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
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;
+ u->sink_put_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);
+ u->thread_info.counter = 0;
+ u->thread_info.smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
pa_log("Failed to parse adjust_time value");
goto fail;
}
- 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_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;
- }
+ u->automatic = !slaves;
+ ss = m->core->default_sample_spec;
- if ((pa_modargs_get_sample_spec(ma, &ss) < 0)) {
+ if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) {
pa_log("Invalid sample specification.");
goto fail;
}
- 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);
+ pa_sink_new_data_init(&data);
+ data.namereg_fail = FALSE;
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter");
- if ((pa_modargs_get_channel_map(ma, NULL, &map) < 0)) {
- pa_log("Invalid channel map.");
- goto fail;
- }
+ if (slaves)
+ pa_proplist_sets(data.proplist, "combine.slaves", slaves);
- if (ss.channels != map.channels) {
- pa_log("Channel map and sample specification don't match.");
- goto fail;
- }
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
- if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ if (!u->sink) {
pa_log("Failed to create sink");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
- u->sink->get_latency = sink_get_latency_cb;
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, "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);
+ pa_sink_set_latency_range(u->sink, REQUEST_LATENCY_USEC, REQUEST_LATENCY_USEC);
+ u->block_usec = u->sink->thread_info.max_latency;
+
+ u->sink->thread_info.max_request =
+ pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
if (!u->automatic) {
const char*split_state;
char *n = NULL;
pa_assert(slaves);
- /* 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;
- }
+ /* The slaves have been specified manually */
split_state = NULL;
while ((n = pa_split(slaves, ",", &split_state))) {
pa_sink *slave_sink;
- if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, 1)) || slave_sink == u->sink) {
+ if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) {
pa_log("Invalid slave sink '%s'", n);
pa_xfree(n);
goto fail;
@@ -1053,17 +1117,16 @@ int pa__init(pa_module*m) {
if (pa_idxset_size(u->outputs) <= 1)
pa_log_warn("No slave sinks specified.");
- u->sink_new_slot = NULL;
+ u->sink_put_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 */
+ /* We're in automatic mode, we add every sink that matches our needs */
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)
+ if (!is_suitable_sink(u, s))
continue;
if (!output_new(u, s)) {
@@ -1072,13 +1135,11 @@ int pa__init(pa_module*m) {
}
}
- 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);
+ u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u);
}
- u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_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);
+ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u);
+ u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
@@ -1116,19 +1177,21 @@ fail:
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->inq_rtpoll_item_read)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ if (o->inq_rtpoll_item_write)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_write);
- if (o->outq_rtpoll_item)
- pa_rtpoll_item_free(o->outq_rtpoll_item);
+ if (o->outq_rtpoll_item_read)
+ pa_rtpoll_item_free(o->outq_rtpoll_item_read);
+ if (o->outq_rtpoll_item_write)
+ pa_rtpoll_item_free(o->outq_rtpoll_item_write);
if (o->inq)
pa_asyncmsgq_unref(o->inq);
@@ -1151,8 +1214,8 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
- if (u->sink_new_slot)
- pa_hook_slot_free(u->sink_new_slot);
+ if (u->sink_put_slot)
+ pa_hook_slot_free(u->sink_put_slot);
if (u->sink_unlink_slot)
pa_hook_slot_free(u->sink_unlink_slot);
@@ -1186,5 +1249,8 @@ void pa__done(pa_module*m) {
if (u->time_event)
u->core->mainloop->time_free(u->time_event);
+ if (u->thread_info.smoother)
+ pa_smoother_free(u->thread_info.smoother);
+
pa_xfree(u);
}
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
new file mode 100644
index 00000000..3adee99e
--- /dev/null
+++ b/src/modules/module-console-kit.c
@@ -0,0 +1,334 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#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 "dbus-util.h"
+#include "module-console-kit-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Create a client for each ConsoleKit session of this user");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct session {
+ char *id;
+ pa_client *client;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_dbus_connection *connection;
+ pa_hashmap *sessions;
+};
+
+static void add_session(struct userdata *u, const char *id) {
+ DBusError error;
+ DBusMessage *m = NULL, *reply = NULL;
+ int32_t uid;
+ struct session *session;
+ char *t;
+
+ dbus_error_init (&error);
+
+ if (pa_hashmap_get(u->sessions, id)) {
+ pa_log_warn("Duplicate session %s, ignoring.", id);
+ return;
+ }
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", id, "org.freedesktop.ConsoleKit.Session", "GetUnixUser"))) {
+ pa_log("Failed to allocate GetUnixUser() method call.");
+ goto fail;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+ pa_log("GetUnixUser() call failed: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ /* FIXME: Why is this in int32? and not an uint32? */
+ if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &uid, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse GetUnixUser() result: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ /* We only care about our own sessions */
+ if ((uid_t) uid != getuid())
+ goto fail;
+
+ session = pa_xnew(struct session, 1);
+ session->id = pa_xstrdup(id);
+
+ t = pa_sprintf_malloc("ConsoleKit Session %s", id);
+ session->client = pa_client_new(u->core, __FILE__, t);
+ pa_xfree(t);
+
+ pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+
+ pa_hashmap_put(u->sessions, session->id, session);
+
+ pa_log_debug("Added new session %s", id);
+
+fail:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+}
+
+static void free_session(struct session *session) {
+ pa_assert(session);
+
+ pa_log_debug("Removing session %s", session->id);
+
+ pa_client_free(session->client);
+ pa_xfree(session->id);
+ pa_xfree(session);
+}
+
+static void remove_session(struct userdata *u, const char *id) {
+ struct session *session;
+
+ if (!(session = pa_hashmap_remove(u->sessions, id)))
+ return;
+
+ free_session(session);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError error;
+ const char *path;
+
+ pa_assert(bus);
+ pa_assert(message);
+ pa_assert(u);
+
+ dbus_error_init(&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.ConsoleKit.Seat", "SessionAdded")) {
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+ pa_log_error("Failed to parse SessionAdded message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ add_session(u, path);
+
+ } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) {
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+ pa_log_error("Failed to parse SessionRemoved message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ remove_session(u, path);
+ }
+
+finish:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static int get_session_list(struct userdata *u) {
+ DBusError error;
+ DBusMessage *m = NULL, *reply = NULL;
+ uint32_t uid;
+ DBusMessageIter iter, sub;
+ int ret = -1;
+
+ pa_assert(u);
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionsForUnixUser"))) {
+ pa_log("Failed to allocate GetSessionsForUnixUser() method call.");
+ goto fail;
+ }
+
+ uid = (uint32_t) getuid();
+ if (!(dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID))) {
+ pa_log("Failed to append arguments to GetSessionsForUnixUser() method call.");
+ goto fail;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+ pa_log("GetSessionsForUnixUser() call failed: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+ pa_log("Failed to parse GetSessionsForUnixUser() result.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ for (;;) {
+ int at;
+ const char *id;
+
+ if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
+ break;
+
+ assert(at == DBUS_TYPE_OBJECT_PATH);
+ dbus_message_iter_get_basic(&sub, &id);
+
+ add_session(u, id);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ ret = 0;
+
+fail:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+int pa__init(pa_module*m) {
+ DBusError error;
+ pa_dbus_connection *connection;
+ struct userdata *u = NULL;
+ pa_modargs *ma;
+
+ pa_assert(m);
+
+ dbus_error_init(&error);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+
+ if (connection)
+ pa_dbus_connection_unref(connection);
+
+ pa_log_error("Unable to contact D-Bus system bus: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->connection = connection;
+ u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ dbus_bus_add_match(pa_dbus_connection_get(connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (get_session_list(u) < 0)
+ goto fail;
+
+ 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_module *m) {
+ struct userdata *u;
+ struct session *session;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sessions) {
+ while ((session = pa_hashmap_steal_first(u->sessions)))
+ free_session(session);
+
+ pa_hashmap_free(u->sessions, NULL, NULL);
+ }
+
+ if (u->connection)
+ pa_dbus_connection_unref(u->connection);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index 71e19e63..2168ac71 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -25,79 +23,179 @@
#include <config.h>
#endif
+#include <errno.h>
+#include <stdio.h>
+
+#include <pulse/timeval.h>
+
#include <pulsecore/core-util.h>
#include <pulsecore/module.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-error.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)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
#define DEFAULT_SINK_FILE "default-sink"
#define DEFAULT_SOURCE_FILE "default-source"
+#define DEFAULT_SAVE_INTERVAL 5
-int pa__init(pa_module *m) {
+struct userdata {
+ pa_core *core;
+ pa_subscription *subscription;
+ pa_time_event *time_event;
+ char *sink_filename, *source_filename;
+ pa_bool_t modified;
+};
+
+static void load(struct userdata *u) {
FILE *f;
/* We never overwrite manually configured settings */
-
- if (m->core->default_sink_name)
+
+ if (u->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"))) {
+ else if ((f = fopen(u->sink_filename, "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);
+ pa_log_info("No previous default sink setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+ pa_log_info("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)
+
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+ if (u->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"))) {
+ else if ((f = fopen(u->source_filename, "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);
+ pa_log_info("No previous default source setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+ pa_log_info("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
+
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+}
+
+static void save(struct userdata *u) {
+ FILE *f;
+
+ if (!u->modified)
+ return;
+
+ if (u->sink_filename) {
+ if ((f = fopen(u->sink_filename, "w"))) {
+ const char *n = pa_namereg_get_default_sink_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
}
+ if (u->source_filename) {
+ if ((f = fopen(u->source_filename, "w"))) {
+ const char *n = pa_namereg_get_default_source_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+ }
+
+ u->modified = FALSE;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+ save(u);
+
+ if (u->time_event) {
+ u->core->mainloop->time_free(u->time_event);
+ u->time_event = NULL;
+ }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ u->modified = TRUE;
+
+ if (!u->time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
+ u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
+ }
+}
+
+int pa__init(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+
+ if (!(u->sink_filename = pa_state_path(DEFAULT_SINK_FILE)))
+ goto fail;
+
+ if (!(u->source_filename = pa_state_path(DEFAULT_SOURCE_FILE)))
+ goto fail;
+
+ load(u);
+
+ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+
return 0;
+
+fail:
+ pa__done(m);
+
+ return -1;
}
void pa__done(pa_module*m) {
- FILE *f;
+ struct userdata *u;
- 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);
- }
+ pa_assert(m);
- 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);
- }
-}
+ if (!(u = m->userdata))
+ return;
+
+ save(u);
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+ if (u->time_event)
+ m->core->mainloop->time_free(u->time_event);
+ pa_xfree(u->sink_filename);
+ pa_xfree(u->source_filename);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index 5bff748e..64ce1928 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -1,4 +1,3 @@
-dnl $Id$
changecom(`/*', `*/')dnl
define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl
define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl
@@ -10,6 +9,7 @@ define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl
#include <pulsecore/core.h>
#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
gen_symbol(pa__init)
gen_symbol(pa__done)
@@ -17,6 +17,7 @@ gen_symbol(pa__get_author)
gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
+gen_symbol(pa__load_once)
int pa__init(pa_module*m);
void pa__done(pa_module*m);
@@ -25,5 +26,6 @@ const char* pa__get_author(void);
const char* pa__get_description(void);
const char* pa__get_usage(void);
const char* pa__get_version(void);
+pa_bool_t pa__load_once(void);
#endif
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 7dc25243..13bcfcd1 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,10 +45,11 @@
#include "module-detect-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("just-one=<boolean>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("just-one=<boolean>");
static const char* const valid_modargs[] = {
"just-one",
@@ -221,7 +220,8 @@ static int detect_waveout(pa_core *c, int just_one) {
#endif
int pa__init(pa_module*m) {
- int just_one = 0, n = 0;
+ pa_bool_t just_one = FALSE;
+ int n = 0;
pa_modargs *ma;
pa_assert(m);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
new file mode 100644
index 00000000..b7a1e1b5
--- /dev/null
+++ b/src/modules/module-device-restore.c
@@ -0,0 +1,348 @@
+/***
+ 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+
+#include "module-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+ NULL,
+};
+
+struct userdata {
+ pa_core *core;
+ pa_subscription *subscription;
+ pa_hook_slot *sink_fixate_hook_slot, *source_fixate_hook_slot;
+ pa_time_event *save_time_event;
+ GDBM_FILE gdbm_file;
+};
+
+struct entry {
+ pa_cvolume volume;
+ int muted;
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ gdbm_sync(u->gdbm_file);
+ pa_log_info("Synced.");
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ struct entry entry;
+ char *name;
+ datum key, data;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+ t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
+ return;
+
+ if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+ pa_sink *sink;
+
+ if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+ return;
+
+ name = pa_sprintf_malloc("sink:%s", sink->name);
+ entry.volume = *pa_sink_get_volume(sink);
+ entry.muted = pa_sink_get_mute(sink);
+
+ } else {
+ pa_source *source;
+
+ pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+ if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+ return;
+
+ name = pa_sprintf_malloc("source:%s", source->name);
+ entry.volume = *pa_source_get_volume(source);
+ entry.muted = pa_source_get_mute(source);
+ }
+
+ key.dptr = name;
+ key.dsize = strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (data.dptr) {
+
+ if (data.dsize == sizeof(struct entry)) {
+ struct entry *old = (struct entry*) data.dptr;
+
+ if (pa_cvolume_valid(&old->volume)) {
+
+ if (pa_cvolume_equal(&old->volume, &entry.volume) &&
+ !old->muted == !entry.muted) {
+
+ pa_xfree(data.dptr);
+ pa_xfree(name);
+ return;
+ }
+ } else
+ pa_log_warn("Invalid volume stored in database for device %s", name);
+
+ } else
+ pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+
+ pa_xfree(data.dptr);
+ }
+
+ data.dptr = (void*) &entry;
+ data.dsize = sizeof(entry);
+
+ pa_log_info("Storing volume/mute for device %s.", name);
+
+ gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+ if (!u->save_time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+ }
+
+ pa_xfree(name);
+}
+
+static struct entry* read_entry(struct userdata *u, char *name) {
+ datum key, data;
+ struct entry *e;
+
+ pa_assert(u);
+ pa_assert(name);
+
+ key.dptr = name;
+ key.dsize = strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (!data.dptr)
+ goto fail;
+
+ if (data.dsize != sizeof(struct entry)) {
+ pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ goto fail;
+ }
+
+ e = (struct entry*) data.dptr;
+
+ if (!(pa_cvolume_valid(&e->volume))) {
+ pa_log_warn("Invalid volume stored in database for device %s", name);
+ goto fail;
+ }
+
+ return e;
+
+fail:
+
+ pa_xfree(data.dptr);
+ return NULL;
+}
+
+
+static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+ if ((e = read_entry(u, name))) {
+
+ if (e->volume.channels == new_data->sample_spec.channels) {
+ pa_log_info("Restoring volume for sink %s.", new_data->name);
+ pa_sink_new_data_set_volume(new_data, &e->volume);
+ }
+
+ pa_log_info("Restoring mute state for sink %s.", new_data->name);
+ pa_sink_new_data_set_muted(new_data, e->muted);
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ name = pa_sprintf_malloc("source:%s", new_data->name);
+
+ if ((e = read_entry(u, name))) {
+
+ if (e->volume.channels == new_data->sample_spec.channels) {
+ pa_log_info("Restoring volume for source %s.", new_data->name);
+ pa_source_new_data_set_volume(new_data, &e->volume);
+ }
+
+ pa_log_info("Restoring mute state for source %s.", new_data->name);
+ pa_source_new_data_set_muted(new_data, e->muted);
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ char *fname, *fn;
+ char hn[256];
+ pa_sink *sink;
+ pa_source *source;
+ uint32_t idx;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->save_time_event = NULL;
+
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+
+ u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
+ u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
+
+ m->userdata = u;
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ goto fail;
+
+ fn = pa_sprintf_malloc("device-volumes.%s.gdbm", hn);
+ fname = pa_state_path(fn);
+ pa_xfree(fn);
+
+ if (!fname)
+ goto fail;
+
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
+ pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+ pa_xfree(fname);
+ goto fail;
+ }
+
+ pa_log_info("Sucessfully opened database file '%s'.", fname);
+ pa_xfree(fname);
+
+ for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+ for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->sink_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_fixate_hook_slot);
+ if (u->source_fixate_hook_slot)
+ pa_hook_slot_free(u->source_fixate_hook_slot);
+
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
+ if (u->gdbm_file)
+ gdbm_close(u->gdbm_file);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
index 49dcf3be..8eb4985b 100644
--- a/src/modules/module-esound-compat-spawnfd.c
+++ b/src/modules/module-esound-compat-spawnfd.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,10 +35,11 @@
#include "module-esound-compat-spawnfd-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation")
-PA_MODULE_USAGE("fd=<file descriptor>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE("fd=<file descriptor>");
static const char* const valid_modargs[] = {
"fd",
@@ -51,13 +50,13 @@ int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1, fd = -1;
char x = 1;
-
+
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;
}
diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c
index 6ad7db7b..67f0a231 100644
--- a/src/modules/module-esound-compat-spawnpid.c
+++ b/src/modules/module-esound-compat-spawnpid.c
@@ -37,10 +37,11 @@
#include "module-esound-compat-spawnpid-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation")
-PA_MODULE_USAGE("pid=<process id>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("pid=<process id>");
static const char* const valid_modargs[] = {
"pid",
@@ -51,7 +52,7 @@ int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1;
uint32_t pid = 0;
-
+
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 8b46637e..e189febd 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -64,15 +62,16 @@
#include "module-esound-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"server=<address> cookie=<filename> "
"format=<sample format> "
"channels=<number of channels> "
- "rate=<sample rate>")
+ "rate=<sample rate>");
#define DEFAULT_SINK_NAME "esound_out"
@@ -142,7 +141,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
@@ -210,7 +209,7 @@ static void thread_func(void *userdata) {
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) {
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
@@ -293,7 +292,7 @@ static void thread_func(void *userdata) {
}
/* Hmm, nothing to do. Let's sleep */
- pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
}
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
@@ -362,7 +361,7 @@ static int do_write(struct userdata *u) {
pa_make_tcp_socket_low_delay(u->fd);
- pa_log_info("Connection authenticated, handing fd to IO thread...");
+ pa_log_debug("Connection authenticated, handing fd to IO thread...");
pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
u->state = STATE_RUNNING;
@@ -496,16 +495,16 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo
u->io = io;
pa_iochannel_set_callback(u->io, io_callback, u);
- pa_log_info("Connection established, authenticating ...");
+ pa_log_debug("Connection established, authenticating ...");
}
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;
+ uint32_t key;
+ pa_sink_new_data data;
pa_assert(m);
@@ -531,13 +530,12 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->fd = -1;
- u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
u->format =
@@ -552,30 +550,38 @@ int pa__init(pa_module*m) {
u->state = STATE_AUTH;
u->latency = 0;
- 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.");
+ if (!(espeaker = getenv("ESPEAKER")))
+ espeaker = ESD_UNIX_SOCKET_NAME;
+
+ espeaker = pa_modargs_get_value(ma, "server", espeaker);
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Esound sink '%s'", espeaker);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- u->sink->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))) {
+ if (!(u->client = pa_socket_client_new_string(u->core->mainloop, 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 */
@@ -584,7 +590,9 @@ int pa__init(pa_module*m) {
pa_log("Failed to load cookie");
goto fail;
}
- *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY;
+
+ key = ESD_ENDIAN_KEY;
+ memcpy((uint8_t*) u->write_data + ESD_KEY_LEN, &key, sizeof(key));
/* Reserve space for the response */
u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 441ebfed..19430a3d 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -53,15 +51,16 @@
#include "dbus-util.h"
#include "module-hal-detect-symdef.h"
-PA_MODULE_AUTHOR("Shahms King")
-PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Shahms King");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
#if defined(HAVE_ALSA) && defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<alsa or oss>")
+PA_MODULE_USAGE("api=<alsa or oss>");
#elif defined(HAVE_ALSA)
-PA_MODULE_USAGE("api=<alsa>")
+PA_MODULE_USAGE("api=<alsa>");
#elif defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<oss>")
+PA_MODULE_USAGE("api=<oss>");
#endif
struct device {
@@ -94,7 +93,7 @@ static const char* const valid_modargs[] = {
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);
@@ -107,7 +106,7 @@ static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
static const char *strip_udi(const char *udi) {
const char *slash;
-
+
if ((slash = strrchr(udi, '/')))
return slash+1;
@@ -130,11 +129,11 @@ static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *
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"))
t = ALSA_TYPE_SOURCE;
- else
+ else
t = ALSA_TYPE_OTHER;
libhal_free_string(type);
@@ -145,13 +144,13 @@ static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *
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);
-
+
return r;
}
@@ -162,7 +161,7 @@ static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char
const char *module_name;
DBusError error;
pa_module *m;
-
+
dbus_error_init(&error);
pa_assert(u);
@@ -170,7 +169,7 @@ static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char
pa_assert(source_name);
*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;
@@ -188,14 +187,14 @@ static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char
if (type == ALSA_TYPE_SINK) {
*sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
-
+
module_name = "module-alsa-sink";
- args = pa_sprintf_malloc("device=hw:%u sink_name=%s", card, *sink_name);
+ args = pa_sprintf_malloc("device_id=%u sink_name=%s", card, *sink_name);
} else {
*source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
-
+
module_name = "module-alsa-source";
- args = pa_sprintf_malloc("device=hw:%u source_name=%s", card, *source_name);
+ args = pa_sprintf_malloc("device_id=%u source_name=%s", card, *source_name);
}
pa_log_debug("Loading %s with arguments '%s'", module_name, args);
@@ -209,7 +208,7 @@ static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char
pa_xfree(*source_name);
*sink_name = *source_name = NULL;
}
-
+
return m;
fail:
@@ -255,7 +254,7 @@ finish:
libhal_free_string(class);
libhal_free_string(dev);
-
+
return r;
}
@@ -264,7 +263,7 @@ static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char
char* device;
DBusError error;
pa_module *m;
-
+
dbus_error_init(&error);
pa_assert(u);
@@ -282,7 +281,7 @@ static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char
*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);
@@ -316,7 +315,7 @@ static struct device* hal_device_add(struct userdata *u, const char *udi) {
pa_assert(u);
pa_assert(u->capability);
pa_assert(!pa_hashmap_get(u->devices, udi));
-
+
#ifdef HAVE_ALSA
if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
@@ -346,12 +345,12 @@ static int hal_device_add_all(struct userdata *u, const char *capability) {
char** udis;
pa_assert(u);
-
+
dbus_error_init(&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);
@@ -363,15 +362,15 @@ static int hal_device_add_all(struct userdata *u, const char *capability) {
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);
+ pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
count++;
}
}
@@ -383,7 +382,7 @@ static int hal_device_add_all(struct userdata *u, const char *capability) {
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(context, udi, "info.capabilities", error);
if (!has_prop || dbus_error_is_set(error))
return FALSE;
@@ -400,18 +399,18 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const s
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)))
+ 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_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
}
}
}
@@ -432,13 +431,13 @@ static void device_added_cb(LibHalContext *context, const char *udi) {
if (pa_hashmap_get(u->devices, udi))
return;
-
+
pa_log_debug("HAL Device added: %s", udi);
dbus_error_init(&error);
if (u->capability) {
-
+
good = device_has_capability(context, udi, u->capability, &error);
if (dbus_error_is_set(&error)) {
@@ -446,7 +445,7 @@ static void device_added_cb(LibHalContext *context, const char *udi) {
dbus_error_free(&error);
return;
}
-
+
} else {
#ifdef HAVE_ALSA
@@ -463,10 +462,10 @@ static void device_added_cb(LibHalContext *context, const char *udi) {
#endif
#if defined(HAVE_OSS) && defined(HAVE_ALSA)
if (!good) {
-#endif
+#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);
@@ -481,7 +480,7 @@ static void device_added_cb(LibHalContext *context, const char *udi) {
}
#endif
}
-
+
if (!good)
return;
@@ -502,7 +501,7 @@ static void device_removed_cb(LibHalContext* context, const char *udi) {
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);
@@ -536,7 +535,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_assert(bus);
pa_assert(message);
pa_assert(u);
-
+
dbus_error_init(&error);
pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
@@ -548,7 +547,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
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;
@@ -559,25 +558,25 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
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;
+ 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);
+ pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
else
d->acl_race_fix = 1;
-
+
} else if (!prev_suspended && suspend) {
/* suspend */
if (pa_sink_suspend(sink, 1) >= 0)
@@ -611,11 +610,11 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
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
@@ -626,15 +625,15 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
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;
@@ -642,14 +641,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
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);
+ pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
}
}
}
-
+
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;
@@ -658,15 +657,15 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_source_suspend(source, 0);
}
}
-
- } else
+
+ } 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;
}
@@ -674,7 +673,7 @@ 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);
@@ -686,7 +685,7 @@ static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
LibHalContext *hal_context = NULL;
dbus_error_init(&error);
-
+
if (!(hal_context = libhal_ctx_new())) {
pa_log_error("libhal_ctx_new() failed");
goto fail;
@@ -721,7 +720,7 @@ int pa__init(pa_module*m) {
int n = 0;
pa_modargs *ma;
const char *api;
-
+
pa_assert(m);
dbus_error_init(&error);
@@ -738,7 +737,7 @@ int pa__init(pa_module*m) {
if (strcmp(api, CAPABILITY_ALSA) == 0) {
good = 1;
api = CAPABILITY_ALSA;
- }
+ }
#endif
#ifdef HAVE_OSS
if (strcmp(api, CAPABILITY_OSS) == 0) {
@@ -746,13 +745,13 @@ int pa__init(pa_module*m) {
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);
@@ -779,7 +778,7 @@ int pa__init(pa_module*m) {
#endif
#if defined(HAVE_ALSA) && defined(HAVE_OSS)
if (n <= 0)
-#endif
+#endif
#ifdef HAVE_OSS
n += hal_device_add_all(u, CAPABILITY_OSS);
#endif
@@ -811,17 +810,17 @@ int pa__init(pa_module*m) {
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_modargs_free(ma);
-
+
return 0;
fail:
if (ma)
pa_modargs_free(ma);
-
+
dbus_error_free(&error);
pa__done(m);
@@ -831,7 +830,7 @@ fail:
void pa__done(pa_module *m) {
struct userdata *u;
-
+
pa_assert(m);
if (!(u = m->userdata))
@@ -845,6 +844,6 @@ void pa__done(pa_module *m) {
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 5019d656..c4d47f8e 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,7 +26,6 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
@@ -54,7 +51,7 @@
/* General overview:
*
- * Because JACK has a very unflexible event loop management, which
+ * 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
@@ -65,16 +62,17 @@
* source support in JACK.
*/
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("JACK Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Sink");
+PA_MODULE_LOAD_ONCE(TRUE);
+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?> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#define DEFAULT_SINK_NAME "jack_out"
@@ -215,8 +213,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -254,8 +252,8 @@ static void jack_init(void *arg) {
pa_log_info("JACK thread starting up.");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+4);
}
static void jack_shutdown(void* arg) {
@@ -273,10 +271,10 @@ int pa__init(pa_module*m) {
jack_status_t status;
const char *server_name, *client_name;
uint32_t channels = 0;
- int do_connect = 1;
+ pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
- char *t;
+ pa_sink_new_data data;
pa_assert(m);
@@ -300,9 +298,8 @@ int pa__init(pa_module*m) {
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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
/* The queue linking the JACK thread and our RT thread */
u->jack_msgq = pa_asyncmsgq_new(0);
@@ -312,7 +309,7 @@ int pa__init(pa_module*m) {
* 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);
+ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(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.");
@@ -333,7 +330,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
+ pa_channel_map_init_extend(&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;
@@ -354,20 +351,31 @@ int pa__init(pa_module*m) {
}
}
- 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.");
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+ if (server_name)
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
+ pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- u->sink->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("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);
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
index b62ebe7a..03f9d15c 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -54,16 +52,17 @@
/* 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_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_USAGE(
"source_name=<name of source> "
"server_name=<jack server name> "
"client_name=<jack client name> "
"channels=<number of channels> "
"connect=<connect ports?>"
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#define DEFAULT_SOURCE_NAME "jack_in"
@@ -191,8 +190,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -230,8 +229,8 @@ static void jack_init(void *arg) {
pa_log_info("JACK thread starting up.");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+4);
}
static void jack_shutdown(void* arg) {
@@ -249,10 +248,10 @@ int pa__init(pa_module*m) {
jack_status_t status;
const char *server_name, *client_name;
uint32_t channels = 0;
- int do_connect = 1;
+ pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
- char *t;
+ pa_source_new_data data;
pa_assert(m);
@@ -277,12 +276,11 @@ int pa__init(pa_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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->jack_msgq = pa_asyncmsgq_new(0);
- u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(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.");
@@ -303,7 +301,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
+ pa_channel_map_init_extend(&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;
@@ -324,20 +322,31 @@ int pa__init(pa_module*m) {
}
}
- 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.");
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+ if (server_name)
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
+ pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log("Failed to create source.");
goto fail;
}
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
- 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);
jack_set_process_callback(u->client, jack_process, u);
jack_on_shutdown(u->client, jack_shutdown, u);
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 0265d971..3e0babfa 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -41,13 +39,15 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.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_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Virtual LADSPA sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"master=<name of sink to remap> "
@@ -57,7 +57,9 @@ PA_MODULE_USAGE(
"channel_map=<channel map> "
"plugin=<ladspa plugin name> "
"label=<ladspa plugin label> "
- "control=<comma seperated list of input control values>")
+ "control=<comma seperated list of input control values>");
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
struct userdata {
pa_core *core;
@@ -78,7 +80,7 @@ struct userdata {
about control out ports. We connect them all to this single buffer. */
LADSPA_Data control_out;
- pa_memchunk memchunk;
+ pa_memblockq *memblockq;
};
static const char* const valid_modargs[] = {
@@ -103,10 +105,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
+ /* Get the latency of the master sink */
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);
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
return 0;
}
}
@@ -121,110 +127,170 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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)))
+ if (PA_SINK_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_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;
+static void sink_request_rewind(pa_sink *s) {
+ struct userdata *u;
- 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);
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- /* Fall through, the default handler will add in the extra
- * latency added by the resampler */
- break;
- }
+ /* Just hand this one over to the master sink */
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- return pa_sink_input_process_msg(o, code, data, offset, chunk);
+ /* Just hand this one over to the master sink */
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
+ float *src, *dst;
+ size_t fs;
+ unsigned n, c;
+ pa_memchunk tchunk;
pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
pa_assert_se(u = i->userdata);
- if (!u->memchunk.memblock) {
- pa_memchunk tchunk;
- float *src, *dst;
- size_t fs;
- unsigned n, c;
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return -1;
- pa_sink_render(u->sink, length, &tchunk);
+ while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+ pa_memchunk nchunk;
- fs = pa_frame_size(&i->sample_spec);
- n = tchunk.length / fs;
-
- pa_assert(n > 0);
+ pa_sink_render(u->sink, nbytes, &nchunk);
+ pa_memblockq_push(u->memblockq, &nchunk);
+ pa_memblock_unref(nchunk.memblock);
+ }
- u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length);
- u->memchunk.index = 0;
- u->memchunk.length = tchunk.length;
+ tchunk.length = PA_MIN(nbytes, tchunk.length);
+ pa_assert(tchunk.length > 0);
- src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
- dst = (float*) pa_memblock_acquire(u->memchunk.memblock);
+ fs = pa_frame_size(&i->sample_spec);
+ n = PA_MIN(tchunk.length, u->block_size) / fs;
- for (c = 0; c < u->channels; c++) {
- unsigned j;
- float *p, *q;
+ pa_assert(n > 0);
- p = src + c;
- q = u->input;
- for (j = 0; j < n; j++, p += u->channels, q++)
- *q = CLAMP(*p, -1.0, 1.0);
+ chunk->index = 0;
+ chunk->length = n*fs;
+ chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
- u->descriptor->run(u->handle[c], n);
+ pa_memblockq_drop(u->memblockq, chunk->length);
- q = u->output;
- p = dst + c;
- for (j = 0; j < n; j++, q++, p += u->channels)
- *p = CLAMP(*q, -1.0, 1.0);
- }
+ src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+ dst = (float*) pa_memblock_acquire(chunk->memblock);
- pa_memblock_release(tchunk.memblock);
- pa_memblock_release(u->memchunk.memblock);
-
- pa_memblock_unref(tchunk.memblock);
+ for (c = 0; c < u->channels; c++) {
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
+ u->descriptor->run(u->handle[c], n);
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
}
- pa_assert(u->memchunk.length > 0);
- pa_assert(u->memchunk.memblock);
+ pa_memblock_release(tchunk.memblock);
+ pa_memblock_release(chunk->memblock);
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
+ pa_memblock_unref(tchunk.memblock);
return 0;
}
/* Called from I/O thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- pa_assert(length > 0);
+ pa_assert(nbytes > 0);
- if (u->memchunk.memblock) {
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return;
- if (length < u->memchunk.length) {
- u->memchunk.index += length;
- u->memchunk.length -= length;
- return;
- }
+ if (u->sink->thread_info.rewind_nbytes > 0) {
+ size_t max_rewrite, amount;
+
+ max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+ amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ if (amount > 0) {
+ unsigned c;
+
+ pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE);
+ pa_sink_process_rewind(u->sink, amount);
- pa_memblock_unref(u->memchunk.memblock);
- length -= u->memchunk.length;
- pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("Resetting plugin");
+
+ /* Reset the plugin */
+ if (u->descriptor->deactivate)
+ for (c = 0; c < u->channels; c++)
+ u->descriptor->deactivate(u->handle[c]);
+ if (u->descriptor->activate)
+ for (c = 0; c < u->channels; c++)
+ u->descriptor->activate(u->handle[c]);
+ }
}
- if (length > 0)
- pa_sink_skip(u->sink, length);
+ pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+ pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
}
/* Called from I/O thread context */
@@ -234,7 +300,12 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
pa_sink_detach_within_thread(u->sink);
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@@ -244,10 +315,14 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
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);
+
+ pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@@ -257,25 +332,43 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ pa_sink_unlink(u->sink);
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_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
pa_module_unload_request(u->module);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT) {
+ pa_log_debug("Requesting rewind due to state change.");
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+}
+
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
char *t;
+ const char *z;
pa_sink *master;
- pa_sink_input_new_data data;
+ pa_sink_input_new_data sink_input_data;
+ pa_sink_new_data sink_data;
const char *plugin, *label;
LADSPA_Descriptor_Function descriptor_func;
const char *e, *cdata;
@@ -283,7 +376,6 @@ int pa__init(pa_module*m) {
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);
@@ -324,7 +416,9 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->master = master;
- pa_memchunk_reset(&u->memchunk);
+ u->sink = NULL;
+ u->sink_input = NULL;
+ u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
if (!(e = getenv("LADSPA_PATH")))
e = LADSPA_PATH;
@@ -341,7 +435,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) {
+ if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) {
pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
goto fail;
}
@@ -349,7 +443,7 @@ int pa__init(pa_module*m) {
for (j = 0;; j++) {
if (!(d = descriptor_func(j))) {
- pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label);
+ pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin);
goto fail;
}
@@ -395,7 +489,7 @@ int pa__init(pa_module*m) {
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]);
+ pa_log_debug("Ignored control output port \"%s\".", d->PortNames[p]);
}
}
@@ -440,8 +534,8 @@ int pa__init(pa_module*m) {
use_default = pa_xnew(pa_bool_t, n_control);
p = 0;
- while ((k = pa_split(cdata, ",", &state))) {
- float f;
+ while ((k = pa_split(cdata, ",", &state)) && p < n_control) {
+ double f;
if (*k == 0) {
use_default[p++] = TRUE;
@@ -449,7 +543,7 @@ int pa__init(pa_module*m) {
continue;
}
- if (pa_atof(k, &f) < 0) {
+ if (pa_atod(k, &f) < 0) {
pa_log("Failed to parse control value '%s'", k);
pa_xfree(k);
goto fail;
@@ -457,15 +551,24 @@ int pa__init(pa_module*m) {
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;
}
+ /* The previous loop doesn't take the last control value into account
+ if it is left empty, so we do it here. */
+ if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
+ if (p < n_control)
+ use_default[p] = TRUE;
+ p++;
+ }
+
+ if (p > n_control || k) {
+ pa_log("Too many control values passed, %lu expected.", n_control);
+ pa_xfree(k);
+ goto fail;
+ }
+
if (p < n_control) {
pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p);
goto fail;
@@ -572,43 +675,68 @@ int pa__init(pa_module*m) {
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_sink_new_data_init(&sink_data);
+ sink_data.driver = __FILE__;
+ sink_data.module = m;
+ if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+ sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
+ sink_data.namereg_fail = FALSE;
+ pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_data, &map);
+ z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
+ pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);
+
+ u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_data);
+
+ if (!u->sink) {
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->update_requested_latency = sink_update_requested_latency;
+ u->sink->request_rewind = sink_request_rewind;
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)))
+ pa_sink_input_new_data_init(&sink_input_data);
+ sink_input_data.driver = __FILE__;
+ sink_input_data.module = m;
+ sink_input_data.sink = u->master;
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+ pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+ pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+
+ u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new_data_done(&sink_input_data);
+
+ if (!u->sink_input)
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->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->update_max_request = sink_input_update_max_request_cb;
+ u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_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->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
@@ -617,7 +745,6 @@ int pa__init(pa_module*m) {
pa_modargs_free(ma);
pa_xfree(use_default);
- pa_xfree(default_sink_name);
return 0;
@@ -626,7 +753,6 @@ fail:
pa_modargs_free(ma);
pa_xfree(use_default);
- pa_xfree(default_sink_name);
pa__done(m);
@@ -642,18 +768,15 @@ void pa__done(pa_module*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);
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
for (c = 0; c < u->channels; c++)
if (u->handle[c]) {
@@ -665,6 +788,9 @@ void pa__done(pa_module*m) {
if (u->output != u->input)
pa_xfree(u->output);
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
pa_xfree(u->input);
pa_xfree(u->control);
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 401763b7..0570a6a1 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,10 +41,11 @@
#include "module-lirc-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("LIRC volume control")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("LIRC volume control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>");
static const char* const valid_modargs[] = {
"config",
@@ -187,7 +186,7 @@ fail:
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
-
+
pa_assert(m);
if (lirc_in_use) {
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 5a922966..769a6b59 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,10 +44,11 @@
#include "module-match-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Playback stream expression matching module")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Playback stream expression matching module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("table=<filename>");
#define WHITESPACE "\n\r \t"
@@ -81,12 +80,14 @@ static int load_rules(struct userdata *u, const char *filename) {
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");
+ if (filename)
+ f = fopen(fn = pa_xstrdup(filename), "r");
+ else
+ f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
if (!f) {
- pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(fn);
+ pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
goto finish;
}
@@ -165,7 +166,8 @@ 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;
-
+ const char *n;
+
pa_assert(c);
pa_assert(u);
@@ -175,13 +177,13 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
- if (!si->name)
+ if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
return;
for (r = u->rules; r; r = r->next) {
- if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
+ if (!regexec(&r->regex, n, 0, NULL, 0)) {
pa_cvolume cv;
- pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume);
+ pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
pa_sink_input_set_volume(si, &cv);
}
@@ -191,7 +193,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
-
+
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
@@ -223,7 +225,7 @@ fail:
void pa__done(pa_module*m) {
struct userdata* u;
struct rule *r, *n;
-
+
pa_assert(m);
if (!(u = m->userdata))
@@ -241,5 +243,3 @@ void pa__done(pa_module*m) {
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index dc0b1c1a..4388e49c 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,10 +44,11 @@
#include "module-mmkbd-evdev-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("device=<evdev device> sink=<sink name>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("device=<evdev device> sink=<sink name>");
#define DEFAULT_DEVICE "/dev/input/event0"
@@ -79,7 +78,7 @@ 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;
-
+
pa_assert(io);
pa_assert(u);
@@ -166,7 +165,7 @@ fail:
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
int pa__init(pa_module*m) {
-
+
pa_modargs *ma = NULL;
struct userdata *u;
int version;
diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c
index b0a9ebb0..1a6f5368 100644
--- a/src/modules/module-native-protocol-fd.c
+++ b/src/modules/module-native-protocol-fd.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,9 +35,10 @@
#include "module-native-protocol-fd-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Native protocol autospawn helper")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Native protocol autospawn helper");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
static const char* const valid_modargs[] = {
"fd",
@@ -52,7 +51,7 @@ int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
int fd, r = -1;
-
+
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index d993988e..604ab158 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -51,18 +49,20 @@
#include "module-null-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Clocked NULL sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Clocked NULL sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "sink_name=<name of sink>"
- "channel_map=<channel map>"
- "description=<description for the sink>")
+ "sink_name=<name of sink> "
+ "channel_map=<channel map> "
+ "description=<description for the sink>");
#define DEFAULT_SINK_NAME "null"
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
struct userdata {
pa_core *core;
@@ -74,8 +74,9 @@ struct userdata {
pa_rtpoll *rtpoll;
size_t block_size;
-
- struct timeval timestamp;
+
+ pa_usec_t block_usec;
+ pa_usec_t timestamp;
};
static const char* const valid_modargs[] = {
@@ -95,26 +96,93 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
- pa_rtclock_get(&u->timestamp);
-
+ u->timestamp = pa_rtclock_usec();
+
break;
-
+
case PA_SINK_MESSAGE_GET_LATENCY: {
- struct timeval now;
-
- 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;
+ pa_usec_t now;
+
+ now = pa_rtclock_usec();
+ *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
+
+ return 0;
}
}
-
+
return pa_sink_process_msg(o, code, data, offset, chunk);
}
+static void sink_update_requested_latency_cb(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ u = s->userdata;
+ pa_assert(u);
+
+ u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+ size_t rewind_nbytes, in_buffer;
+ pa_usec_t delay;
+
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ pa_assert(rewind_nbytes > 0);
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+ if (u->timestamp <= now)
+ return;
+
+ delay = u->timestamp - now;
+ in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+ if (in_buffer <= 0)
+ return;
+
+ if (rewind_nbytes > in_buffer)
+ rewind_nbytes = in_buffer;
+
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+ u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+ size_t ate = 0;
+
+ pa_assert(u);
+
+ /* This is the configured latency. Sink inputs connected to us
+ might not have a single frame more than the maxrequest value
+ queed. Hence: at maximum read this many bytes from the sink
+ inputs. */
+
+ /* Fill the buffer up the the latency size */
+ while (u->timestamp < now + u->block_usec) {
+ pa_memchunk chunk;
+
+ pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+ u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+ ate += chunk.length;
+
+ if (ate >= u->sink->thread_info.max_request)
+ break;
+ }
+
+/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
@@ -125,30 +193,31 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_rtclock_get(&u->timestamp);
+ u->timestamp = pa_rtclock_usec();
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);
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ pa_usec_t now;
+
+ now = pa_rtclock_usec();
- 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));
- }
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ process_rewind(u, now);
- pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
+ if (u->timestamp <= now)
+ process_render(u, now);
+
+ 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)
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
-
+
if (ret == 0)
goto finish;
}
@@ -168,6 +237,7 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
+ pa_sink_new_data data;
pa_assert(m);
@@ -182,39 +252,50 @@ int pa__init(pa_module*m) {
goto fail;
}
- u = pa_xnew0(struct userdata, 1);
+ m->userdata = u = pa_xnew0(struct userdata, 1);
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(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
- pa_log("Failed to create sink.");
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink object.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
+ u->sink->update_requested_latency = sink_update_requested_latency_cb;
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);
- pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
- u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
- if (u->block_size <= 0)
- u->block_size = pa_frame_size(&ss);
-
+ pa_sink_set_latency_range(u->sink, (pa_usec_t) -1, MAX_LATENCY_USEC);
+ u->block_usec = u->sink->thread_info.max_latency;
+
+ u->sink->thread_info.max_rewind =
+ u->sink->thread_info.max_request =
+ pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
pa_sink_put(u->sink);
-
+
pa_modargs_free(ma);
return 0;
@@ -251,6 +332,6 @@ void pa__done(pa_module*m) {
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
-
+
pa_xfree(u);
}
diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c
index 19dceef2..76b13ecc 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/module-oss.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -76,9 +74,10 @@
#include "oss-util.h"
#include "module-oss-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("OSS Sink/Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("OSS Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"source_name=<name for the source> "
@@ -91,7 +90,7 @@ PA_MODULE_USAGE(
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
"channel_map=<channel map> "
- "mmap=<enable memory mapping?>")
+ "mmap=<enable memory mapping?>");
#define DEFAULT_DEVICE "/dev/dsp"
@@ -160,10 +159,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
pa_log_debug("trigger");
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
enable_bits |= PCM_ENABLE_INPUT;
- if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
enable_bits |= PCM_ENABLE_OUTPUT;
pa_log_debug("trigger: %i", enable_bits);
@@ -201,7 +200,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
* register the fd as ready.
*/
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (u->source && PA_SOURCE_IS_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);
@@ -509,6 +508,9 @@ static int suspend(struct userdata *u) {
return 0;
}
+static int sink_get_volume(pa_sink *s);
+static int source_get_volume(pa_source *s);
+
static int unsuspend(struct userdata *u) {
int m;
pa_sample_spec ss, *ss_original;
@@ -599,9 +601,9 @@ static int unsuspend(struct userdata *u) {
build_pollfd(u);
if (u->sink)
- pa_sink_get_volume(u->sink);
+ sink_get_volume(u->sink);
if (u->source)
- pa_source_get_volume(u->source);
+ source_get_volume(u->source);
pa_log_info("Resumed successfully...");
@@ -640,7 +642,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
if (!u->source || u->source_suspended) {
if (suspend(u) < 0)
@@ -657,7 +659,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->sink->thread_info.state == PA_SINK_INIT) {
do_trigger = TRUE;
- quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
+ quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
}
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
@@ -720,7 +722,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED:
- pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if (!u->sink || u->sink_suspended) {
if (suspend(u) < 0)
@@ -737,7 +739,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (u->source->thread_info.state == PA_SOURCE_INIT) {
do_trigger = TRUE;
- quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
+ quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
}
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
@@ -863,8 +865,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -876,7 +878,7 @@ static void thread_func(void *userdata) {
/* 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->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
if (u->use_mmap) {
@@ -984,7 +986,7 @@ static void thread_func(void *userdata) {
/* 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->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
if (u->use_mmap) {
@@ -1094,8 +1096,8 @@ static void thread_func(void *userdata) {
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);
+ ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+ ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
}
/* Hmm, nothing to do. Let's sleep */
@@ -1138,13 +1140,15 @@ int pa__init(pa_module*m) {
int fd = -1;
int nfrags, frag_size;
int mode, caps;
- int record = 1, playback = 1, use_mmap = 1;
+ pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
- char hwdesc[64], *t;
+ char hwdesc[64];
const char *name;
- int namereg_fail;
+ pa_bool_t namereg_fail;
+ pa_sink_new_data sink_new_data;
+ pa_source_new_data source_new_data;
pa_assert(m);
@@ -1154,7 +1158,7 @@ int pa__init(pa_module*m) {
}
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 argument.");
+ pa_log("record= and playback= expect boolean argument.");
goto fail;
}
@@ -1225,17 +1229,16 @@ int pa__init(pa_module*m) {
m->userdata = u;
u->fd = fd;
u->mixer_fd = -1;
- u->use_getospace = u->use_getispace = 1;
- u->use_getodelay = 1;
+ u->use_getospace = u->use_getispace = TRUE;
+ u->use_getodelay = TRUE;
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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
build_pollfd(u);
@@ -1243,14 +1246,14 @@ int pa__init(pa_module*m) {
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;
+ u->use_getispace = TRUE;
}
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
u->out_fragment_size = info.fragsize;
u->out_nfrags = info.fragstotal;
- u->use_getospace = 1;
+ u->use_getospace = TRUE;
}
u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
@@ -1262,21 +1265,37 @@ int pa__init(pa_module*m) {
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;
+ use_mmap = u->use_mmap = FALSE;
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;
+ namereg_fail = TRUE;
else {
name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
- namereg_fail = 0;
+ namereg_fail = FALSE;
}
- u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_source_new_data_init(&source_new_data);
+ source_new_data.driver = __FILE__;
+ source_new_data.module = m;
+ pa_source_new_data_set_name(&source_new_data, name);
+ source_new_data.namereg_fail = namereg_fail;
+ pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+ pa_source_new_data_set_channel_map(&source_new_data, &map);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
+
+ u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&source_new_data);
pa_xfree(name_buf);
+
if (!u->source) {
pa_log("Failed to create source object");
goto fail;
@@ -1285,18 +1304,8 @@ int pa__init(pa_module*m) {
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
- 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->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY;
u->source->refresh_volume = TRUE;
if (use_mmap)
@@ -1314,7 +1323,7 @@ int pa__init(pa_module*m) {
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->use_mmap = use_mmap = FALSE;
u->out_mmap = NULL;
}
} else {
@@ -1324,14 +1333,30 @@ int pa__init(pa_module*m) {
}
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = 1;
+ namereg_fail = TRUE;
else {
name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
- namereg_fail = 0;
+ namereg_fail = FALSE;
}
- u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_sink_new_data_init(&sink_new_data);
+ sink_new_data.driver = __FILE__;
+ sink_new_data.module = m;
+ pa_sink_new_data_set_name(&sink_new_data, name);
+ sink_new_data.namereg_fail = namereg_fail;
+ pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+ pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
+ pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
+
+ u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_new_data);
pa_xfree(name_buf);
+
if (!u->sink) {
pa_log("Failed to create sink object");
goto fail;
@@ -1340,26 +1365,18 @@ int pa__init(pa_module*m) {
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(
- "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->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY;
u->sink->refresh_volume = TRUE;
+ u->sink->thread_info.max_request = u->out_hwbuf_size;
+
if (use_mmap)
u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
}
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
- int do_close = 1;
+ pa_bool_t do_close = TRUE;
u->mixer_devmask = 0;
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
@@ -1371,7 +1388,7 @@ int pa__init(pa_module*m) {
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;
+ do_close = FALSE;
}
if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
@@ -1379,7 +1396,7 @@ int pa__init(pa_module*m) {
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;
+ do_close = FALSE;
}
}
@@ -1401,10 +1418,25 @@ go_on:
}
/* Read mixer settings */
- 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) {
+ if (sink_new_data.volume_is_set) {
+ if (u->sink->set_volume)
+ u->sink->set_volume(u->sink);
+ } else {
+ if (u->sink->get_volume)
+ u->sink->get_volume(u->sink);
+ }
+ }
+
+ if (u->source) {
+ if (source_new_data.volume_is_set) {
+ if (u->source->set_volume)
+ u->source->set_volume(u->source);
+ } else {
+ if (u->source->get_volume)
+ u->source->get_volume(u->source);
+ }
+ }
if (u->sink)
pa_sink_put(u->sink);
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 75748474..cd25b890 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -50,18 +48,19 @@
#include "module-pipe-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"file=<path of the FIFO> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate>"
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
-#define DEFAULT_FILE_NAME "/tmp/music.output"
+#define DEFAULT_FILE_NAME "fifo_output"
#define DEFAULT_SINK_NAME "fifo_output"
struct userdata {
@@ -79,6 +78,8 @@ struct userdata {
pa_memchunk memchunk;
pa_rtpoll_item *rtpoll_item;
+
+ int write_type;
};
static const char* const valid_modargs[] = {
@@ -108,16 +109,64 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
n += u->memchunk.length;
*((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
- break;
+ return 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
+static void process_rewind(struct userdata *u) {
+ pa_assert(u);
+
+ pa_log_debug("Rewind requested but not supported by pipe sink. Ignoring.");
+ u->sink->thread_info.rewind_nbytes = 0;
+}
+
+static int process_render(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->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));
+ return -1;
+ }
+
+ } 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);
+ }
+ }
+
+ return 0;
+ }
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
- int write_type = 0;
pa_assert(u);
@@ -133,39 +182,14 @@ static void thread_func(void *userdata) {
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);
+ if (u->sink->thread_info.state == PA_SINK_RUNNING) {
- pa_assert(u->memchunk.length > 0);
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ process_rewind(u);
- 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));
+ if (pollfd->revents) {
+ if (process_render(u) < 0)
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;
}
@@ -204,8 +228,8 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
- char *t;
struct pollfd *pollfd;
+ pa_sink_new_data data;
pa_assert(m);
@@ -225,11 +249,11 @@ int pa__init(pa_module*m) {
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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->write_type = 0;
- u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+ u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
mkfifo(u->filename, 0666);
if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
@@ -250,20 +274,28 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename);
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- u->sink->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->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 2313df67..b0de34ca 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,16 +47,17 @@
#include "module-pipe-source-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source_name=<name for the source> "
"file=<path of the FIFO> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#define DEFAULT_FILE_NAME "/tmp/music.input"
#define DEFAULT_SOURCE_NAME "fifo_input"
@@ -100,13 +99,13 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
-
+
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;
@@ -152,13 +151,14 @@ static void thread_func(void *userdata) {
/* Hmm, nothing to do. Let's sleep */
pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0;
- if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 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 & ~POLLIN) {
pa_log("FIFO shutdown.");
goto fail;
@@ -181,8 +181,8 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
- char *t;
struct pollfd *pollfd;
+ pa_source_new_data data;
pa_assert(m);
@@ -202,12 +202,11 @@ int pa__init(pa_module*m) {
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);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
- 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));
@@ -227,25 +226,33 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+
+ u->source = pa_source_new(m->core, &data, 0);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
pa_log("Failed to create source.");
goto fail;
}
u->source->userdata = u;
- 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->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;
-
+
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
@@ -268,7 +275,7 @@ fail:
void pa__done(pa_module*m) {
struct userdata *u;
-
+
pa_assert(m);
if (!(u = m->userdata))
@@ -286,16 +293,16 @@ void pa__done(pa_module*m) {
if (u->source)
pa_source_unref(u->source);
-
+
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->filename) {
unlink(u->filename);
pa_xfree(u->filename);
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
new file mode 100644
index 00000000..90e693a3
--- /dev/null
+++ b/src/modules/module-position-event-sounds.c
@@ -0,0 +1,165 @@
+/***
+ 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+
+#include "module-position-event-sounds-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Position event sounds between L and R depending on the position on screen of the widget triggering them.");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_hook_slot *sink_input_fixate_hook_slot;
+};
+
+static pa_bool_t is_left(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t is_right(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
+ const char *hpos;
+ double f;
+ unsigned c;
+ char t[PA_CVOLUME_SNPRINT_MAX];
+
+ pa_assert(data);
+
+ if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS)))
+ return PA_HOOK_OK;
+
+ if (pa_atod(hpos, &f) < 0) {
+ pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos);
+ return PA_HOOK_OK;
+ }
+
+ if (f < 0.0 || f > 1.0) {
+ pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f);
+ return PA_HOOK_OK;
+ }
+
+ pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+
+ if (!data->volume_is_set) {
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_set = TRUE;
+ }
+
+ for (c = 0; c < data->sample_spec.channels; c++) {
+
+ if (is_left(data->channel_map.map[c]))
+ data->volume.values[c] =
+ pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
+
+ if (is_right(data->channel_map.map[c]))
+ data->volume.values[c] =
+ pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
+ }
+
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+
+ 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_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink_input_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index fb7cf22d..0c9529c3 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -76,7 +74,7 @@
#else
#include "module-simple-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION)
+PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("rate=<sample rate> "
"format=<sample format> "
"channels=<number of channels> "
@@ -84,7 +82,7 @@
"source=<source to connect to> "
"playback=<enable playback?> "
"record=<enable record?> "
- SOCKET_USAGE)
+ SOCKET_USAGE);
#elif defined(USE_PROTOCOL_CLI)
#include <pulsecore/protocol-cli.h>
#define protocol_new pa_protocol_cli_new
@@ -98,8 +96,8 @@
#else
#include "module-cli-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION)
- PA_MODULE_USAGE(SOCKET_USAGE)
+ PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION);
+ PA_MODULE_USAGE(SOCKET_USAGE);
#elif defined(USE_PROTOCOL_HTTP)
#include <pulsecore/protocol-http.h>
#define protocol_new pa_protocol_http_new
@@ -113,8 +111,8 @@
#else
#include "module-http-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION)
- PA_MODULE_USAGE(SOCKET_USAGE)
+ PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION);
+ PA_MODULE_USAGE(SOCKET_USAGE);
#elif defined(USE_PROTOCOL_NATIVE)
#include <pulsecore/protocol-native.h>
#define protocol_new pa_protocol_native_new
@@ -140,11 +138,11 @@
#define AUTH_USAGE
#endif
- PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> "
"cookie=<path to cookie file> "
AUTH_USAGE
- SOCKET_USAGE)
+ SOCKET_USAGE);
#elif defined(USE_PROTOCOL_ESOUND)
#include <pulsecore/protocol-esound.h>
#include <pulsecore/esound.h>
@@ -167,19 +165,20 @@
#define AUTH_USAGE
#endif
- PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("sink=<sink to connect to> "
"source=<source to connect to> "
"auth-anonymous=<don't verify cookies?> "
"cookie=<path to cookie file> "
AUTH_USAGE
- SOCKET_USAGE)
+ SOCKET_USAGE);
#else
#error "Broken build system"
#endif
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
static const char* const valid_modargs[] = {
MODULE_ARGUMENTS
@@ -214,11 +213,6 @@ int pa__init(pa_module*m) {
#else
pa_socket_server *s;
int r;
- char tmp[PATH_MAX];
-
-#if defined(USE_PROTOCOL_ESOUND)
- char tmp2[PATH_MAX];
-#endif
#endif
pa_assert(m);
@@ -250,51 +244,57 @@ int pa__init(pa_module*m) {
goto fail;
if (s_ipv4)
- if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
- pa_socket_server_unref(s_ipv4);
-
+ u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
if (s_ipv6)
- if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
- pa_socket_server_unref(s_ipv6);
+ u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
goto fail;
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv4);
+
#else
#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);
-
+#if defined(USE_PER_USER_ESOUND_SOCKET)
+ u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#else
+ u->socket_path = pa_xstrdup("/tmp/.esd/socket");
+#endif
+
/* 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, m->core->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
+ if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 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) {
- pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
+ if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+ pa_log("Failed to generate socket path.");
goto fail;
}
+#endif
- if (r)
- pa_log("Removed stale UNIX socket '%s'.", tmp);
+ if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+ pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
+ goto fail;
+ } else if (r > 0)
+ pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
- if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
+ if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
goto fail;
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail;
+ pa_socket_server_unref(s);
+
#endif
m->userdata = u;
@@ -317,23 +317,21 @@ fail:
#else
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-
- if (u->socket_path)
- pa_xfree(u->socket_path);
+ pa_xfree(u->socket_path);
#endif
pa_xfree(u);
- } else {
+ }
+
#if defined(USE_TCP_SOCKETS)
- if (s_ipv4)
- pa_socket_server_unref(s_ipv4);
- if (s_ipv6)
- pa_socket_server_unref(s_ipv6);
+ if (s_ipv4)
+ pa_socket_server_unref(s_ipv4);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
#else
- if (s)
- pa_socket_server_unref(s);
+ if (s)
+ pa_socket_server_unref(s);
#endif
- }
goto finish;
}
@@ -354,7 +352,7 @@ void pa__done(pa_module*m) {
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-#if defined(USE_PROTOCOL_ESOUND)
+#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
if (u->socket_path) {
char *p = pa_parent_dir(u->socket_path);
rmdir(p);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index e863c0c3..c87b1ece 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -40,9 +38,10 @@
#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_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Virtual channel remapping sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"master=<name of sink to remap> "
@@ -50,7 +49,8 @@ PA_MODULE_USAGE(
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "channel_map=<channel map>")
+ "channel_map=<channel map> "
+ "remix=<remix channels?>");
struct userdata {
pa_core *core;
@@ -58,8 +58,6 @@ struct userdata {
pa_sink *sink, *master;
pa_sink_input *sink_input;
-
- pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
@@ -70,6 +68,7 @@ static const char* const valid_modargs[] = {
"format",
"channels",
"channel_map",
+ "remix",
NULL
};
@@ -82,10 +81,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
+ /* Get the latency of the master sink */
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);
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
return 0;
}
}
@@ -100,67 +103,112 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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)))
+ if (PA_SINK_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_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;
+static void sink_request_rewind(pa_sink *s) {
+ struct userdata *u;
- 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);
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- /* Fall through, the default handler will add in the extra
- * latency added by the resampler */
- break;
- }
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- return pa_sink_input_process_msg(o, code, data, offset, chunk);
+ /* Just hand this one over to the master sink */
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
pa_assert_se(u = i->userdata);
- if (!u->memchunk.memblock)
- pa_sink_render(u->sink, length, &u->memchunk);
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return -1;
- pa_assert(u->memchunk.memblock);
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
+ pa_sink_render(u->sink, nbytes, chunk);
return 0;
}
/* Called from I/O thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- pa_assert(length > 0);
+ pa_assert(nbytes > 0);
- if (u->memchunk.memblock) {
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return;
- if (length < u->memchunk.length) {
- u->memchunk.index += length;
- u->memchunk.length -= length;
- return;
- }
+ if (u->sink->thread_info.rewind_nbytes > 0) {
+ size_t amount;
+
+ amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
+ u->sink->thread_info.rewind_nbytes = 0;
- pa_memblock_unref(u->memchunk.memblock);
- length -= u->memchunk.length;
- pa_memchunk_reset(&u->memchunk);
+ if (amount > 0)
+ pa_sink_process_rewind(u->sink, amount);
}
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
- if (length > 0)
- pa_sink_skip(u->sink, length);
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
}
/* Called from I/O thread context */
@@ -170,7 +218,12 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
pa_sink_detach_within_thread(u->sink);
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@@ -180,10 +233,14 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
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);
+
+ pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@@ -193,26 +250,43 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ pa_sink_unlink(u->sink);
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_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
pa_module_unload_request(u->module);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT) {
+ pa_log_debug("Requesting rewind due to state change.");
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+}
+
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;
+ const char *k;
pa_sink *master;
- pa_sink_input_new_data data;
- char *default_sink_name = NULL;
+ pa_sink_input_new_data sink_input_data;
+ pa_sink_new_data sink_data;
+ pa_bool_t remix = TRUE;
pa_assert(m);
@@ -235,7 +309,7 @@ int pa__init(pa_module*m) {
stream_map = sink_map;
if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
- pa_log("Invalid master hannel map");
+ pa_log("Invalid master channel map");
goto fail;
}
@@ -244,57 +318,83 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_channel_map_equal(&stream_map, &master->channel_map))
+ pa_log_warn("No remapping configured, proceeding nonetheless!");
+
+ if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+ pa_log("Invalid boolean remix parameter");
+ 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);
+ u->sink = NULL;
+ u->sink_input = NULL;
/* 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_sink_new_data_init(&sink_data);
+ sink_data.driver = __FILE__;
+ sink_data.module = m;
+ if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+ sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
+ pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
+ k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+ u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_data);
+
+ if (!u->sink) {
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->update_requested_latency = sink_update_requested_latency;
+ u->sink->request_rewind = sink_request_rewind;
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)))
+ pa_sink_input_new_data_init(&sink_input_data);
+ sink_input_data.driver = __FILE__;
+ sink_input_data.module = m;
+ sink_input_data.sink = u->master;
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+ pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+ pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
+
+ u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+ pa_sink_input_new_data_done(&sink_input_data);
+
+ if (!u->sink_input)
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->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->update_max_request = sink_input_update_max_request_cb;
+ u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->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;
@@ -304,8 +404,6 @@ fail:
pa__done(m);
- pa_xfree(default_sink_name);
-
return -1;
}
@@ -317,18 +415,15 @@ void pa__done(pa_module*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);
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
pa_xfree(u);
}
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 24077dc2..cc6717cb 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -36,9 +34,10 @@
#include "module-rescue-streams-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
static const char* const valid_modargs[] = {
NULL,
@@ -62,7 +61,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
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;
@@ -74,12 +73,12 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
}
while ((i = pa_idxset_first(sink->inputs, NULL))) {
- if (pa_sink_input_move_to(i, target, 1) < 0) {
- pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, i->name, target->name);
+ if (pa_sink_input_move_to(i, target) < 0) {
+ pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
- pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, i->name, target->name);
+ pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
}
@@ -115,11 +114,11 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
while ((o = pa_idxset_first(source->outputs, NULL))) {
if (pa_source_output_move_to(o, target) < 0) {
- pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, o->name, target->name);
+ pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
- pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, o->name, target->name);
+ pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
}
@@ -138,8 +137,8 @@ int pa__init(pa_module*m) {
}
m->userdata = u = pa_xnew(struct userdata, 1);
- 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);
+ u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL);
+ u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL);
pa_modargs_free(ma);
return 0;
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 5684f94c..38780f24 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,10 +37,11 @@
#include "module-sine-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Sine wave generator")
-PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Sine wave generator");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>");
struct userdata {
pa_core *core;
@@ -58,44 +57,43 @@ static const char* const valid_modargs[] = {
NULL,
};
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
-
- pa_assert(i);
- u = i->userdata;
- pa_assert(u);
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
pa_assert(chunk);
chunk->memblock = pa_memblock_ref(u->memblock);
- chunk->index = u->peek_index;
chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index;
-
+ chunk->index = u->peek_index;
+
+ u->peek_index = 0;
+
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
- struct userdata *u;
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
size_t l;
-
- pa_assert(i);
- u = i->userdata;
- pa_assert(u);
- pa_assert(length > 0);
+ struct userdata *u;
- u->peek_index += length;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
l = pa_memblock_get_length(u->memblock);
-
- while (u->peek_index >= l)
- u->peek_index -= l;
+ nbytes %= l;
+
+ if (u->peek_index >= nbytes)
+ u->peek_index -= nbytes;
+ else
+ u->peek_index = l + u->peek_index - nbytes;
}
static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
-
- pa_assert(i);
- u = i->userdata;
- pa_assert(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);
@@ -104,6 +102,20 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_module_unload_request(u->module);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
static void calc_sine(float *f, size_t l, float freq) {
size_t i;
@@ -119,7 +131,6 @@ int pa__init(pa_module*m) {
pa_sink *sink;
pa_sample_spec ss;
uint32_t frequency;
- char t[256];
void *p;
pa_sink_input_new_data data;
@@ -155,25 +166,29 @@ int pa__init(pa_module*m) {
calc_sine(p, pa_memblock_get_length(u->memblock), frequency);
pa_memblock_release(u->memblock);
- pa_snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
-
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = t;
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+ pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
pa_sink_input_new_data_set_sample_spec(&data, &ss);
data.module = m;
- if (!(u->sink_input = pa_sink_input_new(m->core, &data, 0)))
+ u->sink_input = pa_sink_input_new(m->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!u->sink_input)
goto fail;
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_input_put(u->sink_input);
-
+
pa_modargs_free(ma);
return 0;
@@ -187,7 +202,7 @@ fail:
void pa__done(pa_module*m) {
struct userdata *u;
-
+
pa_assert(m);
if (!(u = m->userdata))
@@ -200,7 +215,6 @@ void pa__done(pa_module*m) {
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 a8a94712..6f50543a 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,7 +26,6 @@
#include <stdlib.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 5a711390..bc7c023c 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,9 +35,10 @@
#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)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
static const char* const valid_modargs[] = {
"timeout",
@@ -65,8 +64,6 @@ struct userdata {
*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;
};
@@ -130,27 +127,27 @@ static void resume(struct device_info *d) {
}
}
-static pa_hook_result_t sink_input_new_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
struct device_info *d;
pa_assert(c);
- pa_sink_input_assert_ref(s);
+ pa_assert(data);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+ if ((d = pa_hashmap_get(u->device_infos, data->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) {
+static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
struct device_info *d;
pa_assert(c);
- pa_source_output_assert_ref(s);
+ pa_assert(data);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ if ((d = pa_hashmap_get(u->device_infos, data->source)))
resume(d);
return PA_HOOK_OK;
@@ -184,56 +181,37 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
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) {
+static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, 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(data);
pa_assert(u);
- if (pa_source_used_by(s->source) <= 1) {
- struct device_info *d;
+ if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+ resume(d);
- if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ if (pa_sink_used_by(data->sink_input->sink) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
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) {
+static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
struct device_info *d;
+
pa_assert(c);
- pa_source_output_assert_ref(s);
+ pa_assert(data);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ if ((d = pa_hashmap_get(u->device_infos, data->destination)))
resume(d);
+ if (pa_source_used_by(data->source_output->source) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+ restart(d);
+
return PA_HOOK_OK;
}
@@ -337,7 +315,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_sink_used_by(s) <= 0) {
- if (PA_SINK_OPENED(state))
+ if (PA_SINK_IS_OPENED(state))
restart(d);
}
@@ -348,7 +326,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_source_used_by(s) <= 0) {
- if (PA_SOURCE_OPENED(state))
+ if (PA_SOURCE_IS_OPENED(state))
restart(d);
}
}
@@ -387,24 +365,21 @@ int pa__init(pa_module*m) {
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);
-
+ u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+ u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (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_NORMAL, (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_NORMAL, (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_NORMAL, (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_NORMAL, (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_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
+ u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
+ u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (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_NORMAL, (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_NORMAL, (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_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+ u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (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_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
pa_modargs_free(ma);
return 0;
@@ -448,8 +423,6 @@ void pa__done(pa_module*m) {
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);
@@ -459,8 +432,6 @@ void pa__done(pa_module*m) {
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);
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index b96d46b3..86f30817 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@@ -52,10 +49,21 @@
#include <pulsecore/socket-client.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/authkey-prop.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/proplist-util.h>
#ifdef TUNNEL_SINK
#include "module-tunnel-sink-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sinks")
+#else
+#include "module-tunnel-source-symdef.h"
+#endif
+
+#ifdef TUNNEL_SINK
+PA_MODULE_DESCRIPTION("Tunnel module for sinks");
PA_MODULE_USAGE(
"server=<address> "
"sink=<remote sink name> "
@@ -64,10 +72,9 @@ PA_MODULE_USAGE(
"channels=<number of channels> "
"rate=<sample rate> "
"sink_name=<name for the local sink> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#else
-#include "module-tunnel-source-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sources")
+PA_MODULE_DESCRIPTION("Tunnel module for sources");
PA_MODULE_USAGE(
"server=<address> "
"source=<remote source name> "
@@ -76,21 +83,12 @@ PA_MODULE_USAGE(
"channels=<number of channels> "
"rate=<sample rate> "
"source_name=<name for the local source> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#endif
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8)
-#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
-#define DEFAULT_MINREQ 512
-#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
-#define DEFAULT_FRAGSIZE 1024
-
-#define DEFAULT_TIMEOUT 5
-
-#define LATENCY_INTERVAL 10
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
static const char* const valid_modargs[] = {
"server",
@@ -109,23 +107,70 @@ static const char* const valid_modargs[] = {
NULL,
};
-static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+#define DEFAULT_TIMEOUT 5
+
+#define LATENCY_INTERVAL 10
+
+#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
+
+#ifdef TUNNEL_SINK
+
+enum {
+ SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
+ SINK_MESSAGE_REMOTE_SUSPEND,
+ SINK_MESSAGE_UPDATE_LATENCY,
+ SINK_MESSAGE_POST
+};
+
+#define DEFAULT_TLENGTH_MSEC 150
+#define DEFAULT_MINREQ_MSEC 25
+
+#else
+
+enum {
+ SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+ SOURCE_MESSAGE_REMOTE_SUSPEND,
+ SOURCE_MESSAGE_UPDATE_LATENCY
+};
+
+#define DEFAULT_FRAGSIZE_MSEC 25
+
+#endif
#ifdef TUNNEL_SINK
static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
#endif
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
#ifdef TUNNEL_SINK
[PA_COMMAND_REQUEST] = command_request,
+ [PA_COMMAND_STARTED] = command_started,
#endif
+ [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+ [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
+ [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
[PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
- [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+ [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
+ [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
+ [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
+ [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
};
struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_thread *thread;
+
pa_socket_client *client;
pa_pstream *pstream;
pa_pdispatch *pdispatch;
@@ -134,15 +179,12 @@ struct userdata {
#ifdef TUNNEL_SINK
char *sink_name;
pa_sink *sink;
- uint32_t requested_bytes;
+ int32_t requested_bytes;
#else
char *source_name;
pa_source *source;
#endif
- pa_module *module;
- pa_core *core;
-
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
uint32_t version;
@@ -150,174 +192,487 @@ struct userdata {
uint32_t device_index;
uint32_t channel;
- pa_usec_t host_latency;
+ int64_t counter, counter_delta;
- pa_time_event *time_event;
+ pa_bool_t remote_corked:1;
+ pa_bool_t remote_suspended:1;
- int auth_cookie_in_property;
-};
+ pa_usec_t transport_usec;
+ pa_bool_t transport_usec_valid;
-static void close_stuff(struct userdata *u) {
- assert(u);
+ uint32_t ignore_latency_before;
- if (u->pstream) {
- pa_pstream_close(u->pstream);
- pa_pstream_unref(u->pstream);
- u->pstream = NULL;
- }
+ pa_time_event *time_event;
- if (u->pdispatch) {
- pa_pdispatch_unref(u->pdispatch);
- u->pdispatch = NULL;
- }
+ pa_bool_t auth_cookie_in_property;
- if (u->client) {
- pa_socket_client_unref(u->client);
- u->client = NULL;
- }
+ pa_smoother *smoother;
+ char *device_description;
+ char *server_fqdn;
+ char *user_name;
+
+ uint32_t maxlength;
#ifdef TUNNEL_SINK
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- }
+ uint32_t tlength;
+ uint32_t minreq;
+ uint32_t prebuf;
#else
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
+ uint32_t fragsize;
#endif
+};
- if (u->time_event) {
- u->core->mainloop->time_free(u->time_event);
- u->time_event = NULL;
- }
-}
+static void request_latency(struct userdata *u);
+
+/* Called from main context */
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
-static void die(struct userdata *u) {
- assert(u);
- close_stuff(u);
+ pa_log_warn("Stream killed");
pa_module_unload_request(u->module);
}
-static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
- assert(pd && t && u && u->pdispatch == pd);
- pa_log("stream killed");
- die(u);
-}
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
-static void request_info(struct userdata *u);
+ pa_log_info("Server signalled buffer overrun/underrun.");
+ request_latency(u);
+}
-static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
- pa_subscription_event_type_t e;
- uint32_t idx;
+ uint32_t channel;
+ pa_bool_t suspended;
- assert(pd && t && u);
- assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
- if (pa_tagstruct_getu32(t, &e) < 0 ||
- pa_tagstruct_getu32(t, &idx) < 0 ||
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0 ||
!pa_tagstruct_eof(t)) {
- pa_log("invalid protocol reply");
- die(u);
+ pa_log("Invalid packet");
+ pa_module_unload_request(u->module);
return;
}
#ifdef TUNNEL_SINK
- if (e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
#else
- if (e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
#endif
- request_info(u);
+ request_latency(u);
+}
+
+/* Called from main context */
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
+
+ pa_log_debug("Server reports a stream move.");
+ request_latency(u);
}
#ifdef TUNNEL_SINK
-static void send_prebuf_request(struct userdata *u) {
+
+/* Called from main context */
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
+
+ pa_log_debug("Server reports playback started.");
+ request_latency(u);
+}
+
+#endif
+
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+ pa_usec_t x;
+ pa_assert(u);
+
+ if (u->remote_corked == cork)
+ return;
+
+ u->remote_corked = cork;
+ x = pa_rtclock_usec();
+
+ /* Correct by the time this needs to travel to the other side.
+ * This is a valid thread-safe access, because the main thread is
+ * waiting for us */
+ if (u->transport_usec_valid)
+ x += u->transport_usec;
+
+ if (u->remote_suspended || u->remote_corked)
+ pa_smoother_pause(u->smoother, x);
+ else
+ pa_smoother_resume(u->smoother, x);
+}
+
+/* Called from main context */
+static void stream_cork(struct userdata *u, pa_bool_t cork) {
pa_tagstruct *t;
+ pa_assert(u);
+
+ if (!u->pstream)
+ return;
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
+#else
+ pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
+#endif
pa_tagstruct_putu32(t, u->ctag++);
pa_tagstruct_putu32(t, u->channel);
+ pa_tagstruct_put_boolean(t, !!cork);
pa_pstream_send_tagstruct(u->pstream, t);
+
+ request_latency(u);
}
-static void send_bytes(struct userdata *u) {
- assert(u);
+/* Called from IO thread context */
+static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) {
+ pa_usec_t x;
+ pa_assert(u);
- if (!u->pstream)
+ if (u->remote_suspended == suspend)
return;
+ u->remote_suspended = suspend;
+
+ x = pa_rtclock_usec();
+
+ /* Correct by the time this needed to travel from the other side.
+ * This is a valid thread-safe access, because the main thread is
+ * waiting for us */
+ if (u->transport_usec_valid)
+ x -= u->transport_usec;
+
+ if (u->remote_suspended || u->remote_corked)
+ pa_smoother_pause(u->smoother, x);
+ else
+ pa_smoother_resume(u->smoother, x);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from IO thread context */
+static void send_data(struct userdata *u) {
+ pa_assert(u);
+
while (u->requested_bytes > 0) {
- pa_memchunk chunk;
- if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
+ pa_memchunk memchunk;
+
+ pa_sink_render(u->sink, u->requested_bytes, &memchunk);
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
+ pa_memblock_unref(memchunk.memblock);
+
+ u->requested_bytes -= memchunk.length;
+
+ u->counter += memchunk.length;
+ }
+}
+
+/* This function is called from IO context -- except when it is not. */
+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: {
+ int r;
+
+ /* First, change the state, because otherwide pa_sink_render() would fail */
+ if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
+
+ stream_cork_within_thread(u, u->sink->state == PA_SINK_SUSPENDED);
- if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF)
- send_prebuf_request(u);
+ if (PA_SINK_IS_OPENED(u->sink->state))
+ send_data(u);
+ }
- return;
+ return r;
}
- pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, &chunk);
- pa_memblock_unref(chunk.memblock);
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t yl, yr, *usec = data;
-/* pa_log("sent %lu", (unsigned long) chunk.length); */
+ yl = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+ yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
- if (chunk.length > u->requested_bytes)
- u->requested_bytes = 0;
- else
- u->requested_bytes -= chunk.length;
+ *usec = yl > yr ? yl - yr : 0;
+ return 0;
+ }
+
+ case SINK_MESSAGE_REQUEST:
+
+ pa_assert(offset > 0);
+ u->requested_bytes += (size_t) offset;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ send_data(u);
+
+ return 0;
+
+
+ case SINK_MESSAGE_REMOTE_SUSPEND:
+
+ stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+ return 0;
+
+
+ case SINK_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t y;
+
+ y = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+
+ if (y > (pa_usec_t) offset || offset < 0)
+ y -= offset;
+ else
+ y = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+ return 0;
+ }
+
+ case SINK_MESSAGE_POST:
+
+ /* OK, This might be a bit confusing. This message is
+ * delivered to us from the main context -- NOT from the
+ * IO thread context where the rest of the messages are
+ * dispatched. Yeah, ugly, but I am a lazy bastard. */
+
+ pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
+
+ u->counter_delta += chunk->length;
+
+ 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);
+ u = s->userdata;
+
+ switch ((pa_sink_state_t) state) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_IS_OPENED(s->state));
+ stream_cork(u, TRUE);
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+ if (s->state == PA_SINK_SUSPENDED)
+ stream_cork(u, FALSE);
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ return 0;
+}
+
+#else
+
+/* This function is called from IO context -- except when it is not. */
+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_SINK_MESSAGE_SET_STATE: {
+ int r;
+
+ if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
+ stream_cork_within_thread(u, u->source->state == PA_SOURCE_SUSPENDED);
+
+ return r;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t yr, yl, *usec = data;
+
+ yl = pa_bytes_to_usec(u->counter, &PA_SINK(o)->sample_spec);
+ yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+
+ *usec = yr > yl ? yr - yl : 0;
+ return 0;
+ }
+
+ case SOURCE_MESSAGE_POST:
+
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ pa_source_post(u->source, chunk);
+
+ u->counter += chunk->length;
+
+ return 0;
+
+ case SOURCE_MESSAGE_REMOTE_SUSPEND:
+
+ stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+ return 0;
+
+ case SOURCE_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t y;
+
+ y = pa_bytes_to_usec(u->counter, &u->source->sample_spec);
+
+ if (offset >= 0 || y > (pa_usec_t) -offset)
+ y += offset;
+ else
+ y = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+ return 0;
+ }
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+ struct userdata *u;
+ pa_source_assert_ref(s);
+ u = s->userdata;
+
+ switch ((pa_source_state_t) state) {
+
+ case PA_SOURCE_SUSPENDED:
+ pa_assert(PA_SOURCE_IS_OPENED(s->state));
+ stream_cork(u, TRUE);
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (s->state == PA_SOURCE_SUSPENDED)
+ stream_cork(u, FALSE);
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ ;
}
+
+ return 0;
+}
+
+#endif
+
+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);
+
+ for (;;) {
+ int ret;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 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");
}
-static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+#ifdef TUNNEL_SINK
+/* Called from main context */
+static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
uint32_t bytes, channel;
- assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_REQUEST);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
- pa_tagstruct_getu32(t, &bytes) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid protocol reply");
- die(u);
- return;
+ pa_tagstruct_getu32(t, &bytes) < 0) {
+ pa_log("Invalid protocol reply");
+ goto fail;
}
if (channel != u->channel) {
- pa_log("recieved data for invalid channel");
- die(u);
- return;
+ pa_log("Recieved data for invalid channel");
+ goto fail;
}
- u->requested_bytes += bytes;
- send_bytes(u);
+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
}
#endif
-static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
pa_usec_t sink_usec, source_usec, transport_usec;
- int playing;
+ pa_bool_t playing;
int64_t write_index, read_index;
struct timeval local, remote, now;
- assert(pd && u);
+ pa_sample_spec *ss;
+ int64_t delay;
+
+ pa_assert(pd);
+ pa_assert(u);
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to get latency.");
+ pa_log("Failed to get latency.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
@@ -326,45 +681,96 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_G
pa_tagstruct_get_timeval(t, &local) < 0 ||
pa_tagstruct_get_timeval(t, &remote) < 0 ||
pa_tagstruct_gets64(t, &write_index) < 0 ||
- pa_tagstruct_gets64(t, &read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid reply. (latency)");
- die(u);
+ pa_tagstruct_gets64(t, &read_index) < 0) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+
+#ifdef TUNNEL_SINK
+ if (u->version >= 13) {
+ uint64_t underrun_for = 0, playing_for = 0;
+
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+ }
+#endif
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+
+ if (tag < u->ignore_latency_before) {
+ request_latency(u);
return;
}
pa_gettimeofday(&now);
- /* FIXME! This could use some serious love. */
-
+ /* Calculate transport usec */
if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
/* local and remote seem to have synchronized clocks */
#ifdef TUNNEL_SINK
- transport_usec = pa_timeval_diff(&remote, &local);
+ u->transport_usec = pa_timeval_diff(&remote, &local);
#else
- transport_usec = pa_timeval_diff(&now, &remote);
+ u->transport_usec = pa_timeval_diff(&now, &remote);
#endif
} else
- transport_usec = pa_timeval_diff(&now, &local)/2;
+ u->transport_usec = pa_timeval_diff(&now, &local)/2;
+ u->transport_usec_valid = TRUE;
+ /* First, take the device's delay */
#ifdef TUNNEL_SINK
- u->host_latency = sink_usec + transport_usec;
+ delay = (int64_t) sink_usec;
+ ss = &u->sink->sample_spec;
#else
- u->host_latency = source_usec + transport_usec;
- if (u->host_latency > sink_usec)
- u->host_latency -= sink_usec;
+ delay = (int64_t) source_usec;
+ ss = &u->source->sample_spec;
+#endif
+
+ /* Add the length of our server-side buffer */
+ if (write_index >= read_index)
+ delay += (int64_t) pa_bytes_to_usec(write_index-read_index, ss);
else
- u->host_latency = 0;
+ delay -= (int64_t) pa_bytes_to_usec(read_index-write_index, ss);
+
+ /* Our measurements are already out of date, hence correct by the *
+ * transport latency */
+#ifdef TUNNEL_SINK
+ delay -= (int64_t) transport_usec;
+#else
+ delay += (int64_t) transport_usec;
+#endif
+
+ /* Now correct by what we have have read/written since we requested the update */
+#ifdef TUNNEL_SINK
+ delay += (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#else
+ delay -= (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#endif
+
+#ifdef TUNNEL_SINK
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#else
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
#endif
-/* pa_log("estimated host latency: %0.0f usec", (double) u->host_latency); */
+ return;
+
+fail:
+
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void request_latency(struct userdata *u) {
pa_tagstruct *t;
struct timeval now;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
#ifdef TUNNEL_SINK
@@ -380,34 +786,154 @@ static void request_latency(struct userdata *u) {
pa_pstream_send_tagstruct(u->pstream, t);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
+
+ u->ignore_latency_before = tag;
+ u->counter_delta = 0;
}
-static void stream_get_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
- uint32_t idx, owner_module, monitor_source;
- pa_usec_t latency;
+ struct timeval ntv;
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(u);
+
+ request_latency(u);
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += LATENCY_INTERVAL;
+ m->time_restart(e, &ntv);
+}
+
+/* Called from main context */
+static void update_description(struct userdata *u) {
+ char *d;
+ char un[128], hn[128];
+ pa_tagstruct *t;
+
+ pa_assert(u);
+
+ if (!u->server_fqdn || !u->user_name || !u->device_description)
+ return;
+
+ d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
+
+#ifdef TUNNEL_SINK
+ pa_sink_set_description(u->sink, d);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
+#else
+ pa_source_set_description(u->source, d);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
+#endif
+
+ pa_xfree(d);
+
+ d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
+
+ t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+#else
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
+#endif
+ pa_tagstruct_putu32(t, u->ctag++);
+ pa_tagstruct_putu32(t, u->channel);
+ pa_tagstruct_puts(t, d);
+ pa_pstream_send_tagstruct(u->pstream, t);
+
+ pa_xfree(d);
+}
+
+/* Called from main context */
+static void server_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_sample_spec ss;
+ const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
+ uint32_t cookie;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
+ }
+
+ if (pa_tagstruct_gets(t, &server_name) < 0 ||
+ pa_tagstruct_gets(t, &server_version) < 0 ||
+ pa_tagstruct_gets(t, &user_name) < 0 ||
+ pa_tagstruct_gets(t, &host_name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_gets(t, &default_sink_name) < 0 ||
+ pa_tagstruct_gets(t, &default_source_name) < 0 ||
+ pa_tagstruct_getu32(t, &cookie) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_xfree(u->server_fqdn);
+ u->server_fqdn = pa_xstrdup(host_name);
+
+ pa_xfree(u->user_name);
+ u->user_name = pa_xstrdup(user_name);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void sink_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, monitor_source, flags;
const char *name, *description, *monitor_source_name, *driver;
- int mute;
- uint32_t flags;
- pa_sample_spec sample_spec;
- pa_channel_map channel_map;
+ pa_sample_spec ss;
+ pa_channel_map cm;
pa_cvolume volume;
- assert(pd && u);
+ pa_bool_t mute;
+ pa_usec_t latency;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to get info.");
+ pa_log("Failed to get info.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_gets(t, &description) < 0 ||
- pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
- pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
pa_tagstruct_getu32(t, &owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &volume) < 0 ||
pa_tagstruct_get_boolean(t, &mute) < 0 ||
@@ -415,133 +941,378 @@ static void stream_get_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_
pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &latency) < 0 ||
pa_tagstruct_gets(t, &driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid reply. (get_info)");
- die(u);
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 13) {
+ pa_usec_t configured_latency;
+
+ if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+ pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (!u->sink_name || strcmp(name, u->sink_name))
return;
+
+ pa_xfree(u->device_description);
+ u->device_description = pa_xstrdup(description);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
+}
+
+/* Called from main context */
+static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, client, sink;
+ pa_usec_t buffer_usec, sink_usec;
+ const char *name, *driver, *resample_method;
+ pa_bool_t mute;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
}
-#ifdef TUNNEL_SINK
- assert(u->sink);
- if ((!!mute == !!u->sink->hw_muted) &&
- pa_cvolume_equal(&volume, &u->sink->hw_volume))
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_getu32(t, &owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &client) < 0 ||
+ pa_tagstruct_getu32(t, &sink) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+ pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+ pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+ pa_tagstruct_gets(t, &resample_method) < 0 ||
+ pa_tagstruct_gets(t, &driver) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 11) {
+ if (pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (u->version >= 13) {
+ if (pa_tagstruct_get_proplist(t, pl) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (idx != u->device_index)
return;
-#else
- assert(u->source);
- if ((!!mute == !!u->source->hw_muted) &&
- pa_cvolume_equal(&volume, &u->source->hw_volume))
+
+ pa_assert(u->sink);
+
+ if ((u->version < 11 || !!mute == !!u->sink->muted) &&
+ pa_cvolume_equal(&volume, &u->sink->volume))
return;
-#endif
-#ifdef TUNNEL_SINK
- memcpy(&u->sink->hw_volume, &volume, sizeof(pa_cvolume));
- u->sink->hw_muted = !!mute;
+ memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+
+ if (u->version >= 11)
+ u->sink->muted = !!mute;
+
+ pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
+}
- pa_subscription_post(u->sink->core,
- PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->sink->index);
#else
- memcpy(&u->source->hw_volume, &volume, sizeof(pa_cvolume));
- u->source->hw_muted = !!mute;
- pa_subscription_post(u->source->core,
- PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->source->index);
-#endif
+/* Called from main context */
+static void source_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, monitor_of_sink, flags;
+ const char *name, *description, *monitor_of_sink_name, *driver;
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ pa_cvolume volume;
+ pa_bool_t mute;
+ pa_usec_t latency, configured_latency;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
+ }
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &owner_module) < 0 ||
+ pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
+ pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
+ pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
+ pa_tagstruct_get_usec(t, &latency) < 0 ||
+ pa_tagstruct_gets(t, &driver) < 0 ||
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 13) {
+ if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+ pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (!u->source_name || strcmp(name, u->source_name))
+ return;
+
+ pa_xfree(u->device_description);
+ u->device_description = pa_xstrdup(description);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
}
+#endif
+
+/* Called from main context */
static void request_info(struct userdata *u) {
pa_tagstruct *t;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
+
#ifdef TUNNEL_SINK
- pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
+
+ if (u->sink_name) {
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, u->sink_name);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
+ }
#else
- pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ if (u->source_name) {
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, u->source_name);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
+ }
#endif
- pa_tagstruct_putu32(t, tag = u->ctag++);
+}
+
+/* Called from main context */
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_subscription_event_type_t e;
+ uint32_t idx;
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+
+ if (pa_tagstruct_getu32(t, &e) < 0 ||
+ pa_tagstruct_getu32(t, &idx) < 0) {
+ pa_log("Invalid protocol reply");
+ pa_module_unload_request(u->module);
+ return;
+ }
+
+ if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
#ifdef TUNNEL_SINK
- pa_tagstruct_puts(t, u->sink_name);
+ e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+ e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
#else
- pa_tagstruct_puts(t, u->source_name);
+ e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
#endif
+ )
+ return;
- pa_pstream_send_tagstruct(u->pstream, t);
- pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_info_callback, u, NULL);
+ request_info(u);
}
+/* Called from main context */
static void start_subscribe(struct userdata *u) {
pa_tagstruct *t;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
+ pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
#ifdef TUNNEL_SINK
- pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SINK);
+ PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
#else
- pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SOURCE);
+ PA_SUBSCRIPTION_MASK_SOURCE
#endif
+ );
pa_pstream_send_tagstruct(u->pstream, t);
}
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+/* Called from main context */
+static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
struct timeval ntv;
- assert(m && e && u);
-
- request_latency(u);
-
- pa_gettimeofday(&ntv);
- ntv.tv_sec += LATENCY_INTERVAL;
- m->time_restart(e, &ntv);
-}
+#ifdef TUNNEL_SINK
+ uint32_t bytes;
+#endif
-static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct userdata *u = userdata;
- struct timeval ntv;
- assert(pd && u && u->pdispatch == pd);
+ pa_assert(pd);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to create stream.");
+ pa_log("Failed to create stream.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
pa_tagstruct_getu32(t, &u->device_index) < 0
#ifdef TUNNEL_SINK
- || pa_tagstruct_getu32(t, &u->requested_bytes) < 0
+ || pa_tagstruct_getu32(t, &bytes) < 0
#endif
)
goto parse_error;
if (u->version >= 9) {
#ifdef TUNNEL_SINK
- uint32_t maxlength, tlength, prebuf, minreq;
+ if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->tlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &u->minreq) < 0)
+ goto parse_error;
+#else
+ if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->fragsize) < 0)
+ goto parse_error;
+#endif
+ }
- if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
- pa_tagstruct_getu32(t, &tlength) < 0 ||
- pa_tagstruct_getu32(t, &prebuf) < 0 ||
- pa_tagstruct_getu32(t, &minreq) < 0)
+ if (u->version >= 12) {
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ uint32_t device_index;
+ const char *dn;
+ pa_bool_t suspended;
+
+ if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &device_index) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0)
goto parse_error;
+
+#ifdef TUNNEL_SINK
+ pa_xfree(u->sink_name);
+ u->sink_name = pa_xstrdup(dn);
#else
- uint32_t maxlength, fragsize;
+ pa_xfree(u->source_name);
+ u->source_name = pa_xstrdup(dn);
+#endif
+ }
+
+ if (u->version >= 13) {
+ pa_usec_t usec;
- if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
- pa_tagstruct_getu32(t, &fragsize) < 0)
+ if (pa_tagstruct_get_usec(t, &usec) < 0)
goto parse_error;
+
+#ifdef TUNNEL_SINK
+ pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0);
+#else
+ pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0);
#endif
}
@@ -551,23 +1322,30 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
start_subscribe(u);
request_info(u);
- assert(!u->time_event);
+ pa_assert(!u->time_event);
pa_gettimeofday(&ntv);
ntv.tv_sec += LATENCY_INTERVAL;
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u);
request_latency(u);
+
+ pa_log_debug("Stream created.");
+
#ifdef TUNNEL_SINK
- send_bytes(u);
+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
#endif
return;
parse_error:
- pa_log("invalid reply. (create stream)");
- die(u);
+ pa_log("Invalid reply. (Create stream)");
+
+fail:
+ pa_module_unload_request(u->module);
+
}
+/* Called from main context */
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
pa_tagstruct *reply;
@@ -575,123 +1353,218 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
#ifdef TUNNEL_SINK
pa_cvolume volume;
#endif
- assert(pd && u && u->pdispatch == pd);
+
+ pa_assert(pd);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (command != PA_COMMAND_REPLY ||
pa_tagstruct_getu32(t, &u->version) < 0 ||
!pa_tagstruct_eof(t)) {
+
if (command == PA_COMMAND_ERROR)
- pa_log("failed to authenticate");
+ pa_log("Failed to authenticate");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+
+ goto fail;
}
/* Minimum supported protocol version */
if (u->version < 8) {
- pa_log("incompatible protocol version");
- die(u);
- return;
+ pa_log("Incompatible protocol version");
+ goto fail;
}
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is enabled for this connection or not. We don't
+ support SHM here at all, so we just ignore this. */
+
+ if (u->version >= 13)
+ u->version &= 0x7FFFFFFFU;
+
+ pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
+
#ifdef TUNNEL_SINK
- 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);
+ pa_snprintf(name, sizeof(name), "%s for %s@%s",
+ u->sink_name,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
#else
- pa_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);
+ pa_snprintf(name, sizeof(name), "%s for %s@%s",
+ u->source_name,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
#endif
reply = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version >= 13) {
+ pa_proplist *pl;
+ pl = pa_proplist_new();
+ pa_init_proplist(pl);
+ pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+ pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+ pa_tagstruct_put_proplist(reply, pl);
+ pa_proplist_free(pl);
+ } else
+ pa_tagstruct_puts(reply, "PulseAudio");
+
pa_pstream_send_tagstruct(u->pstream, reply);
/* We ignore the server's reply here */
reply = pa_tagstruct_new(NULL, 0);
+
+ if (u->version < 13)
+ /* Only for older PA versions we need to fill in the maxlength */
+ u->maxlength = 4*1024*1024;
+
+#ifdef TUNNEL_SINK
+ u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
+ u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
+ u->prebuf = u->tlength;
+#else
+ u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
+#endif
+
#ifdef TUNNEL_SINK
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version < 13)
+ pa_tagstruct_puts(reply, name);
+
pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->sink_name);
- pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
- pa_tagstruct_put_boolean(reply, 0);
- pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
- pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
- pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
+ pa_tagstruct_putu32(reply, u->maxlength);
+ pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+ pa_tagstruct_putu32(reply, u->tlength);
+ pa_tagstruct_putu32(reply, u->prebuf);
+ pa_tagstruct_putu32(reply, u->minreq);
pa_tagstruct_putu32(reply, 0);
pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
pa_tagstruct_put_cvolume(reply, &volume);
#else
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version < 13)
+ pa_tagstruct_puts(reply, name);
+
pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->source_name);
- pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
- pa_tagstruct_put_boolean(reply, 0);
- pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
+ pa_tagstruct_putu32(reply, u->maxlength);
+ pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
+ pa_tagstruct_putu32(reply, u->fragsize);
+#endif
+
+ if (u->version >= 12) {
+ pa_tagstruct_put_boolean(reply, FALSE); /* no_remap */
+ pa_tagstruct_put_boolean(reply, FALSE); /* no_remix */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_format */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_rate */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_channels */
+ pa_tagstruct_put_boolean(reply, TRUE); /* no_move */
+ pa_tagstruct_put_boolean(reply, FALSE); /* variable_rate */
+ }
+
+ if (u->version >= 13) {
+ pa_proplist *pl;
+
+ pa_tagstruct_put_boolean(reply, FALSE); /* start muted/peak detect*/
+ pa_tagstruct_put_boolean(reply, TRUE); /* adjust_latency */
+
+ pl = pa_proplist_new();
+ pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
+ pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
+ pa_tagstruct_put_proplist(reply, pl);
+ pa_proplist_free(pl);
+
+#ifndef TUNNEL_SINK
+ pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
#endif
+ }
pa_pstream_send_tagstruct(u->pstream, reply);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
+
+ pa_log_debug("Connection authenticated, creating stream ...");
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void pstream_die_callback(pa_pstream *p, void *userdata) {
struct userdata *u = userdata;
- assert(p && u);
- pa_log("stream died.");
- die(u);
+ pa_assert(p);
+ pa_assert(u);
+
+ pa_log_warn("Stream died.");
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
struct userdata *u = userdata;
- assert(p && packet && u);
+
+ pa_assert(p);
+ pa_assert(packet);
+ pa_assert(u);
if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
- pa_log("invalid packet");
- die(u);
+ pa_log("Invalid packet");
+ pa_module_unload_request(u->module);
+ return;
}
}
#ifndef TUNNEL_SINK
-static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED int64_t offset, PA_GCC_UNUSED pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+/* Called from main context */
+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 userdata *u = userdata;
- assert(p && chunk && u);
+
+ pa_assert(p);
+ pa_assert(chunk);
+ pa_assert(u);
if (channel != u->channel) {
- pa_log("recieved memory block on bad channel.");
- die(u);
+ pa_log("Recieved memory block on bad channel.");
+ pa_module_unload_request(u->module);
return;
}
- pa_source_post(u->source, chunk);
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
+
+ u->counter_delta += chunk->length;
}
+
#endif
+/* Called from main context */
static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
struct userdata *u = userdata;
pa_tagstruct *t;
uint32_t tag;
- assert(sc && u && u->client == sc);
+
+ pa_assert(sc);
+ pa_assert(u);
+ pa_assert(u->client == sc);
pa_socket_client_unref(u->client);
u->client = NULL;
if (!io) {
- pa_log("connection failed.");
+ pa_log("Connection failed: %s", pa_cstrerror(errno));
pa_module_unload_request(u->module);
return;
}
@@ -710,164 +1583,83 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
pa_tagstruct_putu32(t, tag = u->ctag++);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
- pa_pstream_send_tagstruct(u->pstream, t);
- pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
-}
+#ifdef HAVE_CREDS
+{
+ pa_creds ucred;
-#ifdef TUNNEL_SINK
-static void sink_notify(pa_sink*sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
+ if (pa_iochannel_creds_supported(io))
+ pa_iochannel_creds_enable(io);
- send_bytes(u);
-}
+ ucred.uid = getuid();
+ ucred.gid = getgid();
-static pa_usec_t sink_get_latency(pa_sink *sink) {
- struct userdata *u;
- uint32_t l;
- pa_usec_t usec = 0;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- l = DEFAULT_TLENGTH;
-
- if (l > u->requested_bytes) {
- l -= u->requested_bytes;
- usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
- }
-
- usec += u->host_latency;
-
- return usec;
+ pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
}
-
-static int sink_get_hw_volume(pa_sink *sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- return 0;
-}
-
-static int sink_set_hw_volume(pa_sink *sink) {
- struct userdata *u;
- pa_tagstruct *t;
- uint32_t tag;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
- pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->sink_name);
- pa_tagstruct_put_cvolume(t, &sink->hw_volume);
+#else
pa_pstream_send_tagstruct(u->pstream, t);
+#endif
- return 0;
-}
-
-static int sink_get_hw_mute(pa_sink *sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
- return 0;
+ pa_log_debug("Connection established, authenticating ...");
}
-static int sink_set_hw_mute(pa_sink *sink) {
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static int sink_set_volume(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
- assert(sink && sink->userdata);
+
+ pa_assert(sink);
u = sink->userdata;
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_MUTE);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->sink_name);
- pa_tagstruct_put_boolean(t, !!sink->hw_muted);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_tagstruct_put_cvolume(t, &sink->volume);
pa_pstream_send_tagstruct(u->pstream, t);
return 0;
}
-#else
-static pa_usec_t source_get_latency(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
-
- return u->host_latency;
-}
-
-static int source_get_hw_volume(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
-
- return 0;
-}
-
-static int source_set_hw_volume(pa_source *source) {
+/* Called from main context */
+static int sink_set_mute(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
- assert(source && source->userdata);
- u = source->userdata;
-
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SOURCE_VOLUME);
- pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->source_name);
- pa_tagstruct_put_cvolume(t, &source->hw_volume);
- pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
-}
-
-static int source_get_hw_mute(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
- return 0;
-}
+ pa_assert(sink);
+ u = sink->userdata;
+ pa_assert(u);
-static int source_set_hw_mute(pa_source *source) {
- struct userdata *u;
- pa_tagstruct *t;
- uint32_t tag;
- assert(source && source->userdata);
- u = source->userdata;
+ if (u->version < 11)
+ return -1;
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SOURCE_MUTE);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->source_name);
- pa_tagstruct_put_boolean(t, !!source->hw_muted);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_tagstruct_put_boolean(t, !!sink->muted);
pa_pstream_send_tagstruct(u->pstream, t);
return 0;
}
+
#endif
+/* Called from main context */
static int load_key(struct userdata *u, const char*fn) {
- assert(u);
+ pa_assert(u);
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
- pa_log_debug("using already loaded auth cookie.");
+ pa_log_debug("Using already loaded auth cookie.");
pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
u->auth_cookie_in_property = 1;
return 0;
@@ -879,32 +1671,36 @@ 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;
+ u->auth_cookie_in_property = TRUE;
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
- char *t, *dn = NULL;
+ char *dn = NULL;
+#ifdef TUNNEL_SINK
+ pa_sink_new_data data;
+#else
+ pa_source_new_data data;
+#endif
- 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;
}
- u = pa_xmalloc(sizeof(struct userdata));
- m->userdata = u;
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
u->module = m;
- u->core = c;
u->client = NULL;
u->pdispatch = NULL;
u->pstream = NULL;
@@ -917,34 +1713,39 @@ int pa__init(pa_core *c, pa_module*m) {
u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
u->source = NULL;
#endif
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->ctag = 1;
u->device_index = u->channel = PA_INVALID_INDEX;
- u->host_latency = 0;
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
u->time_event = NULL;
+ u->ignore_latency_before = 0;
+ u->transport_usec = 0;
+ u->transport_usec_valid = FALSE;
+ u->remote_suspended = u->remote_corked = FALSE;
+ u->counter = u->counter_delta = 0;
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
- pa_log("no server specified.");
+ pa_log("No server specified.");
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");
goto fail;
}
- if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
- pa_log("failed to connect to server '%s'", u->server_name);
+ if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+ pa_log("Failed to connect to server '%s'", u->server_name);
goto fail;
}
- if (!u->client)
- goto fail;
-
pa_socket_client_set_callback(u->client, on_connection, u);
#ifdef TUNNEL_SINK
@@ -952,55 +1753,104 @@ int pa__init(pa_core *c, pa_module*m) {
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
- if (!(u->sink = pa_sink_new(c, __FILE__, dn, 0, &ss, &map))) {
- pa_log("failed to create sink.");
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ data.namereg_fail = TRUE;
+ pa_sink_new_data_set_name(&data, dn);
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
+ pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+ if (u->sink_name)
+ pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
goto fail;
}
- u->sink->get_latency = sink_get_latency;
- u->sink->get_hw_volume = sink_get_hw_volume;
- u->sink->set_hw_volume = sink_set_hw_volume;
- u->sink->get_hw_mute = sink_get_hw_mute;
- u->sink->set_hw_mute = sink_set_hw_mute;
- u->sink->notify = sink_notify;
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name));
- pa_xfree(t);
+ u->sink->set_state = sink_set_state;
+ u->sink->set_volume = sink_set_volume;
+ u->sink->set_mute = sink_set_mute;
+
+ u->sink->refresh_volume = u->sink->refresh_muted = FALSE;
+
+ pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0);
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
- pa_sink_set_owner(u->sink, m);
#else
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
- if (!(u->source = pa_source_new(c, __FILE__, dn, 0, &ss, &map))) {
- pa_log("failed to create source.");
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ data.namereg_fail = TRUE;
+ pa_source_new_data_set_name(&data, dn);
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
+ pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+ if (u->source_name)
+ pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log("Failed to create source.");
goto fail;
}
- u->source->get_latency = source_get_latency;
- u->source->get_hw_volume = source_get_hw_volume;
- u->source->set_hw_volume = source_set_hw_volume;
- u->source->get_hw_mute = source_get_hw_mute;
- u->source->set_hw_mute = source_set_hw_mute;
+ u->source->parent.process_msg = source_process_msg;
+ u->source->set_state = source_set_state;
u->source->userdata = u;
- pa_source_set_description(u->source, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name));
- pa_xfree(t);
+ pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0);
- pa_source_set_owner(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
#endif
pa_xfree(dn);
u->time_event = NULL;
+ u->maxlength = 0;
+#ifdef TUNNEL_SINK
+ u->tlength = u->minreq = u->prebuf = 0;
+#else
+ u->fragsize = 0;
+#endif
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+#ifdef TUNNEL_SINK
+ pa_sink_put(u->sink);
+#else
+ pa_source_put(u->source);
+#endif
+
pa_modargs_free(ma);
return 0;
fail:
- pa__done(c, m);
+ pa__done(m);
if (ma)
pa_modargs_free(ma);
@@ -1010,17 +1860,59 @@ 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;
- close_stuff(u);
+#ifdef TUNNEL_SINK
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+#else
+ if (u->source)
+ pa_source_unlink(u->source);
+#endif
+
+ 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);
+
+#ifdef TUNNEL_SINK
+ if (u->sink)
+ pa_sink_unref(u->sink);
+#else
+ if (u->source)
+ pa_source_unref(u->source);
+#endif
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->pstream) {
+ pa_pstream_unlink(u->pstream);
+ pa_pstream_unref(u->pstream);
+ }
+
+ if (u->pdispatch)
+ pa_pdispatch_unref(u->pdispatch);
+
+ if (u->client)
+ pa_socket_client_unref(u->client);
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);
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ if (u->time_event)
+ u->core->mainloop->time_free(u->time_event);
#ifdef TUNNEL_SINK
pa_xfree(u->sink_name);
@@ -1029,7 +1921,9 @@ void pa__done(pa_core *c, pa_module*m) {
#endif
pa_xfree(u->server_name);
+ pa_xfree(u->device_description);
+ pa_xfree(u->server_fqdn);
+ pa_xfree(u->user_name);
+
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index 77e6174f..d862c203 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulse/xmalloc.h>
#include <pulse/volume.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@@ -48,34 +47,45 @@
#include "module-volume-restore-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "table=<filename> "
+ "restore_device=<Restore the device for each stream?> "
+ "restore_volume=<Restore the volume for each stream?>"
+);
#define WHITESPACE "\n\r \t"
-
#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
+#define SAVE_INTERVAL 10
static const char* const valid_modargs[] = {
"table",
+ "restore_device",
+ "restore_volume",
NULL,
};
struct rule {
char* name;
- int volume_is_set;
+ pa_bool_t volume_is_set;
pa_cvolume volume;
- char *sink;
- char *source;
+ char *sink, *source;
};
struct userdata {
+ pa_core *core;
pa_hashmap *hashmap;
pa_subscription *subscription;
- pa_hook_slot *sink_input_hook_slot, *source_output_hook_slot;
- int modified;
+ pa_hook_slot
+ *sink_input_new_hook_slot,
+ *sink_input_fixate_hook_slot,
+ *source_output_new_hook_slot;
+ pa_bool_t modified;
char *table_file;
+ pa_time_event *save_time_event;
};
static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
@@ -90,7 +100,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
return NULL;
k = strtol(s, &p, 0);
- if (k <= 0 || k > PA_CHANNELS_MAX)
+ if (k <= 0 || k > (long) PA_CHANNELS_MAX)
return NULL;
v->channels = (unsigned) k;
@@ -103,7 +113,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
k = strtol(p, &p, 0);
- if (k < PA_VOLUME_MUTED)
+ if (k < (long) PA_VOLUME_MUTED)
return NULL;
v->values[i] = (pa_volume_t) k;
@@ -122,16 +132,12 @@ static int load_rules(struct userdata *u) {
char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
char *ln = buf_name;
- f = u->table_file ?
- fopen(u->table_file, "r") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
-
- if (!f) {
+ if (!(f = fopen(u->table_file, "r"))) {
if (errno == ENOENT) {
- pa_log_info("starting with empty ruleset.");
+ pa_log_info("Starting with empty ruleset.");
ret = 0;
} else
- pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+ pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@@ -141,7 +147,7 @@ static int load_rules(struct userdata *u) {
while (!feof(f)) {
struct rule *rule;
pa_cvolume v;
- int v_is_set;
+ pa_bool_t v_is_set;
if (!fgets(ln, sizeof(buf_name), f))
break;
@@ -176,9 +182,9 @@ static int load_rules(struct userdata *u) {
goto finish;
}
- v_is_set = 1;
+ v_is_set = TRUE;
} else
- v_is_set = 0;
+ v_is_set = FALSE;
ln = buf_name;
@@ -219,12 +225,13 @@ static int save_rules(struct userdata *u) {
void *state = NULL;
struct rule *rule;
- f = u->table_file ?
- fopen(u->table_file, "w") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
+ if (!u->modified)
+ return 0;
+
+ pa_log_info("Saving rules...");
- if (!f) {
- pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+ if (!(f = fopen(u->table_file, "w"))) {
+ pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@@ -248,6 +255,8 @@ static int save_rules(struct userdata *u) {
}
ret = 0;
+ u->modified = FALSE;
+ pa_log_debug("Successfully saved rules...");
finish:
if (f) {
@@ -261,10 +270,10 @@ finish:
static char* client_name(pa_client *c) {
char *t, *e;
- if (!c->name || !c->driver)
+ if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
return NULL;
- t = pa_sprintf_malloc("%s$%s", c->driver, c->name);
+ t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
t[strcspn(t, "\n\r#")] = 0;
if (!*t) {
@@ -288,6 +297,21 @@ static char* client_name(pa_client *c) {
return t;
}
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ save_rules(u);
+}
+
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
pa_sink_input *si = NULL;
@@ -328,15 +352,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
pa_log_info("Saving volume for <%s>", r->name);
r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = 1;
- u->modified = 1;
+ r->volume_is_set = TRUE;
+ u->modified = TRUE;
}
if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
pa_log_info("Saving sink for <%s>", r->name);
pa_xfree(r->sink);
r->sink = pa_xstrdup(si->sink->name);
- u->modified = 1;
+ u->modified = TRUE;
}
} else {
pa_assert(so);
@@ -345,7 +369,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
pa_log_info("Saving source for <%s>", r->name);
pa_xfree(r->source);
r->source = pa_xstrdup(so->source->name);
- u->modified = 1;
+ u->modified = TRUE;
}
}
@@ -357,27 +381,61 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (si) {
r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = 1;
+ r->volume_is_set = TRUE;
r->sink = pa_xstrdup(si->sink->name);
r->source = NULL;
} else {
pa_assert(so);
- r->volume_is_set = 0;
+ r->volume_is_set = FALSE;
r->sink = NULL;
r->source = pa_xstrdup(so->source->name);
}
pa_hashmap_put(u->hashmap, r->name, r);
- u->modified = 1;
+ u->modified = TRUE;
+ }
+
+ if (u->modified && !u->save_time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
}
}
-static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
struct rule *r;
char *name;
pa_assert(data);
+ /* In the NEW hook we only adjust the device. Adjusting the volume
+ * is left for the FIXATE hook */
+
+ if (!data->client || !(name = client_name(data->client)))
+ return PA_HOOK_OK;
+
+ if ((r = pa_hashmap_get(u->hashmap, name))) {
+ if (!data->sink && r->sink) {
+ if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
+ pa_log_info("Restoring sink for <%s>", r->name);
+ }
+ }
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+ struct rule *r;
+ char *name;
+
+ pa_assert(data);
+
+ /* In the FIXATE hook we only adjust the volum. Adjusting the device
+ * is left for the NEW hook */
+
if (!data->client || !(name = client_name(data->client)))
return PA_HOOK_OK;
@@ -387,11 +445,6 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
pa_log_info("Restoring volume for <%s>", r->name);
pa_sink_input_new_data_set_volume(data, &r->volume);
}
-
- if (!data->sink && r->sink) {
- if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
- pa_log_info("Restoring sink for <%s>", r->name);
- }
}
pa_xfree(name);
@@ -399,7 +452,7 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
return PA_HOOK_OK;
}
-static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
struct rule *r;
char *name;
@@ -421,6 +474,7 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
+ pa_bool_t restore_device = TRUE, restore_volume = TRUE;
pa_assert(m);
@@ -430,20 +484,41 @@ int pa__init(pa_module*m) {
}
u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->modified = FALSE;
u->subscription = NULL;
- u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
- u->modified = 0;
- u->sink_input_hook_slot = u->source_output_hook_slot = NULL;
+ u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
+ u->save_time_event = NULL;
m->userdata = u;
+ if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE))))
+ goto fail;
+
+ if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
+ pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
+ pa_log("restore_volume= and restore_device= expect boolean arguments");
+ goto fail;
+ }
+
+ if (!(restore_device || restore_volume)) {
+ pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
+ goto fail;
+ }
+
if (load_rules(u) < 0)
goto fail;
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);
+
+ if (restore_device) {
+ u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+ u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
+ }
+
+ if (restore_volume)
+ u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
pa_modargs_free(ma);
return 0;
@@ -477,21 +552,21 @@ void pa__done(pa_module*m) {
if (u->subscription)
pa_subscription_free(u->subscription);
- if (u->sink_input_hook_slot)
- pa_hook_slot_free(u->sink_input_hook_slot);
- if (u->source_output_hook_slot)
- pa_hook_slot_free(u->source_output_hook_slot);
+ if (u->sink_input_new_hook_slot)
+ pa_hook_slot_free(u->sink_input_new_hook_slot);
+ if (u->sink_input_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+ if (u->source_output_new_hook_slot)
+ pa_hook_slot_free(u->source_output_new_hook_slot);
if (u->hashmap) {
-
- if (u->modified)
- save_rules(u);
-
+ save_rules(u);
pa_hashmap_free(u->hashmap, free_func, NULL);
}
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
pa_xfree(u->table_file);
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
index ad3645fc..b452c3bf 100644
--- a/src/modules/module-waveout.c
+++ b/src/modules/module-waveout.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,7 +26,6 @@
#include <windows.h>
#include <mmsystem.h>
-#include <assert.h>
#include <pulse/mainloop-api.h>
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
index e59db83c..f7be48f7 100644
--- a/src/modules/module-x11-bell.c
+++ b/src/modules/module-x11-bell.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -44,14 +42,25 @@
#include "module-x11-bell-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Bell interceptor")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 bell interceptor");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "sample",
+ "display",
+ NULL
+};
struct userdata {
pa_core *core;
+ pa_module *module;
+
int xkb_event_base;
+
char *sink_name;
char *scache_item;
@@ -59,17 +68,10 @@ struct userdata {
pa_x11_client *x11_client;
};
-static const char* const valid_modargs[] = {
- "sink",
- "sample",
- "display",
- NULL
-};
-
-static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
+static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
XkbBellNotifyEvent *bne;
struct userdata *u = userdata;
-
+
pa_assert(w);
pa_assert(e);
pa_assert(u);
@@ -80,7 +82,7 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
bne = (XkbBellNotifyEvent*) e;
- if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, (bne->percent*PA_VOLUME_NORM)/100, 1) < 0) {
+ if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 0) {
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);
}
@@ -88,13 +90,32 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
return 1;
}
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_modargs *ma = NULL;
int major, minor;
unsigned int auto_ctrls, auto_values;
-
+
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
@@ -104,7 +125,8 @@ int pa__init(pa_module*m) {
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->module = m;
+ u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system"));
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
u->x11_client = NULL;
@@ -132,7 +154,7 @@ int pa__init(pa_module*m) {
XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values);
XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0);
- u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u);
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u);
pa_modargs_free(ma);
@@ -143,20 +165,18 @@ fail:
pa_modargs_free(ma);
pa__done(m);
-
+
return -1;
}
void pa__done(pa_module*m) {
struct userdata *u;
-
+
pa_assert(m);
- if (!m->userdata)
+ if (!(u = 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 7da9cc2d..705d90f4 100644
--- a/src/modules/module-x11-publish.c
+++ b/src/modules/module-x11-publish.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -53,10 +51,11 @@
#include "module-x11-publish-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Credential Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("display=<X11 display>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 credential publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("display=<X11 display>");
static const char* const valid_modargs[] = {
"display",
@@ -68,16 +67,39 @@ static const char* const valid_modargs[] = {
struct userdata {
pa_core *core;
- pa_x11_wrapper *x11_wrapper;
+ pa_module *module;
+
char *id;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
+ pa_bool_t auth_cookie_in_property;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
};
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
static int load_key(struct userdata *u, const char*fn) {
pa_assert(u);
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
pa_log_debug("using already loaded auth cookie.");
@@ -95,7 +117,7 @@ static int load_key(struct userdata *u, const char*fn) {
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;
+ u->auth_cookie_in_property = TRUE;
return 0;
}
@@ -110,16 +132,19 @@ int pa__init(pa_module*m) {
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));
+ m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
+ u->module = m;
u->id = NULL;
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
@@ -130,7 +155,10 @@ int pa__init(pa_module*m) {
if (!(l = pa_property_get(m->core, PA_NATIVE_SERVER_PROPERTY_NAME)))
goto fail;
+ l = pa_strlist_reverse(l);
s = pa_strlist_tostring(l);
+ l = pa_strlist_reverse(l);
+
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s);
pa_xfree(s);
@@ -148,7 +176,10 @@ int pa__init(pa_module*m) {
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx)));
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
pa_modargs_free(ma);
+
return 0;
fail:
@@ -156,17 +187,21 @@ fail:
pa_modargs_free(ma);
pa__done(m);
+
return -1;
}
void pa__done(pa_module*m) {
struct userdata*u;
-
+
pa_assert(m);
if (!(u = m->userdata))
return;
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
if (u->x11_wrapper) {
char t[256];
@@ -181,10 +216,9 @@ void pa__done(pa_module*m) {
pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE");
XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False);
}
- }
- if (u->x11_wrapper)
pa_x11_wrapper_unref(u->x11_wrapper);
+ }
if (u->auth_cookie_in_property)
pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
@@ -192,4 +226,3 @@ void pa__done(pa_module*m) {
pa_xfree(u->id);
pa_xfree(u);
}
-
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
index 4ef437a1..696826d8 100644
--- a/src/modules/module-x11-xsmp.c
+++ b/src/modules/module-x11-xsmp.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,26 +40,44 @@
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/x11wrap.h>
#include "module-x11-xsmp-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 session management")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 session management");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>");
-static int ice_in_use = 0;
+static pa_bool_t ice_in_use = FALSE;
static const char* const valid_modargs[] = {
+ "session_manager",
+ "display",
NULL
};
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_client *client;
+ SmcConn connection;
+ pa_x11_wrapper *x11;
+};
+
static void die_cb(SmcConn connection, SmPointer client_data){
- pa_core *c = PA_CORE(client_data);
+ struct userdata *u = client_data;
+ pa_assert(u);
+
+ pa_log_debug("Got die message from XSMP.");
+
+ pa_x11_wrapper_kill(u->x11);
+
+ pa_x11_wrapper_unref(u->x11);
+ u->x11 = NULL;
- pa_log_debug("Got die message from XSM. Exiting...");
-
- pa_core_assert_ref(c);
- c->mainloop->quit(c->mainloop, 0);
+ pa_module_unload_request(u->module);
}
static void save_complete_cb(SmcConn connection, SmPointer client_data) {
@@ -85,12 +101,15 @@ static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_fla
}
static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
- pa_core *c = client_data;
+ struct 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);
+ *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);
}
@@ -98,50 +117,63 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
- char t[256], *vendor, *client_id;
+ char t[256], *vendor, *client_id, *k;
SmcCallbacks callbacks;
SmProp prop_program, prop_user;
SmProp *prop_list[2];
SmPropValue val_program, val_user;
- SmcConn connection;
-
+ struct userdata *u;
+ const char *e;
+
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;
+ ice_in_use = TRUE;
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->client = NULL;
+ u->connection = NULL;
+ u->x11 = NULL;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
- if (!getenv("SESSION_MANAGER")) {
+ if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+ goto fail;
+
+ e = pa_modargs_get_value(ma, "session_manager", NULL);
+
+ if (!e && !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.die.client_data = u;
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,
+
+ if (!(u->connection = SmcOpenConnection(
+ (char*) e, 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;
}
@@ -163,12 +195,19 @@ int pa__init(pa_module*m) {
prop_user.vals = &val_user;
prop_list[1] = &prop_user;
- SmcSetProperties(connection, PA_ELEMENTSOF(prop_list), prop_list);
+ SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
+
+ pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
+ k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
+ u->client = pa_client_new(u->core, __FILE__, k);
+ pa_xfree(k);
+
+ pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
+ pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
- 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;
@@ -176,20 +215,33 @@ int pa__init(pa_module*m) {
fail:
if (ma)
pa_modargs_free(ma);
-
+
pa__done(m);
-
+
return -1;
}
void pa__done(pa_module*m) {
+ struct userdata *u;
+
pa_assert(m);
- if (m->userdata)
- SmcCloseConnection(m->userdata, 0, NULL);
+ if ((u = m->userdata)) {
+
+ if (u->connection)
+ SmcCloseConnection(u->connection, 0, NULL);
+
+ if (u->client)
+ pa_client_free(u->client);
+
+ if (u->x11)
+ pa_x11_wrapper_unref(u->x11);
+
+ pa_xfree(u);
+ }
if (ice_in_use) {
IceRemoveConnectionWatch(new_ice_connection, m->core);
- ice_in_use = 0;
+ ice_in_use = FALSE;
}
}
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
new file mode 100644
index 00000000..2fc81370
--- /dev/null
+++ b/src/modules/module-zeroconf-discover.c
@@ -0,0 +1,438 @@
+/***
+ 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 <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-zeroconf-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_non-monitor._sub._pulse-source._tcp"
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct tunnel {
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ char *name, *type, *domain;
+ uint32_t module_index;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *source_browser, *sink_browser;
+
+ pa_hashmap *tunnels;
+};
+
+static unsigned tunnel_hash(const void *p) {
+ const struct tunnel *t = p;
+
+ return
+ (unsigned) t->interface +
+ (unsigned) t->protocol +
+ pa_idxset_string_hash_func(t->name) +
+ pa_idxset_string_hash_func(t->type) +
+ pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+ const struct tunnel *ta = a, *tb = b;
+ int r;
+
+ if (ta->interface != tb->interface)
+ return 1;
+ if (ta->protocol != tb->protocol)
+ return 1;
+ if ((r = strcmp(ta->name, tb->name)))
+ return r;
+ if ((r = strcmp(ta->type, tb->type)))
+ return r;
+ if ((r = strcmp(ta->domain, tb->domain)))
+ return r;
+
+ return 0;
+}
+
+static struct tunnel *tunnel_new(
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ const char *name, const char *type, const char *domain) {
+
+ struct tunnel *t;
+ t = pa_xnew(struct tunnel, 1);
+ t->interface = interface;
+ t->protocol = protocol;
+ t->name = pa_xstrdup(name);
+ t->type = pa_xstrdup(type);
+ t->domain = pa_xstrdup(domain);
+ t->module_index = PA_IDXSET_INVALID;
+ return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+ pa_assert(t);
+ pa_xfree(t->name);
+ pa_xfree(t->type);
+ pa_xfree(t->domain);
+ pa_xfree(t);
+}
+
+static void resolver_cb(
+ AvahiServiceResolver *r,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name, const char *type, const char *domain,
+ const char *host_name, const AvahiAddress *a, uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *tnl;
+
+ pa_assert(u);
+
+ tnl = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+ else {
+ char *device = NULL, *dname, *module_name, *args;
+ const char *t;
+ char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ AvahiStringList *l;
+ pa_bool_t channel_map_set = FALSE;
+ pa_module *m;
+
+ ss = u->core->default_sample_spec;
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+ for (l = txt; l; l = l->next) {
+ char *key, *value;
+ pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+ if (strcmp(key, "device") == 0) {
+ pa_xfree(device);
+ device = value;
+ value = NULL;
+ } else if (strcmp(key, "rate") == 0)
+ ss.rate = atoi(value);
+ else if (strcmp(key, "channels") == 0)
+ ss.channels = atoi(value);
+ else if (strcmp(key, "format") == 0)
+ ss.format = pa_parse_sample_format(value);
+ else if (strcmp(key, "channel_map") == 0) {
+ pa_channel_map_parse(&cm, value);
+ channel_map_set = TRUE;
+ }
+
+ avahi_free(key);
+ avahi_free(value);
+ }
+
+ if (!channel_map_set && cm.channels != ss.channels)
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log("Service '%s' contains an invalid sample specification.", name);
+ avahi_free(device);
+ goto finish;
+ }
+
+ if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
+ pa_log("Service '%s' contains an invalid channel map.", name);
+ avahi_free(device);
+ goto finish;
+ }
+
+ if (device)
+ dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
+ else
+ dname = pa_sprintf_malloc("tunnel.%s", host_name);
+
+ if (!pa_namereg_is_valid_name(dname)) {
+ pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+ avahi_free(device);
+ pa_xfree(dname);
+ goto finish;
+ }
+
+ t = strstr(type, "sink") ? "sink" : "source";
+
+ module_name = pa_sprintf_malloc("module-tunnel-%s", t);
+ args = pa_sprintf_malloc("server=[%s]:%u "
+ "%s=%s "
+ "format=%s "
+ "channels=%u "
+ "rate=%u "
+ "%s_name=%s "
+ "channel_map=%s",
+ avahi_address_snprint(at, sizeof(at), a), port,
+ t, device,
+ pa_sample_format_to_string(ss.format),
+ ss.channels,
+ ss.rate,
+ t, dname,
+ pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
+
+ pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+ if ((m = pa_module_load(u->core, module_name, args))) {
+ tnl->module_index = m->index;
+ pa_hashmap_put(u->tunnels, tnl, tnl);
+ tnl = NULL;
+ }
+
+ pa_xfree(module_name);
+ pa_xfree(dname);
+ pa_xfree(args);
+ avahi_free(device);
+ }
+
+finish:
+
+ avahi_service_resolver_free(r);
+
+ if (tnl)
+ tunnel_free(tnl);
+}
+
+static void browser_cb(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name, const char *type, const char *domain,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *t;
+
+ pa_assert(u);
+
+ if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+ return;
+
+ t = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event == AVAHI_BROWSER_NEW) {
+
+ if (!pa_hashmap_get(u->tunnels, t))
+ if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+ pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+ /* We ignore the returned resolver object here, since the we don't
+ * need to attach any special data to it, and we can still destory
+ * it from the callback */
+
+ } else if (event == AVAHI_BROWSER_REMOVE) {
+ struct tunnel *t2;
+
+ if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+ pa_module_unload_by_index(u->core, t2->module_index);
+ pa_hashmap_remove(u->tunnels, t2);
+ tunnel_free(t2);
+ }
+ }
+
+ tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ u->client = c;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+
+ if (!u->sink_browser) {
+
+ if (!(u->sink_browser = avahi_service_browser_new(
+ c,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SINK,
+ NULL,
+ 0,
+ browser_cb, u))) {
+
+ pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ if (!u->source_browser) {
+
+ if (!(u->source_browser = avahi_service_browser_new(
+ c,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SOURCE,
+ NULL,
+ 0,
+ browser_cb, u))) {
+
+ pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+ int error;
+
+ pa_log_debug("Avahi daemon disconnected.");
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ /* Fall through */
+
+ case AVAHI_CLIENT_CONNECTING:
+
+ if (u->sink_browser) {
+ avahi_service_browser_free(u->sink_browser);
+ u->sink_browser = NULL;
+ }
+
+ if (u->source_browser) {
+ avahi_service_browser_free(u->source_browser);
+ u->source_browser = NULL;
+ }
+
+ break;
+
+ default: ;
+ }
+}
+
+int pa__init(pa_module*m) {
+
+ struct userdata *u;
+ pa_modargs *ma = NULL;
+ int error;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->sink_browser = u->source_browser = NULL;
+
+ u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+ u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ goto fail;
+ }
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata*u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->client)
+ avahi_client_free(u->client);
+
+ if (u->avahi_poll)
+ pa_avahi_poll_free(u->avahi_poll);
+
+ if (u->tunnels) {
+ struct tunnel *t;
+
+ while ((t = pa_hashmap_steal_first(u->tunnels))) {
+ pa_module_unload_by_index(u->core, t->module_index);
+ tunnel_free(t);
+ }
+
+ pa_hashmap_free(u->tunnels, NULL, NULL);
+ }
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
index 113686cf..cb9c1285 100644
--- a/src/modules/module-zeroconf-publish.c
+++ b/src/modules/module-zeroconf-publish.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -52,29 +50,44 @@
#include "module-zeroconf-publish-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("port=<IP port number>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("port=<IP port number>");
#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+#define SERVICE_SUBTYPE_SINK_HARDWARE "_hardware._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SINK_VIRTUAL "_virtual._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SOURCE_HARDWARE "_hardware._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_VIRTUAL "_virtual._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE
static const char* const valid_modargs[] = {
"port",
NULL
};
+enum service_subtype {
+ SUBTYPE_HARDWARE,
+ SUBTYPE_VIRTUAL,
+ SUBTYPE_MONITOR
+};
+
struct service {
struct userdata *userdata;
AvahiEntryGroup *entry_group;
char *service_name;
pa_object *device;
+ enum service_subtype subtype;
};
struct userdata {
pa_core *core;
+ pa_module *module;
AvahiPoll *avahi_poll;
AvahiClient *client;
@@ -88,10 +101,11 @@ struct userdata {
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 service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, const char **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, enum service_subtype *ret_subtype) {
pa_assert(s);
pa_assert(ret_ss);
pa_assert(ret_description);
+ pa_assert(ret_subtype);
if (pa_sink_isinstance(s->device)) {
pa_sink *sink = PA_SINK(s->device);
@@ -99,7 +113,8 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann
*ret_ss = sink->sample_spec;
*ret_map = sink->channel_map;
*ret_name = sink->name;
- *ret_description = sink->description;
+ *ret_description = pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+ *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
} else if (pa_source_isinstance(s->device)) {
pa_source *source = PA_SOURCE(s->device);
@@ -107,7 +122,9 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann
*ret_ss = source->sample_spec;
*ret_map = source->channel_map;
*ret_name = source->name;
- *ret_description = source->description;
+ *ret_description = pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+ *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
} else
pa_assert_not_reached();
}
@@ -174,6 +191,13 @@ static int publish_service(struct service *s) {
pa_sample_spec ss;
pa_channel_map map;
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ enum service_subtype subtype;
+
+ const char * const subtype_text[] = {
+ [SUBTYPE_HARDWARE] = "hardware",
+ [SUBTYPE_VIRTUAL] = "virtual",
+ [SUBTYPE_MONITOR] = "monitor"
+ };
pa_assert(s);
@@ -190,12 +214,13 @@ static int publish_service(struct service *s) {
txt = txt_record_server_data(s->userdata->core, txt);
- get_service_data(s, &ss, &map, &name, &description);
+ get_service_data(s, &ss, &map, &name, &description, &subtype);
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, "subtype", subtype_text[subtype]);
if (avahi_entry_group_add_service_strlst(
s->entry_group,
@@ -212,6 +237,35 @@ static int publish_service(struct service *s) {
goto finish;
}
+ if (avahi_entry_group_add_service_subtype(
+ 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,
+ pa_sink_isinstance(s->device) ? (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+ (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+
+ pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
+ }
+
+ if (pa_source_isinstance(s->device) && subtype != SUBTYPE_MONITOR) {
+ if (avahi_entry_group_add_service_subtype(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ 0,
+ s->service_name,
+ SERVICE_TYPE_SOURCE,
+ NULL,
+ SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) {
+
+ pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
+ }
+ }
+
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;
@@ -248,10 +302,10 @@ static struct service *get_service(struct userdata *u, pa_object *device) {
s->device = device;
if (pa_sink_isinstance(device)) {
- if (!(n = PA_SINK(device)->description))
+ if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
n = PA_SINK(device)->name;
} else {
- if (!(n = PA_SOURCE(device)->description))
+ if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
n = PA_SOURCE(device)->name;
}
@@ -280,11 +334,24 @@ static void service_free(struct service *s) {
pa_xfree(s);
}
+static pa_bool_t shall_ignore(pa_object *o) {
+ pa_object_assert_ref(o);
+
+ if (pa_sink_isinstance(o))
+ return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
+
+ if (pa_source_isinstance(o))
+ return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
+
+ pa_assert_not_reached();
+}
+
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);
- publish_service(get_service(u, o));
+ if (!shall_ignore(o))
+ publish_service(get_service(u, o));
return PA_HOOK_OK;
}
@@ -394,10 +461,12 @@ static int publish_all_services(struct userdata *u) {
pa_log_debug("Publishing services in Zeroconf");
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 (!shall_ignore(PA_OBJECT(sink)))
+ publish_service(get_service(u, PA_OBJECT(sink)));
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 (!shall_ignore(PA_OBJECT(source)))
+ publish_service(get_service(u, PA_OBJECT(source)));
if (publish_main_service(u) < 0)
goto fail;
@@ -468,8 +537,10 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
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)))
- pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_module_unload_request(u->module);
+ }
}
break;
@@ -487,36 +558,37 @@ int pa__init(pa_module*m) {
int error;
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_u32(ma, "port", &port) < 0 || port <= 0 || port > 0xFFFF) {
- pa_log("invalid port specified.");
+ pa_log("Invalid port specified.");
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
+ u->module = m;
u->port = (uint16_t) port;
u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- 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->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (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_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+ u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (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_LATE, (pa_hook_cb_t) device_unlink_cb, u);
u->main_entry_group = NULL;
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));
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
goto fail;
}
diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c
index 25e45a35..2791e165 100644
--- a/src/modules/oss-util.c
+++ b/src/modules/oss-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -164,6 +162,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
[PA_SAMPLE_S16BE] = AFMT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
};
pa_assert(fd >= 0);
@@ -249,7 +249,7 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
return 0;
}
-int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
@@ -271,7 +271,7 @@ int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *v
return 0;
}
-int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
pa_volume_t l, r;
@@ -293,29 +293,39 @@ int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvo
}
static int get_device_number(const char *dev) {
- char buf[PATH_MAX];
const char *p, *e;
+ char *rp = NULL;
+ int r;
- if (readlink(dev, buf, sizeof(buf)) < 0) {
- if (errno != EINVAL && errno != ENOLINK)
- return -1;
+ if (!(p = rp = pa_readlink(dev))) {
+ if (errno != EINVAL && errno != ENOLINK) {
+ r = -1;
+ goto finish;
+ }
p = dev;
- } else
- p = buf;
+ }
if ((e = strrchr(p, '/')))
p = e+1;
- if (p == 0)
- return 0;
+ if (p == 0) {
+ r = 0;
+ goto finish;
+ }
p = strchr(p, 0) -1;
- if (*p >= '0' && *p <= '9')
- return *p - '0';
+ if (*p >= '0' && *p <= '9') {
+ r = *p - '0';
+ goto finish;
+ }
- return -1;
+ r = -1;
+
+finish:
+ pa_xfree(rp);
+ return r;
}
int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h
index 259a622a..654f7bba 100644
--- a/src/modules/oss-util.h
+++ b/src/modules/oss-util.h
@@ -1,8 +1,6 @@
#ifndef fooossutilhfoo
#define fooossutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,8 +31,8 @@ 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_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_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 6c018931..cff5cf8b 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -51,6 +51,7 @@
#include <pulsecore/atomic.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/atomic.h>
+#include <pulsecore/time-smoother.h>
#include "module-rtp-recv-symdef.h"
@@ -58,19 +59,22 @@
#include "sdp.h"
#include "sap.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink=<name of the sink> "
"sap_address=<multicast address to listen on> "
-)
+);
#define SAP_PORT 9875
#define DEFAULT_SAP_ADDRESS "224.0.0.56"
-#define MEMBLOCKQ_MAXLENGTH (1024*170)
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*40)
#define MAX_SESSIONS 16
#define DEATH_TIMEOUT 20
+#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC)
+#define LATENCY_USEC (500*PA_USEC_PER_MSEC)
static const char* const valid_modargs[] = {
"sink",
@@ -96,6 +100,12 @@ struct session {
pa_rtpoll_item *rtpoll_item;
pa_atomic_t timestamp;
+
+ pa_smoother *smoother;
+ pa_usec_t intended_latency;
+ pa_usec_t sink_latency;
+
+ pa_usec_t last_rate_update;
};
struct userdata {
@@ -132,21 +142,37 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t
}
/* Called from I/O thread context */
-static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct session *s;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
- return pa_memblockq_peek(s->memblockq, chunk);
+ if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+ return -1;
+
+ pa_memblockq_drop(s->memblockq, chunk->length);
+
+ return 0;
}
/* Called from I/O thread context */
-static void sink_input_drop(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
+
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
- pa_memblockq_drop(s->memblockq, length);
+ pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct session *s;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
/* Called from main context */
@@ -214,20 +240,82 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE);
+ pa_rtclock_get(&now);
+
+ pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));
+
if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
- /* queue overflow, let's flush it and try again */
- pa_memblockq_flush(s->memblockq);
- pa_memblockq_push(s->memblockq, &chunk);
+ pa_log_warn("Queue overrun");
+ pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE);
}
- /* The next timestamp we expect */
- s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size);
+ pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq));
pa_memblock_unref(chunk.memblock);
- pa_rtclock_get(&now);
+ /* The next timestamp we expect */
+ s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size);
+
pa_atomic_store(&s->timestamp, now.tv_sec);
+ if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
+ pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix;
+ unsigned fix_samples;
+
+ pa_log("Updating sample rate");
+
+ wi = pa_smoother_get(s->smoother, pa_timeval_load(&now));
+ ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
+
+ if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)
+ sink_delay = 0;
+
+ render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
+
+ if (ri > render_delay+sink_delay)
+ ri -= render_delay+sink_delay;
+ else
+ ri = 0;
+
+ if (wi < ri)
+ latency = 0;
+ else
+ latency = wi - ri;
+
+ pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC);
+
+ /* Calculate deviation */
+ if (latency < s->intended_latency)
+ fix = s->intended_latency - latency;
+ else
+ fix = latency - s->intended_latency;
+
+ /* How many samples is this per second? */
+ fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL;
+
+ /* Check if deviation is in bounds */
+ if (fix_samples > s->sink_input->sample_spec.rate*.20)
+ pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples);
+
+ /* Fix up rate */
+ if (latency < s->intended_latency)
+ s->sink_input->sample_spec.rate -= fix_samples;
+ else
+ s->sink_input->sample_spec.rate += fix_samples;
+
+ pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
+
+ pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate);
+
+ s->last_rate_update = pa_timeval_load(&now);
+ }
+
+ if (pa_memblockq_is_readable(s->memblockq) &&
+ s->sink_input->thread_info.underrun_for > 0) {
+ pa_log_debug("Requesting rewind due to end of underrun");
+ pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+ }
+
return 1;
}
@@ -313,10 +401,9 @@ fail:
static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
struct session *s = NULL;
- char *c;
pa_sink *sink;
int fd = -1;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_sink_input_new_data data;
struct timeval now;
@@ -328,37 +415,46 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail;
}
- if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
pa_log("Sink does not exist.");
goto fail;
}
+ pa_rtclock_get(&now);
+
s = pa_xnew0(struct session, 1);
s->userdata = u;
s->first_packet = FALSE;
s->sdp_info = *sdp_info;
s->rtpoll_item = NULL;
-
- pa_rtclock_get(&now);
+ s->intended_latency = LATENCY_USEC;
+ s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);
+ pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));
+ s->last_rate_update = pa_timeval_load(&now);
pa_atomic_store(&s->timestamp, now.tv_sec);
if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
goto fail;
- c = pa_sprintf_malloc("RTP Stream%s%s%s",
- sdp_info->session_name ? " (" : "",
- sdp_info->session_name ? sdp_info->session_name : "",
- sdp_info->session_name ? ")" : "");
-
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = c;
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
+ "RTP Stream%s%s%s",
+ sdp_info->session_name ? " (" : "",
+ sdp_info->session_name ? sdp_info->session_name : "",
+ sdp_info->session_name ? ")" : "");
+
+ if (sdp_info->session_name)
+ pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name);
+ pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin);
+ pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload);
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->module->core, &data, 0);
- pa_xfree(c);
+ pa_sink_input_new_data_done(&data);
if (!s->sink_input) {
pa_log("Failed to create sink input.");
@@ -368,27 +464,31 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
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->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill;
s->sink_input->attach = sink_input_attach;
s->sink_input->detach = sink_input_detach;
- 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));
+ pa_sink_input_get_silence(s->sink_input, &silence);
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2);
+
+ if (s->intended_latency < s->sink_latency*2)
+ s->intended_latency = s->sink_latency*2;
s->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
MEMBLOCKQ_MAXLENGTH,
pa_frame_size(&s->sink_input->sample_spec),
- pa_bytes_per_second(&s->sink_input->sample_spec)/10+1,
+ pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec),
+ 0,
0,
- silence);
+ &silence);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
@@ -428,12 +528,14 @@ static void session_free(struct session *s) {
pa_sdp_info_destroy(&s->sdp_info);
pa_rtp_context_destroy(&s->rtp_context);
+ pa_smoother_free(s->smoother);
+
pa_xfree(s);
}
static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
struct userdata *u = userdata;
- int goodbye;
+ pa_bool_t goodbye = FALSE;
pa_sdp_info info;
struct session *s;
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index f79867c2..d0d06c4d 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -57,9 +55,10 @@
#include "sdp.h"
#include "sap.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source=<name of the source> "
"format=<sample format> "
@@ -69,7 +68,7 @@ PA_MODULE_USAGE(
"port=<port number> "
"mtu=<maximum transfer unit> "
"loop=<loopback to local host?>"
-)
+);
#define DEFAULT_PORT 46000
#define SAP_PORT 9875
@@ -111,7 +110,7 @@ static int source_output_process_msg(pa_msgobject *o, int code, void *data, int6
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;
@@ -178,11 +177,11 @@ int pa__init(pa_module*m) {
pa_source_output *o = NULL;
uint8_t payload;
char *p;
- int r;
+ int r, j;
socklen_t k;
struct timeval tv;
char hn[128], *n;
- int loop = 0;
+ pa_bool_t loop = FALSE;
pa_source_output_new_data data;
pa_assert(m);
@@ -273,8 +272,9 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0 ||
- setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ j = !!loop;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 ||
+ setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) {
pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -286,14 +286,20 @@ int pa__init(pa_module*m) {
pa_make_fd_cloexec(sap_fd);
pa_source_output_new_data_init(&data);
- data.name = "RTP Monitor Stream";
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
+ pa_proplist_sets(data.proplist, "rtp.destination", dest);
+ pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
+ pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
data.driver = __FILE__;
data.module = m;
data.source = s;
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
- if (!(o = pa_source_output_new(m->core, &data, 0))) {
+ o = pa_source_output_new(m->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!o) {
pa_log("failed to create source output.");
goto fail;
}
@@ -316,6 +322,7 @@ int pa__init(pa_module*m) {
pa_frame_size(&ss),
1,
0,
+ 0,
NULL);
u->mtu = mtu;
@@ -345,7 +352,7 @@ int pa__init(pa_module*m) {
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);
return 0;
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 60df7274..5a33ebc2 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,6 +53,8 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr
c->payload = payload & 127;
c->frame_size = frame_size;
+ pa_memchunk_reset(&c->memchunk);
+
return c;
}
@@ -78,24 +78,24 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
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;
pa_assert(chunk.memblock);
-
+
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 ++;
-
+
n += k;
pa_memblockq_drop(q, k);
}
pa_assert(n % c->frame_size == 0);
-
+
if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {
uint32_t header[3];
struct msghdr m;
@@ -152,6 +152,8 @@ pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame
c->fd = fd;
c->frame_size = frame_size;
+
+ pa_memchunk_reset(&c->memchunk);
return c;
}
@@ -173,12 +175,28 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
goto fail;
}
- if (!size)
+ if (size <= 0)
return 0;
- chunk->memblock = pa_memblock_new(pool, size);
+ if (c->memchunk.length < (unsigned) size) {
+ size_t l;
+
+ if (c->memchunk.memblock)
+ pa_memblock_unref(c->memchunk.memblock);
+
+ l = PA_MAX((size_t) size, pa_mempool_block_size_max(pool));
+
+ c->memchunk.memblock = pa_memblock_new(pool, l);
+ c->memchunk.index = 0;
+ c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock);
+ }
+
+ pa_assert(c->memchunk.length >= (size_t) size);
+
+ chunk->memblock = pa_memblock_ref(c->memchunk.memblock);
+ chunk->index = c->memchunk.index;
- iov.iov_base = pa_memblock_acquire(chunk->memblock);
+ iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
iov.iov_len = size;
m.msg_name = NULL;
@@ -191,11 +209,11 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
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;
}
@@ -236,14 +254,22 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
goto fail;
}
- chunk->index = 12 + cc*4;
- chunk->length = size - chunk->index;
+ chunk->index += 12 + cc*4;
+ chunk->length = size - 12 + cc*4;
if (chunk->length % c->frame_size != 0) {
pa_log_warn("Bad RTP packet size.");
goto fail;
}
+ c->memchunk.index = chunk->index + chunk->length;
+ c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock) - c->memchunk.index;
+
+ if (c->memchunk.length <= 0) {
+ pa_memblock_unref(c->memchunk.memblock);
+ pa_memchunk_reset(&c->memchunk);
+ }
+
return 0;
fail:
@@ -329,7 +355,10 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
void pa_rtp_context_destroy(pa_rtp_context *c) {
pa_assert(c);
- pa_close(c->fd);
+ pa_assert_se(pa_close(c->fd) == 0);
+
+ if (c->memchunk.memblock)
+ pa_memblock_unref(c->memchunk.memblock);
}
const char* pa_rtp_format_to_string(pa_sample_format_t f) {
@@ -361,4 +390,3 @@ pa_sample_format_t pa_rtp_string_to_format(const char *s) {
else
return PA_SAMPLE_INVALID;
}
-
diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h
index ad7175ca..a2728f05 100644
--- a/src/modules/rtp/rtp.h
+++ b/src/modules/rtp/rtp.h
@@ -1,8 +1,6 @@
#ifndef foortphfoo
#define foortphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,6 +35,8 @@ typedef struct pa_rtp_context {
uint32_t ssrc;
uint8_t payload;
size_t frame_size;
+
+ pa_memchunk memchunk;
} pa_rtp_context;
pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size);
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index ed7eb0be..5d9b58fa 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,7 +69,7 @@ void pa_sap_context_destroy(pa_sap_context *c) {
pa_xfree(c->sdp_data);
}
-int pa_sap_send(pa_sap_context *c, int goodbye) {
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {
uint32_t header;
struct sockaddr_storage sa_buf;
struct sockaddr *sa = (struct sockaddr*) &sa_buf;
@@ -127,7 +125,7 @@ pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
return c;
}
-int pa_sap_recv(pa_sap_context *c, int *goodbye) {
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {
struct msghdr m;
struct iovec iov;
int size, k;
diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h
index f906a32b..69c757cb 100644
--- a/src/modules/rtp/sap.h
+++ b/src/modules/rtp/sap.h
@@ -1,8 +1,6 @@
#ifndef foosaphfoo
#define foosaphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,9 +38,9 @@ typedef struct pa_sap_context {
pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data);
void pa_sap_context_destroy(pa_sap_context *c);
-int pa_sap_send(pa_sap_context *c, int goodbye);
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye);
pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd);
-int pa_sap_recv(pa_sap_context *c, int *goodbye);
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye);
#endif
diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c
index 50ac157a..cef90433 100644
--- a/src/modules/rtp/sdp.c
+++ b/src/modules/rtp/sdp.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -117,7 +115,7 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
uint16_t port = 0;
- int ss_valid = 0;
+ pa_bool_t ss_valid = FALSE;
pa_assert(t);
pa_assert(i);
@@ -202,7 +200,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
i->payload = (uint8_t) _payload;
if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
- ss_valid = 1;
+ ss_valid = TRUE;
}
}
} else if (pa_startswith(t, "a=rtpmap:")) {
@@ -222,7 +220,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
c[strcspn(c, "\n")] = 0;
if (parse_sdp_sample_spec(&i->sample_spec, c))
- ss_valid = 1;
+ ss_valid = TRUE;
}
}
}
diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h
index 7c91fca6..933a602b 100644
--- a/src/modules/rtp/sdp.h
+++ b/src/modules/rtp/sdp.h
@@ -1,8 +1,6 @@
#ifndef foosdphfoo
#define foosdphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/.gitignore b/src/pulse/.gitignore
new file mode 100644
index 00000000..67020331
--- /dev/null
+++ b/src/pulse/.gitignore
@@ -0,0 +1 @@
+version.h
diff --git a/src/pulse/browser.c b/src/pulse/browser.c
index af56e47d..1a3f657f 100644
--- a/src/pulse/browser.c
+++ b/src/pulse/browser.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,7 @@
struct pa_browser {
PA_REFCNT_DECLARE;
-
+
pa_mainloop_api *mainloop;
AvahiPoll* avahi_poll;
@@ -63,7 +61,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))
@@ -313,10 +311,15 @@ static void client_callback(AvahiClient *s, AvahiClientState state, void *userda
static void browser_free(pa_browser *b);
+
+PA_WARN_REFERENCE(pa_browser_new, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
}
+PA_WARN_REFERENCE(pa_browser_new_full, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
pa_browser *b;
int error;
@@ -420,6 +423,8 @@ static void browser_free(pa_browser *b) {
pa_xfree(b);
}
+PA_WARN_REFERENCE(pa_browser_ref, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_ref(pa_browser *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@@ -428,6 +433,8 @@ pa_browser *pa_browser_ref(pa_browser *b) {
return b;
}
+PA_WARN_REFERENCE(pa_browser_unref, "libpulse-browse is being phased out.");
+
void pa_browser_unref(pa_browser *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@@ -436,6 +443,8 @@ void pa_browser_unref(pa_browser *b) {
browser_free(b);
}
+PA_WARN_REFERENCE(pa_browser_set_callback, "libpulse-browse is being phased out.");
+
void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@@ -444,6 +453,8 @@ void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
b->userdata = userdata;
}
+PA_WARN_REFERENCE(pa_browser_set_error_callback, "libpulse-browse is being phased out.");
+
void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
index b039ca33..c4e0a17e 100644
--- a/src/pulse/browser.h
+++ b/src/pulse/browser.h
@@ -1,8 +1,6 @@
#ifndef foobrowserhfoo
#define foobrowserhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h
index e1f23d25..8c5b2d0f 100644
--- a/src/pulse/cdecl.h
+++ b/src/pulse/cdecl.h
@@ -1,8 +1,6 @@
#ifndef foopulsecdeclhfoo
#define foopulsecdeclhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -41,22 +39,4 @@
#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 2b8ef2b0..7348b32e 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -36,7 +34,7 @@
#include "channelmap.h"
-const char *const table[] = {
+const char *const table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
@@ -99,7 +97,7 @@ const char *const table[] = {
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
};
-const char *const pretty_table[] = {
+const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "Mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center",
@@ -396,6 +394,34 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
}
}
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+ unsigned c;
+
+ pa_assert(m);
+ pa_assert(channels > 0);
+ pa_assert(channels <= PA_CHANNELS_MAX);
+
+ pa_channel_map_init(m);
+
+ for (c = channels; c > 0; c--) {
+
+ if (pa_channel_map_init_auto(m, c, def)) {
+ unsigned i = 0;
+
+ for (; c < channels; c++) {
+
+ m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
+ i++;
+ }
+
+ m->channels = channels;
+
+ return m;
+ }
+ }
+
+ return NULL;
+}
const char* pa_channel_position_to_string(pa_channel_position_t pos) {
@@ -531,4 +557,3 @@ int pa_channel_map_valid(const pa_channel_map *map) {
return 1;
}
-
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index a05e1911..2551eae9 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -1,8 +1,6 @@
#ifndef foochannelmaphfoo
#define foochannelmaphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,6 +25,7 @@
#include <pulse/sample.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \page channelmap Channel Maps
*
@@ -167,10 +166,18 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
/** Initialize the specified channel map for stereophonic audio and return a pointer to it */
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
-/** Initialize the specified channel map for the specified number
- * of channels using default labels and return a pointer to it. */
+/** Initialize the specified channel map for the specified number of
+ * channels using default labels and return a pointer to it. This call
+ * will fail (return NULL) if there is no default channel map known for this
+ * specific number of channels and mapping. */
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+/** Similar to pa_channel_map_init_auto() but instead of failing if no
+ * default mapping is known with the specified parameters it will
+ * synthesize a mapping based on a known mapping with fewer channels
+ * and fill up the rest with AUX0...AUX31 channels \since 0.9.11 */
+pa_channel_map* pa_channel_map_init_extend(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) PA_GCC_PURE;
@@ -183,7 +190,7 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
/** Make a humand readable string from the specified channel map */
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
-/** Parse a channel position list into a channel map structure. \since 0.8.1 */
+/** Parse a channel position list into a channel map structure. */
pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
/** Compare two channel maps. Return 1 if both match. */
diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c
index e240ba88..393a7cd3 100644
--- a/src/pulse/client-conf-x11.c
+++ b/src/pulse/client-conf-x11.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,7 +44,10 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
pa_assert(c);
- if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0'))
+ if (!dname && !(dname = getenv("DISPLAY")))
+ goto finish;
+
+ if (*dname == 0)
goto finish;
if (!(d = XOpenDisplay(dname))) {
@@ -80,7 +81,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
pa_assert(sizeof(cookie) == sizeof(c->cookie));
memcpy(c->cookie, cookie, sizeof(cookie));
- c->cookie_valid = 1;
+ c->cookie_valid = TRUE;
pa_xfree(c->cookie_file);
c->cookie_file = NULL;
diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h
index 56cd406d..f2f40e03 100644
--- a/src/pulse/client-conf-x11.h
+++ b/src/pulse/client-conf-x11.h
@@ -1,8 +1,6 @@
#ifndef fooclientconfx11hfoo
#define fooclientconfx11hfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index abd277a6..2ead871f 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -58,10 +56,10 @@ static const pa_client_conf default_conf = {
.default_sink = NULL,
.default_source = NULL,
.default_server = NULL,
- .autospawn = 0,
- .disable_shm = 0,
+ .autospawn = TRUE,
+ .disable_shm = FALSE,
.cookie_file = NULL,
- .cookie_valid = 0,
+ .cookie_valid = FALSE,
};
pa_client_conf *pa_client_conf_new(void) {
@@ -112,13 +110,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm;
- f = filename ?
- fopen((fn = pa_xstrdup(filename)), "r") :
- pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
+ if (filename) {
+
+ if (!(f = fopen(filename, "r"))) {
+ pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ goto finish;
+ }
+
+ fn = pa_xstrdup(fn);
+
+ } else {
- if (!f && errno != EINTR) {
- pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
- goto finish;
+ if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
+ if (errno != ENOENT)
+ goto finish;
}
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
@@ -126,7 +131,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
if (!r)
r = pa_client_conf_load_cookie(c);
-
finish:
pa_xfree(fn);
@@ -172,15 +176,14 @@ int pa_client_conf_env(pa_client_conf *c) {
int pa_client_conf_load_cookie(pa_client_conf* c) {
pa_assert(c);
- c->cookie_valid = 0;
-
if (!c->cookie_file)
return -1;
+ c->cookie_valid = FALSE;
+
if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
return -1;
- c->cookie_valid = 1;
+ c->cookie_valid = TRUE;
return 0;
}
-
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
index 6de64582..699279aa 100644
--- a/src/pulse/client-conf.h
+++ b/src/pulse/client-conf.h
@@ -1,8 +1,6 @@
#ifndef fooclientconfhfoo
#define fooclientconfhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,9 +28,9 @@
typedef struct pa_client_conf {
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
- int autospawn, disable_shm;
+ pa_bool_t autospawn, disable_shm;
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
- int cookie_valid; /* non-zero, when cookie is valid */
+ pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
} pa_client_conf;
/* Create a new configuration data object and reset it to defaults */
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
index 3cfd9760..749e9688 100644
--- a/src/pulse/client.conf.in
+++ b/src/pulse/client.conf.in
@@ -1,5 +1,3 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
@@ -17,29 +15,18 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-## Configuration file for pulseaudio clients. Default values are
-## commented out. Use either ; or # for commenting
-
-## Path to the pulseaudio daemon to run when autospawning.
-; daemon-binary = @PA_BINARY@
-
-## Extra arguments to pass to the pulseaudio daemon
-; extra-arguments = --log-target=syslog --exit-idle-time=5
+## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
+## more information. Default values a commented out. Use either ; or # for
+## commenting.
-## The default sink to connect to
-; default-sink =
-
-## The default source to connect to
+; default-sink =
; default-source =
-
-## The default sever to connect to
; default-server =
-## Autospawn daemons?
-; autospawn = 0
+; autospawn = yes
+; daemon-binary = @PA_BINARY@
+; extra-arguments = --log-target=syslog --exit-idle-time=5
-### Cookie file
-; cookie-file =
+; cookie-file =
-### Disable shared memory data transfer
-; disable-shm = 0
+; disable-shm = no
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 805cd44e..f56cb241 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -35,6 +33,7 @@
#include <errno.h>
#include <signal.h>
#include <limits.h>
+#include <locale.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
@@ -50,11 +49,13 @@
#include <netdb.h>
#endif
-#include "../pulsecore/winsock.h"
-
-#include <pulsecore/core-error.h>
#include <pulse/version.h>
#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/native-common.h>
#include <pulsecore/pdispatch.h>
@@ -67,6 +68,7 @@
#include <pulsecore/socket-util.h>
#include <pulsecore/creds.h>
#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
#include "internal.h"
@@ -86,6 +88,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
[PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
+ [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
+ [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
+ [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+ [PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
@@ -93,10 +100,14 @@ static void unlock_autospawn_lock_file(pa_context *c) {
pa_assert(c);
if (c->autospawn_lock_fd >= 0) {
- char lf[PATH_MAX];
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+ char *lf;
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
+ pa_log_warn("Cannot unlock autospawn because runtime path is no more.");
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+ pa_xfree(lf);
+
c->autospawn_lock_fd = -1;
}
}
@@ -104,20 +115,42 @@ static void unlock_autospawn_lock_file(pa_context *c) {
static void context_free(pa_context *c);
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+ return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+static void reset_callbacks(pa_context *c) {
+ pa_assert(c);
+
+ c->state_callback = NULL;
+ c->state_userdata = NULL;
+
+ c->subscribe_callback = NULL;
+ c->subscribe_userdata = NULL;
+}
+
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
pa_assert(mainloop);
- pa_assert(name);
+
+ if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+ return NULL;
c = pa_xnew(pa_context, 1);
PA_REFCNT_INIT(c);
- c->name = pa_xstrdup(name);
+
+ c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
c->record_streams = pa_dynarray_new();
+ c->client_index = PA_INVALID_INDEX;
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
@@ -127,18 +160,15 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
c->ctag = 0;
c->csyncid = 0;
- c->state_callback = NULL;
- c->state_userdata = NULL;
-
- c->subscribe_callback = NULL;
- c->subscribe_userdata = NULL;
+ reset_callbacks(c);
- c->is_local = -1;
+ c->is_local = FALSE;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
- c->do_autospawn = 0;
+ c->do_autospawn = FALSE;
+ c->do_shm = FALSE;
#ifndef MSG_NOSIGNAL
#ifdef SIGPIPE
@@ -147,10 +177,10 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
#endif
c->conf = pa_client_conf_new();
- pa_client_conf_load(c->conf, NULL);
#ifdef HAVE_X11
pa_client_conf_from_x11(c->conf, NULL);
#endif
+ pa_client_conf_load(c->conf, NULL);
pa_client_conf_env(c->conf);
if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) {
@@ -167,26 +197,48 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return c;
}
-static void context_free(pa_context *c) {
+static void context_unlink(pa_context *c) {
+ pa_stream *s;
+
pa_assert(c);
- unlock_autospawn_lock_file(c);
+ s = c->streams ? pa_stream_ref(c->streams) : NULL;
+ while (s) {
+ pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+ pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+ pa_stream_unref(s);
+ s = n;
+ }
while (c->operations)
pa_operation_cancel(c->operations);
- while (c->streams)
- pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
-
- if (c->client)
- pa_socket_client_unref(c->client);
- if (c->pdispatch)
+ if (c->pdispatch) {
pa_pdispatch_unref(c->pdispatch);
+ c->pdispatch = NULL;
+ }
+
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
+ c->pstream = NULL;
}
+ if (c->client) {
+ pa_socket_client_unref(c->client);
+ c->client = NULL;
+ }
+
+ reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+ pa_assert(c);
+
+ context_unlink(c);
+
+ unlock_autospawn_lock_file(c);
+
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
@@ -200,7 +252,9 @@ static void context_free(pa_context *c) {
pa_strlist_free(c->server_list);
- pa_xfree(c->name);
+ if (c->proplist)
+ pa_proplist_free(c->proplist);
+
pa_xfree(c->server);
pa_xfree(c);
}
@@ -231,46 +285,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
pa_context_ref(c);
c->state = st;
+
if (c->state_callback)
c->state_callback(c, c->state_userdata);
- if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
- pa_stream *s;
-
- s = c->streams ? pa_stream_ref(c->streams) : NULL;
- while (s) {
- pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
- pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
- pa_stream_unref(s);
- s = n;
- }
-
- if (c->pdispatch)
- pa_pdispatch_unref(c->pdispatch);
- c->pdispatch = NULL;
-
- if (c->pstream) {
- pa_pstream_unlink(c->pstream);
- pa_pstream_unref(c->pstream);
- }
- c->pstream = NULL;
-
- if (c->client)
- pa_socket_client_unref(c->client);
- c->client = NULL;
- }
+ if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+ context_unlink(c);
pa_context_unref(c);
}
-void pa_context_fail(pa_context *c, int error) {
- 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) {
pa_assert(error >= 0);
pa_assert(error < PA_ERR_MAX);
@@ -281,6 +305,14 @@ int pa_context_set_error(pa_context *c, int error) {
return error;
}
+void pa_context_fail(pa_context *c, int error) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_set_error(c, error);
+ pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
@@ -337,25 +369,41 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_context_unref(c);
}
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
+ uint32_t err;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) {
pa_assert(t);
- if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ if (pa_tagstruct_getu32(t, &err) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
-
}
+
} else if (command == PA_COMMAND_TIMEOUT)
- c->error = PA_ERR_TIMEOUT;
+ err = PA_ERR_TIMEOUT;
else {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
+ if (err == PA_OK) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return -1;
+ }
+
+ if (err >= PA_ERR_MAX)
+ err = PA_ERR_UNKNOWN;
+
+ if (fail) {
+ pa_context_fail(c, err);
+ return -1;
+ }
+
+ pa_context_set_error(c, err);
+
return 0;
}
@@ -369,17 +417,14 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_context_ref(c);
if (command != PA_COMMAND_REPLY) {
-
- if (pa_context_handle_error(c, command, t) < 0)
- pa_context_fail(c, PA_ERR_PROTOCOL);
-
- pa_context_fail(c, c->error);
+ pa_context_handle_error(c, command, t, TRUE);
goto finish;
}
switch(c->state) {
case PA_CONTEXT_AUTHORIZING: {
pa_tagstruct *reply;
+ pa_bool_t shm_on_remote;
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -393,10 +438,22 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
goto finish;
}
+ /* Starting with protocol version 13 the MSB of the version
+ tag reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
/* Enable shared memory support if possible */
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->mempool) &&
- c->is_local) {
+ if (c->do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ c->do_shm = FALSE;
+
+ if (c->do_shm) {
/* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise
@@ -404,14 +461,22 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
#ifdef HAVE_CREDS
const pa_creds *creds;
- if ((creds = pa_pdispatch_creds(pd)))
- if (getuid() == creds->uid)
- pa_pstream_use_shm(c->pstream, 1);
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ c->do_shm = FALSE;
#endif
}
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
+ pa_pstream_enable_shm(c->pstream, c->do_shm);
+
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(reply, c->name);
+
+ if (c->version >= 13) {
+ pa_init_proplist(c->proplist);
+ pa_tagstruct_put_proplist(reply, c->proplist);
+ } else
+ pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+
pa_pstream_send_tagstruct(c->pstream, reply);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
@@ -420,11 +485,19 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
case PA_CONTEXT_SETTING_NAME :
+
+ if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
+ c->client_index == PA_INVALID_INDEX)) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
pa_context_set_state(c, PA_CONTEXT_READY);
break;
default:
- pa_assert(0);
+ pa_assert_not_reached();
}
finish:
@@ -451,10 +524,19 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid)
- pa_log_warn("No cookie loaded. Attempting to connect without.");
+ pa_log_info("No cookie loaded. Attempting to connect without.");
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
- pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
+
+ c->do_shm =
+ pa_mempool_is_shared(c->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
+
+ /* Starting with protocol version 13 we use the MSB of the version
+ * tag for informing the other side if we could do SHM or not */
+ pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0));
pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
#ifdef HAVE_CREDS
@@ -490,10 +572,13 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ;
pa_iochannel *io;
+ if (getuid() == 0)
+ return -1;
+
pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
- pa_log("socketpair(): %s", pa_cstrerror(errno));
+ pa_log_error("socketpair(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
@@ -507,7 +592,7 @@ static int context_connect_spawn(pa_context *c) {
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
- pa_log("fork(): %s", pa_cstrerror(errno));
+ pa_log_error("fork(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork)
@@ -522,9 +607,13 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
+ char *f;
- /* Not required, since fds[0] has CLOEXEC enabled anyway */
- pa_assert_se(pa_close(fds[0]) == 0);
+ pa_close_all(fds[1], -1);
+
+ f = pa_sprintf_malloc("%i", fds[1]);
+ pa_set_env("PULSE_PASSED_FD", f);
+ pa_xfree(f);
if (c->spawn_api.atfork)
c->spawn_api.atfork();
@@ -557,6 +646,9 @@ static int context_connect_spawn(pa_context *c) {
/* Parent */
+ pa_assert_se(pa_close(fds[1]) == 0);
+ fds[1] = -1;
+
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
@@ -571,14 +663,12 @@ static int context_connect_spawn(pa_context *c) {
goto fail;
}
- pa_assert_se(pa_close(fds[1]) == 0);
+ c->is_local = TRUE;
- c->is_local = 1;
+ unlock_autospawn_lock_file(c);
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
-
setup_context(c, io);
- unlock_autospawn_lock_file(c);
pa_context_unref(c);
@@ -630,7 +720,7 @@ static int try_next_connection(pa_context *c) {
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
- c->is_local = pa_socket_client_is_local(c->client);
+ c->is_local = !!pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
@@ -645,6 +735,7 @@ finish:
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
+ int saved_errno = errno;
pa_assert(client);
pa_assert(c);
@@ -657,7 +748,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) {
/* Try the item in the list */
- if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+ if (saved_errno == ECONNREFUSED ||
+ saved_errno == ETIMEDOUT ||
+ saved_errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
@@ -673,6 +766,29 @@ finish:
pa_context_unref(c);
}
+
+static char *get_legacy_runtime_dir(void) {
+ char *p, u[128];
+ struct stat st;
+
+ if (!pa_get_user_name(u, sizeof(u)))
+ return NULL;
+
+ p = pa_sprintf_malloc("/tmp/pulse-%s", u);
+
+ if (stat(p, &st) < 0) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ if (st.st_uid != getuid()) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ return p;
+}
+
int pa_context_connect(
pa_context *c,
const char *server,
@@ -701,8 +817,8 @@ int pa_context_connect(
goto finish;
}
} else {
- char *d;
- char ufn[PATH_MAX];
+ char *d, *ufn;
+ static char *legacy_dir;
/* Prepend in reverse order */
@@ -722,25 +838,40 @@ int pa_context_connect(
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */
- c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
+ c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+ /* The old per-user instance path. This is supported only to ease upgrades */
+ if ((legacy_dir = get_legacy_runtime_dir())) {
+ char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
+ c->server_list = pa_strlist_prepend(c->server_list, p);
+ pa_xfree(p);
+ pa_xfree(legacy_dir);
+ }
/* The per-user instance */
- c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+ if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+ c->server_list = pa_strlist_prepend(c->server_list, ufn);
+ pa_xfree(ufn);
+ }
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
- char lf[PATH_MAX];
+ char *lf;
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
+ pa_context_fail(c, PA_ERR_ACCESS);
+ goto finish;
+ }
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
- pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
+ pa_xfree(lf);
if (api)
c->spawn_api = *api;
- c->do_autospawn = 1;
- }
+ c->do_autospawn = TRUE;
+ }
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@@ -756,7 +887,8 @@ void pa_context_disconnect(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+ if (PA_CONTEXT_IS_GOOD(c->state))
+ pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
@@ -777,6 +909,9 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
c->state_callback = cb;
c->state_userdata = userdata;
}
@@ -785,11 +920,7 @@ int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY(c,
- c->state == PA_CONTEXT_CONNECTING ||
- c->state == PA_CONTEXT_AUTHORIZING ||
- c->state == PA_CONTEXT_SETTING_NAME ||
- c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@@ -866,7 +997,7 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -885,7 +1016,7 @@ finish:
pa_operation_unref(o);
}
-pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
@@ -895,32 +1026,20 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb,
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ o = pa_operation_new(c, NULL, cb, userdata);
- t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
+ t = pa_tagstruct_command(c, command, &tag);
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);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
-pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
- pa_tagstruct *t;
- pa_operation *o;
- uint32_t tag;
-
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
- o = pa_operation_new(c, NULL, cb, userdata);
-
- t = pa_tagstruct_command(c, command, &tag);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
@@ -934,7 +1053,6 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -954,7 +1072,6 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -965,14 +1082,15 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
int pa_context_is_local(pa_context *c) {
pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
- return c->is_local;
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
+
+ return !!c->is_local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
- pa_tagstruct *t;
pa_operation *o;
- uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -980,12 +1098,22 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
- t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(t, name);
- 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);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+ pa_tagstruct_puts(t, name);
+ 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;
}
@@ -1017,6 +1145,8 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
return c->version;
}
@@ -1032,3 +1162,71 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
return t;
}
+
+uint32_t pa_context_get_index(pa_context *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+ return c->client_index;
+}
+
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, 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, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ 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);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const *k;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ 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);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index 1de3abad..8dff7642 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -1,8 +1,6 @@
#ifndef foocontexthfoo
#define foocontexthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,6 +28,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
#include <pulse/operation.h>
+#include <pulse/proplist.h>
/** \page async Asynchronous API
*
@@ -166,9 +165,15 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
/** Instantiate a new connection context with an abstract mainloop API
- * and an application name */
+ * and an application name. It is recommended to use pa_context_new_with_proplist()
+ * instead and specify some initial properties.*/
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name, and specify the the initial client property
+ * list. \since 0.9.11 */
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+
/** Decrease the reference counter of the context by one */
void pa_context_unref(pa_context *c);
@@ -207,27 +212,42 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u
* returning a success notification */
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
-/** Set the name of the default sink. \since 0.4 */
+/** Set the name of the default sink. */
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Set the name of the default source. \since 0.4 */
+/** Set the name of the default source. */
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */
int pa_context_is_local(pa_context *c);
-/** Set a different application name for context on the server. \since 0.5 */
+/** Set a different application name for context on the server. */
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Return the server name this context is connected to. \since 0.7 */
+/** Return the server name this context is connected to. */
const char* pa_context_get_server(pa_context *c);
-/** Return the protocol version of the library. \since 0.8 */
+/** Return the protocol version of the library. */
uint32_t pa_context_get_protocol_version(pa_context *c);
-/** Return the protocol version of the connected server. \since 0.8 */
+/** Return the protocol version of the connected server. */
uint32_t pa_context_get_server_protocol_version(pa_context *c);
+/* Update the property list of the client, adding new entries. Please
+ * note that it is highly recommended to set as much properties
+ * initially via pa_context_new_with_proplist() as possible instead a
+ * posteriori with this function, since that information may then be
+ * used to route streams of the client to the right device. \since 0.9.11 */
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
+
+/* Update the property list of the client, remove entries. \since 0.9.11 */
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
+
+/** Return the client index this context is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
+uint32_t pa_context_get_index(pa_context *s);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index c2816234..a91c1031 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -1,8 +1,6 @@
#ifndef foodefhfoo
#define foodefhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -48,6 +46,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+ return
+ x == PA_CONTEXT_CONNECTING ||
+ x == PA_CONTEXT_AUTHORIZING ||
+ x == PA_CONTEXT_SETTING_NAME ||
+ x == PA_CONTEXT_READY;
+}
+
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@@ -57,6 +64,13 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+ return
+ x == PA_STREAM_CREATING ||
+ x == PA_STREAM_READY;
+}
+
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@@ -67,7 +81,7 @@ typedef enum pa_operation_state {
/** An invalid index */
#define PA_INVALID_INDEX ((uint32_t) -1)
-/** Some special flags for contexts. \since 0.8 */
+/** Some special flags for contexts. */
typedef enum pa_context_flags {
PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */
} pa_context_flags_t;
@@ -80,7 +94,7 @@ typedef enum pa_stream_direction {
PA_STREAM_UPLOAD /**< Sample upload stream */
} pa_stream_direction_t;
-/** Some special flags for stream connections. \since 0.6 */
+/** Some special flags for stream connections. */
typedef enum pa_stream_flags {
PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for
@@ -122,7 +136,7 @@ typedef enum pa_stream_flags {
* ahead can be corrected
* quickly, without the need to
* wait. */
- PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests
+ PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests
* are issued periodically
* automatically. Combined with
* PA_STREAM_INTERPOLATE_TIMING
@@ -132,15 +146,150 @@ typedef enum pa_stream_flags {
* pa_stream_get_latency() at
* all times without a packet
* round trip.*/
+ PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by
+ * their name, instead map them
+ * simply by their
+ * index. Implies
+ * PA_STREAM_NO_REMIX_CHANNELS. Only
+ * supported when the server is
+ * at least PA 0.9.8. It is
+ * ignored on older
+ * servers.\since 0.9.8 */
+ PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by
+ * name, don't upmix or downmix
+ * them to related
+ * channels. Copy them into
+ * matching channels of the
+ * device 1:1. Only supported
+ * when the server is at least
+ * PA 0.9.8. It is ignored on
+ * older servers. \since
+ * 0.9.8 */
+ PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the
+ * sink/device this stream is being
+ * connected to, and possibly ignore
+ * the format the sample spec contains
+ * -- but you still have to pass a
+ * valid value in it as a hint to
+ * PulseAudio what would suit your
+ * stream best. If this is used you
+ * should query the used sample format
+ * after creating the stream by using
+ * pa_stream_get_sample_spec(). Also,
+ * if you specified manual buffer
+ * metrics it is recommended to update
+ * them with
+ * pa_stream_set_buffer_attr() to
+ * compensate for the changed frame
+ * sizes. Only supported when the
+ * server is at least PA 0.9.8. It is
+ * ignored on older servers. \since
+ * 0.9.8 */
+
+ PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink,
+ * and possibly ignore the rate the
+ * sample spec contains. Usage similar
+ * to PA_STREAM_FIX_FORMAT.Only
+ * supported when the server is at least
+ * PA 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+
+ PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and
+ * the channel map of the sink, and
+ * possibly ignore the number of
+ * channels and the map the sample spec
+ * and the passed channel map
+ * contains. Usage similar to
+ * PA_STREAM_FIX_FORMAT. Only supported
+ * when the server is at least PA
+ * 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+ PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to
+ * another sink/device. Useful if you use
+ * any of the PA_STREAM_FIX_ flags and
+ * want to make sure that resampling
+ * never takes place -- which might
+ * happen if the stream is moved to
+ * another sink/source whith a different
+ * sample spec/channel map. Only
+ * supported when the server is at least
+ * PA 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+ PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the
+ * sampling rate during playback
+ * with
+ * pa_stream_update_sample_rate(). Only
+ * supported when the server is at
+ * least PA 0.9.8. It is ignored
+ * on older servers. \since
+ * 0.9.8 */
+ PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of
+ * resampling. \since 0.9.11 */
+
+ PA_STREAM_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */
+
+
+ PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of
+ * the sink/source based on the
+ * requested buffer metrics and
+ * adjust buffer metrics
+ * accordingly. \since 0.9.11 */
} pa_stream_flags_t;
+
+/** English is an evil language \since 0.9.11 */
+#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONOUS
+
/** Playback and record buffer metrics */
typedef struct pa_buffer_attr {
- uint32_t maxlength; /**< Maximum length of the buffer */
- uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
- uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
- uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
- uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
+ uint32_t maxlength; /**< Maximum length of the
+ * buffer. Setting this to 0 will
+ * initialize this to the maximum value
+ * supported by server, which is
+ * recommended. */
+ uint32_t tlength; /**< Playback only: target length of the
+ * buffer. The server tries to assure
+ * that at least tlength bytes are always
+ * available in the buffer. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the
+ * server. However, this value will
+ * default to something like 2s, i.e. for
+ * applications that have specific
+ * latency requirements this value should
+ * be set to the maximum latency that the
+ * application can deal with. */
+ uint32_t prebuf; /**< Playback only: pre-buffering. The
+ * server does not start with playback
+ * before at least prebug bytes are
+ * available in the buffer. It is
+ * recommended to set this to 0, which
+ * will initialize this to the same value
+ * as tlength, whatever that may be. */
+ uint32_t minreq; /**< Playback only: minimum request. The
+ * server does not request less than
+ * minreq bytes from the client, instead
+ * waits until the buffer is free enough
+ * to request more bytes at once. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the server. */
+ uint32_t fragsize; /**< Recording only: fragment size. The
+ * server sends data in blocks of
+ * fragsize bytes size. Large values
+ * deminish interactivity with other
+ * operations on the connection context
+ * but decrease control overhead. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the
+ * server. However, this value will
+ * default to something like 2s, i.e. for
+ * applications that have specific
+ * latency requirements this value should
+ * be set to the maximum latency that the
+ * application can deal with. */
} pa_buffer_attr;
/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
@@ -162,9 +311,10 @@ enum {
PA_ERR_MODINITFAILED, /**< Module initialization failed */
PA_ERR_BADSTATE, /**< Bad state */
PA_ERR_NODATA, /**< No data */
- PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */
- PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */
+ PA_ERR_VERSION, /**< Incompatible protocol version */
+ PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
+ PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@@ -178,9 +328,9 @@ typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
- PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
- PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */
- PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */
+ PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. */
+ PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. */
+ PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events */
} pa_subscription_mask_t;
/** Subscription event types, as used by pa_context_subscribe() */
@@ -192,8 +342,8 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
- PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
- PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
+ PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
+ PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
@@ -220,7 +370,9 @@ typedef enum pa_subscription_event_type {
* source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
* sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink. The two latency estimations
- * described here are implemented in pa_stream_get_latency().*/
+ * described here are implemented in pa_stream_get_latency(). Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release.*/
typedef struct pa_timing_info {
struct timeval timestamp; /**< The time when this timing info structure was current */
int synchronized_clocks; /**< Non-zero if the local and the
@@ -229,14 +381,21 @@ typedef struct pa_timing_info {
* detected transport_usec becomes much
* more reliable. However, the code that
* detects synchronized clocks is very
- * limited und unreliable itself. \since
- * 0.5 */
+ * limited und unreliable itself. */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
- pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
- pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
-
- int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+ pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
+ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
+
+ int playing; /**< Non-zero when the stream is
+ * currently not underrun and data is
+ * being passed on to the device. Only
+ * for playback streams. This field does
+ * not say whether the data is actually
+ * already being played. To determine
+ * this check whether since_underrun
+ * (converted to usec) is larger than
+ * sink_usec.*/
int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
@@ -245,20 +404,19 @@ typedef struct pa_timing_info {
* info was current . Only write
* commands with SEEK_RELATIVE_ON_READ
* and SEEK_RELATIVE_END can corrupt
- * write_index. \since 0.8 */
+ * write_index. */
int64_t write_index; /**< Current write index into the
* playback buffer in bytes. Think twice before
* using this for seeking purposes: it
* might be out of date a the time you
* want to use it. Consider using
- * PA_SEEK_RELATIVE instead. \since
- * 0.8 */
+ * PA_SEEK_RELATIVE instead. */
int read_index_corrupt; /**< Non-zero if read_index is not
* up-to-date because a local pause or
* flush request that corrupted it has
* been issued in the time since this
- * latency info was current. \since 0.8 */
+ * latency info was current. */
int64_t read_index; /**< Current read index into the
* playback buffer in bytes. Think twice before
@@ -266,7 +424,20 @@ typedef struct pa_timing_info {
* might be out of date a the time you
* want to use it. Consider using
* PA_SEEK_RELATIVE_ON_READ
- * instead. \since 0.8 */
+ * instead. */
+
+ pa_usec_t configured_sink_usec; /**< The static configured latency for
+ * the sink. \since 0.9.11 */
+ pa_usec_t configured_source_usec; /**< The static configured latency for
+ * the source. \since 0.9.11 */
+
+ int64_t since_underrun; /**< Bytes that were handed to the sink
+ since the last underrun happened, or
+ since playback started again after
+ the last underrun. playing will tell
+ you which case it is. \since
+ 0.9.11 */
+
} pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto
@@ -275,7 +446,7 @@ typedef struct pa_timing_info {
* waitpid() is used on the child's PID. The spawn routine will not
* block or ignore SIGCHLD signals, since this cannot be done in a
* thread compatible way. You might have to do this in
- * prefork/postfork. \since 0.4 */
+ * prefork/postfork. */
typedef struct pa_spawn_api {
void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
@@ -288,7 +459,7 @@ typedef struct pa_spawn_api {
* passed to the new process. */
} pa_spawn_api;
-/** Seek type for pa_stream_write(). \since 0.8*/
+/** Seek type for pa_stream_write(). */
typedef enum pa_seek_mode {
PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */
PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
@@ -296,18 +467,24 @@ typedef enum pa_seek_mode {
PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */
} pa_seek_mode_t;
-/** Special sink flags. \since 0.8 */
+/** Special sink flags. */
typedef enum pa_sink_flags {
PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SINK_LATENCY = 2, /**< Supports latency querying */
- PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
+ PA_SINK_HARDWARE = 4, /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
+ PA_SINK_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SINK_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
+ PA_SINK_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_sink_flags_t;
-/** Special source flags. \since 0.8 */
+/** Special source flags. */
typedef enum pa_source_flags {
PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SOURCE_LATENCY = 2, /**< Supports latency querying */
- PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
+ PA_SOURCE_HARDWARE = 4, /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
+ PA_SOURCE_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SOURCE_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
+ PA_SOURCE_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_source_flags_t;
/** A generic free() like callback prototype */
diff --git a/src/pulse/error.c b/src/pulse/error.c
index 78f0da95..29690c89 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/error.h b/src/pulse/error.h
index 44a2e5ec..9f9e3d33 100644
--- a/src/pulse/error.h
+++ b/src/pulse/error.h
@@ -1,8 +1,6 @@
#ifndef fooerrorhfoo
#define fooerrorhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/gccmacro.h b/src/pulse/gccmacro.h
index e9f0d093..e4062033 100644
--- a/src/pulsecore/gccmacro.h
+++ b/src/pulse/gccmacro.h
@@ -1,8 +1,6 @@
#ifndef foopulsegccmacrohfoo
#define foopulsegccmacrohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -60,7 +58,7 @@
#endif
#ifndef PA_GCC_PURE
-#ifdef __GNUCC__
+#ifdef __GNUC__
#define PA_GCC_PURE __attribute__ ((pure))
#else
/** This function's return value depends only the arguments list and global state **/
@@ -69,7 +67,7 @@
#endif
#ifndef PA_GCC_CONST
-#ifdef __GNUCC__
+#ifdef __GNUC__
#define PA_GCC_CONST __attribute__ ((const))
#else
/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
@@ -77,4 +75,22 @@
#endif
#endif
+#ifndef PA_GCC_DEPRECATED
+#ifdef __GNUC__
+#define PA_GCC_DEPRECATED __attribute__ ((deprecated))
+#else
+/** This function is deprecated **/
+#define PA_GCC_DEPRECATED
+#endif
+#endif
+
+#ifndef PA_GCC_PACKED
+#ifdef __GNUCC__
+#define PA_GCC_PACKED __attribute__ ((packed))
+#else
+/** Structure shall be packed in memory **/
+#define PA_GCC_PACKED
+#endif
+#endif
+
#endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
index b7a7537a..6ddb0faa 100644
--- a/src/pulse/glib-mainloop.c
+++ b/src/pulse/glib-mainloop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index a4e06ea0..60fd61a3 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -1,8 +1,6 @@
#ifndef fooglibmainloophfoo
#define fooglibmainloophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 95593adb..9ed541d1 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -1,8 +1,6 @@
#ifndef foointernalhfoo
#define foointernalhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,6 +40,7 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
#include "client-conf.h"
@@ -50,7 +49,7 @@
struct pa_context {
PA_REFCNT_DECLARE;
- char *name;
+ pa_proplist *proplist;
pa_mainloop_api* mainloop;
pa_socket_client *client;
@@ -69,14 +68,14 @@ struct pa_context {
pa_context_notify_cb_t state_callback;
void *state_userdata;
-
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
pa_mempool *mempool;
- int is_local;
- int do_autospawn;
+ pa_bool_t is_local:1;
+ pa_bool_t do_autospawn:1;
+ pa_bool_t do_shm:1;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
@@ -85,46 +84,60 @@ struct pa_context {
char *server;
pa_client_conf *conf;
+
+ uint32_t client_index;
};
-#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction {
uint32_t tag;
- int valid;
int64_t value;
- int absolute, corrupt;
+ pa_bool_t valid:1;
+ pa_bool_t absolute:1;
+ pa_bool_t corrupt:1;
} pa_index_correction;
struct pa_stream {
PA_REFCNT_DECLARE;
+ PA_LLIST_FIELDS(pa_stream);
+
pa_context *context;
pa_mainloop_api *mainloop;
- PA_LLIST_FIELDS(pa_stream);
- char *name;
- pa_buffer_attr buffer_attr;
+ uint32_t direct_on_input;
+
+ pa_stream_direction_t direction;
+ pa_stream_state_t state;
+ pa_stream_flags_t flags;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_stream_flags_t flags;
+
+ pa_proplist *proplist;
+
+ pa_bool_t channel_valid:1;
+ pa_bool_t suspended:1;
+ pa_bool_t corked:1;
+ pa_bool_t timing_info_valid:1;
+ pa_bool_t auto_timing_update_requested:1;
+
uint32_t channel;
uint32_t syncid;
- int channel_valid;
- uint32_t device_index;
- pa_stream_direction_t direction;
- pa_stream_state_t state;
+ uint32_t stream_index;
uint32_t requested_bytes;
+ pa_buffer_attr buffer_attr;
+
+ uint32_t device_index;
+ char *device_name;
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;
- int corked;
-
/* Store latest latency info */
pa_timing_info timing_info;
- int timing_info_valid;
/* Use to make sure that time advances monotonically */
pa_usec_t previous_time;
@@ -139,10 +152,8 @@ struct pa_stream {
/* Latency interpolation stuff */
pa_time_event *auto_timing_update_event;
- int auto_timing_update_requested;
- pa_usec_t cached_time;
- int cached_time_valid;
+ pa_smoother *smoother;
/* Callbacks */
pa_stream_notify_cb_t state_callback;
@@ -157,6 +168,12 @@ struct pa_stream {
void *underflow_userdata;
pa_stream_notify_cb_t latency_update_callback;
void *latency_update_userdata;
+ pa_stream_notify_cb_t moved_callback;
+ void *moved_userdata;
+ pa_stream_notify_cb_t suspended_callback;
+ void *suspended_userdata;
+ pa_stream_notify_cb_t started_callback;
+ void *started_userdata;
};
typedef void (*pa_operation_cb_t)(void);
@@ -172,13 +189,17 @@ struct pa_operation {
pa_operation_state_t state;
void *userdata;
pa_operation_cb_t callback;
+
+ void *private; /* some operations might need this */
};
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
@@ -190,7 +211,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
@@ -212,5 +233,4 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
-
#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index d2b48f04..4be2c62a 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,8 +25,8 @@
#endif
#include <pulse/context.h>
+#include <pulse/gccmacro.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
@@ -47,12 +45,12 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
pa_assert(PA_REFCNT_VALUE(o) >= 1);
memset(&i, 0, sizeof(i));
-
+
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -90,12 +88,12 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
pa_assert(PA_REFCNT_VALUE(o) >= 1);
memset(&i, 0, sizeof(i));
-
+
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -106,7 +104,8 @@ 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_getu32(t, &i.cookie) < 0 ||
+ !pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
@@ -140,7 +139,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -149,7 +148,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
while (!pa_tagstruct_eof(t)) {
pa_sink_info i;
+ pa_bool_t mute = FALSE;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -158,23 +160,30 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
- pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 &&
+ (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+ pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags;
if (o->callback) {
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -251,7 +260,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -260,7 +269,10 @@ 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;
+ pa_bool_t mute = FALSE;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -269,23 +281,30 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
- pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 &&
+ (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+ pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags;
if (o->callback) {
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -362,7 +381,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -370,13 +389,18 @@ 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));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0 ) {
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -384,6 +408,8 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -437,7 +463,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -445,17 +471,20 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_module_info i;
+ pa_bool_t auto_unload = FALSE;
memset(&i, 0, sizeof(i));
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.argument) < 0 ||
pa_tagstruct_getu32(t, &i.n_used) < 0 ||
- pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
+ pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
+ i.auto_unload = (int) auto_unload;
+
if (o->callback) {
pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
@@ -513,7 +542,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -521,8 +550,11 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
while (!pa_tagstruct_eof(t)) {
pa_sink_input_info i;
+ pa_bool_t mute = FALSE;
+
memset(&i, 0, sizeof(i));
-
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
@@ -535,16 +567,22 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0)) {
+ (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
+
if (o->callback) {
pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -598,7 +636,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -608,7 +646,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info i;
memset(&i, 0, sizeof(i));
-
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
@@ -619,9 +658,11 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.source_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 >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -629,6 +670,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -923,7 +966,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -931,8 +974,10 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_sample_info i;
+ pa_bool_t lazy = FALSE;
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -941,17 +986,22 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.bytes) < 0 ||
- pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
- pa_tagstruct_gets(t, &i.filename) < 0) {
+ pa_tagstruct_get_boolean(t, &lazy) < 0 ||
+ pa_tagstruct_gets(t, &i.filename) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
+ i.lazy = (int) lazy;
+
if (o->callback) {
pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -1060,7 +1110,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
@@ -1121,7 +1171,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -1158,6 +1208,8 @@ finish:
pa_operation_unref(o);
}
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@@ -1182,6 +1234,8 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na
return o;
}
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@@ -1204,10 +1258,15 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx,
return o;
}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
}
+PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
@@ -1234,6 +1293,8 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo
return o;
}
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
@@ -1257,6 +1318,8 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name
return o;
}
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
@@ -1278,7 +1341,7 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p
return o;
}
-pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata) {
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
@@ -1328,7 +1391,7 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u
return o;
}
-pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata) {
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
@@ -1378,7 +1441,7 @@ 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* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
@@ -1425,7 +1488,7 @@ pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int
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* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index e700e39b..c8c13a75 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -1,8 +1,6 @@
#ifndef foointrospecthfoo
#define foointrospecthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,8 +28,10 @@
#include <pulse/operation.h>
#include <pulse/context.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
/** \page introspect Server Query and Control
*
@@ -206,21 +206,32 @@
PA_C_DECL_BEGIN
-/** Stores information about sinks */
+#define PA_PORT_DIGITAL "spdif"
+#define PA_PORT_ANALOG_STEREO "analog-stereo"
+#define PA_PORT_ANALOG_5_1 "analog-5-1"
+#define PA_PORT_ANALOG_4_0 "analog-4-0"
+
+/** @{ \name Sinks */
+
+/** Stores information about sinks. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sink_info {
const char *name; /**< Name of the sink */
uint32_t index; /**< Index of the sink */
const char *description; /**< Description of this sink */
pa_sample_spec sample_spec; /**< Sample spec of this sink */
- pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the sink */
- int mute; /**< Mute switch of the sink \since 0.8 */
+ int mute; /**< Mute switch of the sink */
uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
const char *monitor_source_name; /**< The name of the monitor source */
- pa_usec_t latency; /**< Length of filled playback buffer of this sink */
- const char *driver; /**< Driver name. \since 0.8 */
- pa_sink_flags_t flags; /**< Flags \since 0.8 */
+ pa_usec_t latency; /**< Length of queued audio in the output buffer. */
+ const char *driver; /**< Driver name. */
+ pa_sink_flags_t flags; /**< Flags */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
+ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -235,21 +246,47 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_s
/** Get the complete sink list */
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
-/** Stores information about sources */
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its index */
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its name */
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, 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, const 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);
+
+/** @} */
+
+/** @{ \name Sources */
+
+/** Stores information about sources. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_source_info {
- const char *name ; /**< Name of the source */
+ const char *name; /**< Name of the source */
uint32_t index; /**< Index of the source */
const char *description; /**< Description of this source */
pa_sample_spec sample_spec; /**< Sample spec of this source */
- pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
- pa_cvolume volume; /**< Volume of the source \since 0.8 */
- int mute; /**< Mute switch of the sink \since 0.8 */
+ pa_cvolume volume; /**< Volume of the source */
+ int mute; /**< Mute switch of the sink */
uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
- pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
- const char *driver; /**< Driver name \since 0.8 */
- pa_source_flags_t flags; /**< Flags \since 0.8 */
+ pa_usec_t latency; /**< Length of filled record buffer of this source. */
+ const char *driver; /**< Driver name */
+ pa_source_flags_t flags; /**< Flags */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
+ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@@ -264,16 +301,34 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa
/** Get the complete source list */
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
-/** Server information */
+/** Set the volume of a source device specified by its index */
+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);
+
+/** Set the volume of a source device specified by its name */
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its index */
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its name */
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Server */
+
+/** Server information. Please note that this structure can be
+ * extended as part of evolutionary API updates at any time in any new
+ * release. */
typedef struct pa_server_info {
const char *user_name; /**< User name of the daemon process */
const char *host_name; /**< Host name the daemon is running on */
const char *server_version; /**< Version string of the daemon */
const char *server_name; /**< Server package name (usually "pulseaudio") */
pa_sample_spec sample_spec; /**< Default sample specification */
- const char *default_sink_name; /**< Name of default sink. \since 0.4 */
- const char *default_source_name; /**< Name of default sink. \since 0.4*/
- uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. \since 0.8 */
+ const char *default_sink_name; /**< Name of default sink. */
+ const char *default_source_name; /**< Name of default sink. */
+ uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. */
} pa_server_info;
/** Callback prototype for pa_context_get_server_info() */
@@ -282,7 +337,13 @@ typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void
/** Get some information about the server */
pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
-/** Stores information about modules */
+/** @} */
+
+/** @{ \name Modules */
+
+/** Stores information about modules. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_module_info {
uint32_t index; /**< Index of the module */
const char*name, /**< Name of the module */
@@ -300,12 +361,28 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_
/** Get the complete list of currently loaded modules */
pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
-/** Stores information about clients */
+/** Callback prototype for pa_context_load_module() */
+typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Load a module. */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+
+/** Unload a module. */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Clients */
+
+/** Stores information about clients. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_client_info {
uint32_t index; /**< Index of this client */
const char *name; /**< Name of this client */
uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
- const char *driver; /**< Driver name \since 0.8 */
+ const char *driver; /**< Driver name */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_client_info;
/** Callback prototype for pa_context_get_client_info() and firends*/
@@ -317,7 +394,16 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_
/** Get the complete client list */
pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
-/** Stores information about sink inputs */
+/** Kill a client. */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Sink Inputs */
+
+/** Stores information about sink inputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sink_input_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
@@ -329,9 +415,10 @@ typedef struct pa_sink_input_info {
pa_cvolume volume; /**< The volume of this sink input */
pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
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 */
+ const char *resample_method; /**< Thre resampling method used by this sink input. */
+ const char *driver; /**< Driver name */
int mute; /**< Stream muted \since 0.9.7 */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_sink_input_info;
/** Callback prototype for pa_context_get_sink_input_info() and firends*/
@@ -343,7 +430,28 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin
/** Get the complete sink input list */
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
-/** Stores information about source outputs */
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** 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);
+
+/** Kill a sink input. */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Source Outputs */
+
+/** Stores information about source outputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_source_output_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
@@ -352,10 +460,11 @@ typedef struct pa_source_output_info {
uint32_t source; /**< Index of the connected source */
pa_sample_spec sample_spec; /**< The sample specification of the source output */
pa_channel_map channel_map; /**< Channel map */
- pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
- pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
- const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
- const char *driver; /**< Driver name \since 0.8 */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. */
+ pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. */
+ const char *resample_method; /**< Thre resampling method used by this source output. */
+ const char *driver; /**< Driver name */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_source_output_info;
/** Callback prototype for pa_context_get_source_output_info() and firends*/
@@ -367,43 +476,34 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_
/** Get the complete list of source outputs */
pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
-/** Set the volume of a sink device specified by its index */
-pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
-
-/** Set the volume of a sink device specified by its name */
-pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
-
-/** Set the mute switch of a sink device specified by its index \since 0.8 */
-pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
-
-/** Set the mute switch of a sink device specified by its name \since 0.8 */
-pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata);
-/** 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);
+/** 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);
-/** 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);
+/** Suspend/Resume a source. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, 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);
+/** 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);
-/** Set the volume of a source device specified by its name \since 0.8 */
-pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+/** Kill a source output. */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-/** Set the mute switch of a source device specified by its index \since 0.8 */
-pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+/** @} */
-/** Set the mute switch of a source device specified by its name \since 0.8 */
-pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+/** @{ \name Statistics */
-/** Memory block statistics */
+/** Memory block statistics. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_stat_info {
uint32_t memblock_total; /**< Currently allocated memory blocks */
uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
- uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
+ uint32_t scache_size; /**< Total size of all sample cache entries. */
} pa_stat_info;
/** Callback prototype for pa_context_stat() */
@@ -412,7 +512,13 @@ typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *u
/** Get daemon memory block statistics */
pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
-/** Stores information about sample cache entries */
+/** @} */
+
+/** @{ \name Cached Samples */
+
+/** Stores information about sample cache entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sample_info {
uint32_t index; /**< Index of this entry */
const char *name; /**< Name of this entry */
@@ -420,9 +526,10 @@ typedef struct pa_sample_info {
pa_sample_spec sample_spec; /**< Sample specification of the sample */
pa_channel_map channel_map; /**< The channel map */
pa_usec_t duration; /**< Duration of this entry */
- uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
- int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
- const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
+ uint32_t bytes; /**< Length of this sample in bytes. */
+ int lazy; /**< Non-zero when this is a lazy cache entry. */
+ const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */
+ pa_proplist *proplist; /**< Property list for this sample. \since 0.9.11 */
} pa_sample_info;
/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
@@ -437,31 +544,21 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p
/** Get the complete list of samples stored in the daemon. */
pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
-/** Kill a client. \since 0.5 */
-pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Kill a sink input. \since 0.5 */
-pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Kill a source output. \since 0.5 */
-pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */
-typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+/** @} */
-/** Load a module. \since 0.5 */
-pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+/** \cond fulldocs */
-/** Unload a module. \since 0.5 */
-pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+/** @{ \name Autoload Entries */
-/** Type of an autoload entry. \since 0.5 */
+/** Type of an autoload entry. */
typedef enum pa_autoload_type {
PA_AUTOLOAD_SINK = 0,
PA_AUTOLOAD_SOURCE = 1
} pa_autoload_type_t;
-/** Stores information about autoload entries. \since 0.5 */
+/** Stores information about autoload entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_autoload_info {
uint32_t index; /**< Index of this autoload entry */
const char *name; /**< Name of the sink or source */
@@ -473,47 +570,27 @@ typedef struct pa_autoload_info {
/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
-/** Get info about a specific autoload entry. \since 0.6 */
-pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata);
-
-/** Get info about a specific autoload entry. \since 0.6 */
-pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata);
-
-/** Get the complete list of autoload entries. \since 0.5 */
-pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata);
-
-/** Add a new autoload entry. \since 0.5 */
-pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata);
-
-/** Remove an autoload entry. \since 0.6 */
-pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata);
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Remove an autoload entry. \since 0.6 */
-pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata);
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Move the specified sink input to a different sink. \since 0.9.5 */
-pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata);
-
-/** Move the specified sink input to a different sink. \since 0.9.5 */
-pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+/** Get the complete list of autoload entries. */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Move the specified source output to a different source. \since 0.9.5 */
-pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata);
-
-/** 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);
+/** Add a new autoload entry. */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
-/** 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);
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
-/** 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);
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
-/** 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);
+/** \endcond */
PA_C_DECL_END
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
index 989a5cc1..90aff164 100644
--- a/src/pulse/mainloop-api.c
+++ b/src/pulse/mainloop-api.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,8 +26,8 @@
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "mainloop-api.h"
@@ -41,10 +39,10 @@ struct once_info {
static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
struct once_info *i = userdata;
-
+
pa_assert(m);
pa_assert(i);
-
+
pa_assert(i->callback);
i->callback(m, i->userdata);
@@ -75,4 +73,3 @@ void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *
pa_assert_se(e = m->defer_new(m, once_callback, i));
m->defer_set_destroy(e, free_callback);
}
-
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
index 985806e6..53c7411e 100644
--- a/src/pulse/mainloop-api.h
+++ b/src/pulse/mainloop-api.h
@@ -1,8 +1,6 @@
#ifndef foomainloopapihfoo
#define foomainloopapihfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
index a986b241..9161dec4 100644
--- a/src/pulse/mainloop-signal.c
+++ b/src/pulse/mainloop-signal.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -39,11 +37,11 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.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"
@@ -55,9 +53,9 @@ struct pa_signal_event {
#else
void (*saved_handler)(int sig);
#endif
- void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
void *userdata;
- void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
+ pa_signal_cb_t callback;
+ pa_signal_destroy_cb_t destroy_callback;
pa_signal_event *previous, *next;
};
@@ -67,10 +65,17 @@ static pa_io_event* io_event = NULL;
static pa_signal_event *signals = NULL;
static void signal_handler(int sig) {
+ int saved_errno;
+
+ saved_errno = errno;
+
#ifndef HAVE_SIGACTION
signal(sig, signal_handler);
#endif
+
pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
+
+ errno = saved_errno;
}
static void dispatch(pa_mainloop_api*a, int sig) {
@@ -87,7 +92,7 @@ static void dispatch(pa_mainloop_api*a, int sig) {
static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
ssize_t r;
int sig;
-
+
pa_assert(a);
pa_assert(e);
pa_assert(f == PA_IO_EVENT_INPUT);
@@ -136,23 +141,21 @@ int pa_signal_init(pa_mainloop_api *a) {
}
void pa_signal_done(void) {
- 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);
- api->io_free(io_event);
- io_event = NULL;
+ if (io_event) {
+ pa_assert(api);
+ api->io_free(io_event);
+ io_event = NULL;
+ }
pa_close_pipe(signal_pipe);
api = NULL;
}
-pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
pa_signal_event *e = NULL;
#ifdef HAVE_SIGACTION
@@ -217,8 +220,8 @@ void pa_signal_free(pa_signal_event *e) {
pa_xfree(e);
}
-void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
pa_assert(e);
-
+
e->destroy_callback = _callback;
}
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
index 50aa99ce..a6c16f2f 100644
--- a/src/pulse/mainloop-signal.h
+++ b/src/pulse/mainloop-signal.h
@@ -1,12 +1,10 @@
#ifndef foomainloopsignalhfoo
#define foomainloopsignalhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -39,23 +37,27 @@ PA_C_DECL_BEGIN
* signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
*/
+/** An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
+
+typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
+
/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
int pa_signal_init(pa_mainloop_api *api);
/** Cleanup the signal subsystem */
void pa_signal_done(void);
-/** An opaque UNIX signal event source object */
-typedef struct pa_signal_event pa_signal_event;
-
/** Create a new UNIX signal event source object */
-pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata);
/** Free a UNIX signal event source object */
void pa_signal_free(pa_signal_event *e);
/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
-void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback);
PA_C_DECL_END
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index ad4e4e97..aaed3caf 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index db2797fb..907e94a7 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -1,8 +1,6 @@
#ifndef foomainloophfoo
#define foomainloophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
index ed5eb4aa..13b470a8 100644
--- a/src/pulse/operation.c
+++ b/src/pulse/operation.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,18 +25,24 @@
#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "internal.h"
#include "operation.h"
+PA_STATIC_FLIST_DECLARE(operations, 0, pa_xfree);
+
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {
pa_operation *o;
pa_assert(c);
- o = pa_xnew(pa_operation, 1);
+ if (!(o = pa_flist_pop(PA_STATIC_FLIST_GET(operations))))
+ o = pa_xnew(pa_operation, 1);
+
PA_REFCNT_INIT(o);
o->context = c;
o->stream = s;
+ o->private = NULL;
o->state = PA_OPERATION_RUNNING;
o->callback = cb;
@@ -58,7 +62,6 @@ pa_operation *pa_operation_ref(pa_operation *o) {
PA_REFCNT_INC(o);
return o;
}
-
void pa_operation_unref(pa_operation *o) {
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
@@ -66,10 +69,29 @@ void pa_operation_unref(pa_operation *o) {
if (PA_REFCNT_DEC(o) <= 0) {
pa_assert(!o->context);
pa_assert(!o->stream);
- pa_xfree(o);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(operations), o) < 0)
+ pa_xfree(o);
}
}
+static void operation_unlink(pa_operation *o) {
+ pa_assert(o);
+
+ if (o->context) {
+ pa_assert(PA_REFCNT_VALUE(o) >= 2);
+
+ PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+ pa_operation_unref(o);
+
+ o->context = NULL;
+ }
+
+ o->stream = NULL;
+ o->callback = NULL;
+ o->userdata = NULL;
+}
+
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
@@ -81,20 +103,8 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
o->state = st;
- if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
-
- if (o->context) {
- pa_assert(PA_REFCNT_VALUE(o) >= 2);
-
- PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
- pa_operation_unref(o);
- }
-
- o->context = NULL;
- o->stream = NULL;
- o->callback = NULL;
- o->userdata = NULL;
- }
+ if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
+ operation_unlink(o);
pa_operation_unref(o);
}
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 97d1c6b8..188e2cb9 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -1,8 +1,6 @@
#ifndef foooperationhfoo
#define foooperationhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
new file mode 100644
index 00000000..74aea20a
--- /dev/null
+++ b/src/pulse/proplist.c
@@ -0,0 +1,321 @@
+/***
+ 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 <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "proplist.h"
+
+struct property {
+ char *key;
+ void *value;
+ size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_PROPLIST(p) ((pa_proplist*) (p))
+
+static pa_bool_t property_name_valid(const char *key) {
+
+ if (!pa_utf8_valid(key))
+ return FALSE;
+
+ if (strlen(key) <= 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void property_free(struct property *prop) {
+ pa_assert(prop);
+
+ pa_xfree(prop->key);
+ pa_xfree(prop->value);
+ pa_xfree(prop);
+}
+
+pa_proplist* pa_proplist_new(void) {
+ return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
+}
+
+void pa_proplist_free(pa_proplist* p) {
+ pa_assert(p);
+
+ pa_proplist_clear(p);
+ pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key) || !pa_utf8_valid(value))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = pa_xstrdup(value);
+ prop->nbytes = strlen(value)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+ va_list ap;
+ int r;
+ char *t;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key) || !pa_utf8_valid(format))
+ return -1;
+
+ va_start(ap, format);
+ t = pa_vsprintf_malloc(format, ap);
+ va_end(ap);
+
+ r = pa_proplist_sets(p, key, t);
+
+ pa_xfree(t);
+ return r;
+}
+
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = pa_xmemdup(data, nbytes);
+ prop->nbytes = nbytes;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+const char *pa_proplist_gets(pa_proplist *p, const char *key) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return NULL;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return NULL;
+
+ if (prop->nbytes <= 0)
+ return NULL;
+
+ if (((char*) prop->value)[prop->nbytes-1] != 0)
+ return NULL;
+
+ if (strlen((char*) prop->value) != prop->nbytes-1)
+ return NULL;
+
+ if (!pa_utf8_valid((char*) prop->value))
+ return NULL;
+
+ return (char*) prop->value;
+}
+
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return -1;
+
+ *data = prop->value;
+ *nbytes = prop->nbytes;
+
+ return 0;
+}
+
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
+ struct property *prop;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
+ pa_assert(other);
+
+ if (mode == PA_UPDATE_SET)
+ pa_proplist_clear(p);
+
+ while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
+
+ if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
+ continue;
+
+ pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
+ }
+}
+
+int pa_proplist_unset(pa_proplist *p, const char *key) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
+ return -2;
+
+ property_free(prop);
+ return 0;
+}
+
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
+ const char * const * k;
+ int n = 0;
+
+ pa_assert(p);
+ pa_assert(keys);
+
+ for (k = keys; *k; k++)
+ if (!property_name_valid(*k))
+ return -1;
+
+ for (k = keys; *k; k++)
+ if (pa_proplist_unset(p, *k) >= 0)
+ n++;
+
+ return n;
+}
+
+const char *pa_proplist_iterate(pa_proplist *p, void **state) {
+ struct property *prop;
+
+ if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+ return NULL;
+
+ return prop->key;
+}
+
+char *pa_proplist_to_string(pa_proplist *p) {
+ const char *key;
+ void *state = NULL;
+ pa_strbuf *buf;
+
+ pa_assert(p);
+
+ buf = pa_strbuf_new();
+
+ while ((key = pa_proplist_iterate(p, &state))) {
+
+ const char *v;
+
+ if ((v = pa_proplist_gets(p, key)))
+ pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
+ else {
+ const void *value;
+ size_t nbytes;
+ char *c;
+
+ pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
+ c = pa_xnew(char, nbytes*2+1);
+ pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
+
+ pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
+ pa_xfree(c);
+ }
+ }
+
+ return pa_strbuf_tostring_free(buf);
+}
+
+int pa_proplist_contains(pa_proplist *p, const char *key) {
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return 0;
+
+ return 1;
+}
+
+void pa_proplist_clear(pa_proplist *p) {
+ struct property *prop;
+ pa_assert(p);
+
+ while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+ property_free(prop);
+}
+
+pa_proplist* pa_proplist_copy(pa_proplist *template) {
+ pa_proplist *p;
+
+ pa_assert_se(p = pa_proplist_new());
+
+ if (template)
+ pa_proplist_update(p, PA_UPDATE_REPLACE, template);
+
+ return p;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
new file mode 100644
index 00000000..f75cca54
--- /dev/null
+++ b/src/pulse/proplist.h
@@ -0,0 +1,217 @@
+#ifndef foopulseproplisthfoo
+#define foopulseproplisthfoo
+
+/***
+ 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 <sys/types.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+PA_C_DECL_BEGIN
+
+/* Defined properties:
+ *
+ * media.name "Guns'N'Roses: Civil War"
+ * media.title "Civil War"
+ * media.artist "Guns'N'Roses"
+ * media.language "de_DE"
+ * media.filename
+ * media.icon
+ * media.icon_name
+ * media.role video, music, game, event, phone, production, filter, abstract, stream
+ * event.id button-click, session-login
+ * event.mouse.x
+ * event.mouse.y
+ * event.mouse.hpos
+ * event.mouse.vpos
+ * event.mouse.button
+ * window.name
+ * window.id
+ * window.icon
+ * window.icon_name
+ * window.x11.display
+ * window.x11.screen
+ * window.x11.monitor
+ * window.x11.xid
+ * application.name "Rhythmbox Media Player"
+ * application.id "org.gnome.rhythmbox"
+ * application.version
+ * application.icon
+ * application.icon_name
+ * application.language
+ * application.process.id
+ * application.process.binary
+ * application.process.user
+ * application.process.host
+ * device.string
+ * device.api oss, alsa, sunaudio
+ * device.description
+ * device.bus_path
+ * device.serial
+ * device.vendor_product_id
+ * device.class sound, modem, monitor, filter, abstract
+ * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ * device.connector isa, pci, usb, firewire, bluetooth
+ * device.access_mode mmap, mmap_rewrite, serial
+ * device.master_device
+ * device.bufferin.buffer_size
+ * device.bufferin.fragment_size
+ */
+#define PA_PROP_MEDIA_NAME "media.name"
+#define PA_PROP_MEDIA_TITLE "media.title"
+#define PA_PROP_MEDIA_ARTIST "media.artist"
+#define PA_PROP_MEDIA_LANGUAGE "media.language"
+#define PA_PROP_MEDIA_FILENAME "media.filename"
+#define PA_PROP_MEDIA_ICON "media.icon"
+#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
+#define PA_PROP_MEDIA_ROLE "media.role"
+#define PA_PROP_EVENT_ID "event.id"
+#define PA_PROP_EVENT_MOUSE_X "event.mouse.x"
+#define PA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+#define PA_PROP_EVENT_MOUSE_HPOS "event.mouse.hpos"
+#define PA_PROP_EVENT_MOUSE_VPOS "event.mouse.vpos"
+#define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+#define PA_PROP_WINDOW_NAME "window.name"
+#define PA_PROP_WINDOW_ID "window.id"
+#define PA_PROP_WINDOW_ICON "window.icon"
+#define PA_PROP_WINDOW_ICON_NAME "window.icon_name"
+#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
+#define PA_PROP_WINDOW_X11_SCREEN "window.x11.screen"
+#define PA_PROP_WINDOW_X11_MONITOR "window.x11.monitor"
+#define PA_PROP_WINDOW_X11_XID "window.x11.xid"
+#define PA_PROP_APPLICATION_NAME "application.name"
+#define PA_PROP_APPLICATION_ID "application.id"
+#define PA_PROP_APPLICATION_VERSION "application.version"
+#define PA_PROP_APPLICATION_ICON "application.icon"
+#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+#define PA_PROP_APPLICATION_LANGUAGE "application.language"
+#define PA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+#define PA_PROP_DEVICE_STRING "device.string"
+#define PA_PROP_DEVICE_API "device.api"
+#define PA_PROP_DEVICE_DESCRIPTION "device.description"
+#define PA_PROP_DEVICE_BUS_PATH "device.bus_path"
+#define PA_PROP_DEVICE_SERIAL "device.serial"
+#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID "device.vendor_product_id"
+#define PA_PROP_DEVICE_CLASS "device.class"
+#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
+#define PA_PROP_DEVICE_CONNECTOR "device.connector"
+#define PA_PROP_DEVICE_ACCESS_MODE "device.access_mode"
+#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
+#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
+#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+
+/** A property list object. Basically a dictionary with UTF-8 strings
+ * as keys and arbitrary data as values. \since 0.9.11 */
+typedef struct pa_proplist pa_proplist;
+
+/** Allocate a property list. \since 0.9.11 */
+pa_proplist* pa_proplist_new(void);
+
+/** Free the property list. \since 0.9.11 */
+void pa_proplist_free(pa_proplist* p);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. \since 0.9.11 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. The data can be passed as printf()-style format string with
+ * arguments. \since 0.9.11 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4);
+
+/** Append a new arbitrary data entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. \since 0.9.11 */
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+
+/* Return a string entry for the specified key. Will return NULL if
+ * the data is not valid UTF-8. Will return a NUL-terminated string in
+ * an internally allocated buffer. The caller should make a copy of
+ * the data before accessing the property list again. \since 0.9.11 */
+const char *pa_proplist_gets(pa_proplist *p, const char *key);
+
+/** Return the the value for the specified key. Will return a
+ * NUL-terminated string for string entries. The pointer returned will
+ * point to an internally allocated buffer. The caller should make a
+ * copy of the data before the property list is accessed again. \since
+ * 0.9.11 */
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
+
+/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
+typedef enum pa_update_mode {
+ PA_UPDATE_SET, /*< Replace the entirey property list with the new one. Don't keep any of the old data around */
+ PA_UPDATE_MERGE, /*< Merge new property list into the existing one, not replacing any old entries if they share a common key with the new property list. */
+ PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with the new property list. */
+} pa_update_mode_t;
+
+/** Merge property list "other" into "p", adhering the merge mode as
+ * specified in "mode". \since 0.9.11 */
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
+
+/** Removes a single entry from the property list, identified be the
+ * specified key name. \since 0.9.11 */
+int pa_proplist_unset(pa_proplist *p, const char *key);
+
+/** Similar to pa_proplist_remove() but takes an array of keys to
+ * remove. The array should be terminated by a NULL pointer. Return -1
+ * on failure, otherwise the number of entries actually removed (which
+ * might even be 0, if there where no matching entries to
+ * remove). \since 0.9.11 */
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
+
+/** Iterate through the property list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_proplist_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. The property list should not be modified during
+ * iteration through the list -- except for deleting the current
+ * looked at entry. On each invication this function will return the
+ * key string for the next entry. The keys in the property list do not
+ * have any particular order. \since 0.9.11 */
+const char *pa_proplist_iterate(pa_proplist *p, void **state);
+
+/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
+ * 0.9.11 */
+char *pa_proplist_to_string(pa_proplist *p);
+
+/** Returns 1 if an entry for the specified key is existant in the
+ * property list. \since 0.9.11 */
+int pa_proplist_contains(pa_proplist *p, const char *key);
+
+/** Remove all entries from the property list object. \since 0.9.11 */
+void pa_proplist_clear(pa_proplist *p);
+
+/** Allocate a new property list and copy over every single entry from
+ * the specific list. \since 0.9.11 */
+pa_proplist* pa_proplist_copy(pa_proplist *t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index 88d1275b..ee8a4bb8 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -1,8 +1,6 @@
#ifndef foopulseaudiohfoo
#define foopulseaudiohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 327e8e54..4aef5bb0 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,11 +30,12 @@
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulse/timeval.h>
#include "sample.h"
size_t pa_sample_size(const pa_sample_spec *spec) {
-
+
static const size_t table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
@@ -44,13 +43,15 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
- [PA_SAMPLE_FLOAT32BE] = 4
+ [PA_SAMPLE_FLOAT32BE] = 4,
+ [PA_SAMPLE_S32LE] = 4,
+ [PA_SAMPLE_S32BE] = 4,
};
pa_assert(spec);
pa_assert(spec->format >= 0);
pa_assert(spec->format < PA_SAMPLE_MAX);
-
+
return table[spec->format];
}
@@ -68,13 +69,13 @@ size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
- return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
+ return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
pa_assert(spec);
- return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec);
+ return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
int pa_sample_spec_valid(const pa_sample_spec *spec) {
@@ -95,7 +96,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(a);
pa_assert(b);
- return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+ return
+ (a->format == b->format) &&
+ (a->rate == b->rate) &&
+ (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f) {
@@ -107,6 +111,8 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
+ [PA_SAMPLE_S32LE] = "s32le",
+ [PA_SAMPLE_S32BE] = "s32be",
};
if (f < 0 || f >= PA_SAMPLE_MAX)
@@ -130,7 +136,7 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
pa_assert(s);
-
+
if (v >= ((unsigned) 1024)*1024*1024)
pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
else if (v >= ((unsigned) 1024)*1024)
@@ -156,7 +162,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
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)
+ else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0 || strcasecmp(format, "float") == 0)
return PA_SAMPLE_FLOAT32NE;
else if (strcasecmp(format, "float32re") == 0)
return PA_SAMPLE_FLOAT32RE;
@@ -168,6 +174,14 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
return PA_SAMPLE_ULAW;
else if (strcasecmp(format, "alaw") == 0)
return PA_SAMPLE_ALAW;
+ else if (strcasecmp(format, "s32le") == 0)
+ return PA_SAMPLE_S32LE;
+ else if (strcasecmp(format, "s32be") == 0)
+ return PA_SAMPLE_S32BE;
+ else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
+ return PA_SAMPLE_S32NE;
+ else if (strcasecmp(format, "s32re") == 0)
+ return PA_SAMPLE_S32RE;
return -1;
}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index b307621e..1ba3f871 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -1,8 +1,6 @@
#ifndef foosamplehfoo
#define foosamplehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,8 +25,10 @@
#include <inttypes.h>
#include <sys/types.h>
+#include <sys/param.h>
#include <math.h>
+#include <pulse/gccmacro.h>
#include <pulse/cdecl.h>
/** \page sample Sample Format Specifications
@@ -42,13 +42,15 @@
*
* PulseAudio supports the following sample formats:
*
- * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM.
- * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian.
- * \li PA_SAMPLE_S16BE - Signed 16 bit PCM, big endian.
+ * \li PA_SAMPLE_U8 - Unsigned 8 bit integer PCM.
+ * \li PA_SAMPLE_S16LE - Signed 16 integer bit PCM, little endian.
+ * \li PA_SAMPLE_S16BE - Signed 16 integer bit PCM, big endian.
* \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian.
* \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian.
* \li PA_SAMPLE_ALAW - 8 bit a-Law.
* \li PA_SAMPLE_ULAW - 8 bit mu-Law.
+ * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
+ * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
*
* The floating point sample formats have the range from -1 to 1.
*
@@ -102,11 +104,19 @@
PA_C_DECL_BEGIN
+#if !defined(WORDS_BIGENDIAN)
+#if defined(__BYTE_ORDER)
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#endif
+#endif
+
/** Maximum number of allowed channels */
-#define PA_CHANNELS_MAX 32
+#define PA_CHANNELS_MAX 32U
/** Maximum allowed sample rate */
-#define PA_RATE_MAX (48000*4)
+#define PA_RATE_MAX (48000U*4U)
/** Sample format */
typedef enum pa_sample_format {
@@ -117,6 +127,8 @@ typedef enum pa_sample_format {
PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+ PA_SAMPLE_S32LE, /**< Signed 32 Bit PCM, little endian (PC) */
+ PA_SAMPLE_S32BE, /**< Signed 32 Bit PCM, big endian (PC) */
PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
PA_SAMPLE_INVALID = -1 /**< An invalid value */
} pa_sample_format_t;
@@ -126,19 +138,27 @@ typedef enum pa_sample_format {
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
#else
/** Signed 16 Bit PCM, native endian */
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
#endif
/** A Shortcut for PA_SAMPLE_FLOAT32NE */
@@ -151,7 +171,7 @@ typedef struct pa_sample_spec {
uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
} pa_sample_spec;
-/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
+/** Type for usec specifications (unsigned). Always 64 bit. */
typedef uint64_t pa_usec_t;
/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index a4a7244a..5e31e7af 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,8 +27,11 @@
#include <stdio.h>
#include <string.h>
+#include <pulse/utf8.h>
+
#include <pulsecore/pstream-util.h>
#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
#include "internal.h"
@@ -39,6 +40,7 @@
int pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_tagstruct *t;
uint32_t tag;
+ const char *name;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -46,15 +48,28 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID);
+ if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME);
+
+ PA_CHECK_VALIDITY(s->context, name && *name && pa_utf8_valid(name), PA_ERR_INVALID);
+
pa_stream_ref(s);
s->direction = PA_STREAM_UPLOAD;
+ s->flags = 0;
t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
- pa_tagstruct_puts(t, s->name);
+
+ pa_tagstruct_puts(t, name);
pa_tagstruct_put_sample_spec(t, &s->sample_spec);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_putu32(t, length);
+
+ if (s->context->version >= 13) {
+ pa_init_proplist(s->proplist);
+ pa_tagstruct_put_proplist(t, s->proplist);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@@ -67,7 +82,7 @@ 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;
-
+
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -85,6 +100,73 @@ int pa_stream_finish_upload(pa_stream *s) {
return 0;
}
+static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+ uint32_t idx = PA_INVALID_INDEX;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ } else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
+ success = 0;
+
+ if (o->callback) {
+ pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+ cb(o->context, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ idx = PA_INVALID_INDEX;
+ } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
@@ -107,8 +189,47 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
+
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
+ pa_tagstruct_put_proplist(t, p);
+ pa_proplist_free(p);
+ }
+
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);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_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, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ if (!dev)
+ dev = c->conf->default_sink;
+
+ t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, dev);
+ pa_tagstruct_putu32(t, volume);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
@@ -128,9 +249,9 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
pa_tagstruct_puts(t, name);
+
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/scache.h b/src/pulse/scache.h
index 31fd8956..f380b4e8 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -1,8 +1,6 @@
#ifndef fooscachehfoo
#define fooscachehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -79,14 +77,25 @@
PA_C_DECL_BEGIN
+/** Callback prototype for pa_context_play_sample_with_proplist(). The
+ * idx value is the index of the sink input object, or
+ * PA_INVALID_INDEX on failure. \since 0.9.11 */
+typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
/** Make this stream a sample upload stream */
int pa_stream_connect_upload(pa_stream *s, size_t length);
-/** Finish the sample upload, the stream name will become the sample name. You cancel a samp
- * le upload by issuing pa_stream_disconnect() */
+/** Finish the sample upload, the stream name will become the sample
+ * name. You cancel a sample upload by issuing
+ * pa_stream_disconnect() */
int pa_stream_finish_upload(pa_stream *s);
-/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Play a sample from the sample cache to the specified device. If
+ * the latter is NULL use the default sink. Returns an operation
+ * object */
pa_operation* pa_context_play_sample(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
@@ -95,8 +104,18 @@ pa_operation* pa_context_play_sample(
pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
-/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
-pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata);
+/** Play a sample from the sample cache to the specified device,
+ * allowing specification of a property list for the playback
+ * stream. If the latter is NULL use the default sink. Returns an
+ * operation object. \since 0.9.11 */
+pa_operation* pa_context_play_sample_with_proplist(
+ pa_context *c /**< Context */,
+ const char *name /**< Name of the sample to play */,
+ const char *dev /**< Sink to play this sample on */,
+ pa_volume_t volume /**< Volume to play this sample with */ ,
+ pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
+ pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
+ void *userdata /**< Userdata to pass to the callback */);
PA_C_DECL_END
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index 1072fb4d..70396835 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -1,6 +1,4 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
index f76c1d67..a1380a0a 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -1,8 +1,6 @@
#ifndef foosimplehfoo
#define foosimplehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -130,18 +128,18 @@ pa_simple* pa_simple_new(
void pa_simple_free(pa_simple *s);
/** Write some data to the server */
-int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error);
+int pa_simple_write(pa_simple *s, const void*data, size_t bytes, int *error);
/** Wait until all data already written is played by the daemon */
int pa_simple_drain(pa_simple *s, int *error);
/** Read some data from the server */
-int pa_simple_read(pa_simple *s, void*data, size_t length, int *error);
+int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error);
-/** Return the playback latency. \since 0.5 */
+/** Return the playback latency. */
pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
-/** Flush the playback buffer. \since 0.5 */
+/** Flush the playback buffer. */
int pa_simple_flush(pa_simple *s, int *error);
PA_C_DECL_END
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 47906a5c..3bee7a05 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,26 +36,21 @@
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
#include "internal.h"
-#define LATENCY_IPOL_INTERVAL_USEC (100000L)
+#define LATENCY_IPOL_INTERVAL_USEC (333*PA_USEC_PER_MSEC)
-pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
- pa_stream *s;
- int i;
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#define SMOOTHER_MIN_HISTORY (4)
- 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);
- PA_REFCNT_INIT(s);
- s->context = c;
- s->mainloop = c->mainloop;
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+ return pa_stream_new_with_proplist(c, name, ss, map, NULL);
+}
+static void reset_callbacks(pa_stream *s) {
s->read_callback = NULL;
s->read_userdata = NULL;
s->write_callback = NULL;
@@ -70,47 +63,90 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
s->underflow_userdata = NULL;
s->latency_update_callback = NULL;
s->latency_update_userdata = NULL;
+ s->moved_callback = NULL;
+ s->moved_userdata = NULL;
+ s->suspended_callback = NULL;
+ s->suspended_userdata = NULL;
+ s->started_callback = NULL;
+ s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+ pa_context *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_proplist *p) {
+
+ pa_stream *s;
+ int i;
+ pa_channel_map tmap;
+
+ 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, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
+
+ if (!map)
+ PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+ s = pa_xnew(pa_stream, 1);
+ PA_REFCNT_INIT(s);
+ s->context = c;
+ s->mainloop = c->mainloop;
s->direction = PA_STREAM_NODIRECTION;
- s->name = pa_xstrdup(name);
- s->sample_spec = *ss;
+ s->state = PA_STREAM_UNCONNECTED;
s->flags = 0;
- if (map)
- s->channel_map = *map;
- else
- pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ s->sample_spec = *ss;
+ s->channel_map = *map;
+
+ s->direct_on_input = PA_INVALID_INDEX;
+
+ s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+ if (name)
+ pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
s->channel = 0;
- s->channel_valid = 0;
+ s->channel_valid = FALSE;
s->syncid = c->csyncid++;
- s->device_index = PA_INVALID_INDEX;
+ s->stream_index = PA_INVALID_INDEX;
+
s->requested_bytes = 0;
- s->state = PA_STREAM_UNCONNECTED;
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
- s->peek_memchunk.index = 0;
- s->peek_memchunk.length = 0;
- s->peek_memchunk.memblock = NULL;
+ s->device_index = PA_INVALID_INDEX;
+ s->device_name = NULL;
+ s->suspended = FALSE;
+
+ pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
+ s->corked = FALSE;
+
+ memset(&s->timing_info, 0, sizeof(s->timing_info));
+ s->timing_info_valid = FALSE;
+
s->previous_time = 0;
- s->timing_info_valid = 0;
+
s->read_index_not_before = 0;
s->write_index_not_before = 0;
-
for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
s->write_index_corrections[i].valid = 0;
s->current_write_index_correction = 0;
- s->corked = 0;
+ s->auto_timing_update_event = NULL;
+ s->auto_timing_update_requested = FALSE;
- s->cached_time_valid = 0;
+ reset_callbacks(s);
- s->auto_timing_update_event = NULL;
- s->auto_timing_update_requested = 0;
+ s->smoother = NULL;
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
PA_LLIST_PREPEND(pa_stream, c->streams, s);
@@ -119,16 +155,51 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
return s;
}
-static void stream_free(pa_stream *s) {
+static void stream_unlink(pa_stream *s) {
+ pa_operation *o, *n;
pa_assert(s);
- pa_assert(!s->context);
- pa_assert(!s->channel_valid);
+
+ if (!s->context)
+ return;
+
+ /* Detach from context */
+
+ /* Unref all operatio object that point to us */
+ for (o = s->context->operations; o; o = n) {
+ n = o->next;
+
+ if (o->stream == s)
+ pa_operation_cancel(o);
+ }
+
+ /* Drop all outstanding replies for this stream */
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ if (s->channel_valid) {
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+ s->channel = 0;
+ s->channel_valid = FALSE;
+ }
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+
+ s->context = NULL;
if (s->auto_timing_update_event) {
pa_assert(s->mainloop);
s->mainloop->time_free(s->auto_timing_update_event);
}
+ reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+ pa_assert(s);
+
+ stream_unlink(s);
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -138,7 +209,13 @@ static void stream_free(pa_stream *s) {
if (s->record_memblockq)
pa_memblockq_free(s->record_memblockq);
- pa_xfree(s->name);
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ pa_xfree(s->device_name);
pa_xfree(s);
}
@@ -178,7 +255,7 @@ uint32_t pa_stream_get_index(pa_stream *s) {
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
- return s->device_index;
+ return s->stream_index;
}
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
@@ -191,46 +268,41 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
pa_stream_ref(s);
s->state = st;
+
if (s->state_callback)
s->state_callback(s, s->state_userdata);
- if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
-
- /* Detach from context */
- pa_operation *o, *n;
-
- /* Unref all operatio object that point to us */
- for (o = s->context->operations; o; o = n) {
- n = o->next;
-
- if (o->stream == s)
- pa_operation_cancel(o);
- }
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+ stream_unlink(s);
- /* Drop all outstanding replies for this stream */
- if (s->context->pdispatch)
- pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+ pa_stream_unref(s);
+}
- if (s->channel_valid)
- pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
- pa_stream_unref(s);
+ if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+ return;
- s->channel = 0;
- s->channel_valid = 0;
+ if (s->state == PA_STREAM_READY &&
+ (force || !s->auto_timing_update_requested)) {
+ pa_operation *o;
- s->context = NULL;
+/* pa_log("automatically requesting new timing data"); */
- 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;
+ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+ pa_operation_unref(o);
+ s->auto_timing_update_requested = TRUE;
+ }
}
- pa_stream_unref(s);
+ 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);
+ }
}
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -255,6 +327,9 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
pa_context_set_error(c, PA_ERR_KILLED);
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -262,6 +337,196 @@ finish:
pa_context_unref(c);
}
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ const char *dn;
+ pa_bool_t suspended;
+ uint32_t di;
+ pa_usec_t usec;
+ uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 12) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &di) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->version >= 13) {
+
+ if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+ if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &fragsize) < 0 ||
+ pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ } else {
+ if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &tlength) < 0 ||
+ pa_tagstruct_getu32(t, &prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &minreq) < 0 ||
+ pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!dn || di == PA_INVALID_INDEX) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (c->version >= 13) {
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+
+ s->buffer_attr.maxlength = maxlength;
+ s->buffer_attr.fragsize = fragsize;
+ s->buffer_attr.tlength = tlength;
+ s->buffer_attr.prebuf = prebuf;
+ s->buffer_attr.minreq = minreq;
+ }
+
+ pa_xfree(s->device_name);
+ s->device_name = pa_xstrdup(dn);
+ s->device_index = di;
+
+ s->suspended = suspended;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->moved_callback)
+ s->moved_callback(s, s->moved_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ pa_bool_t suspended;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 12) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ s->suspended = suspended;
+
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ else
+ pa_smoother_resume(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->suspended_callback)
+ s->suspended_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_STARTED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 13) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->started_callback)
+ s->started_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
@@ -285,12 +550,13 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
- if (s->state == PA_STREAM_READY) {
- s->requested_bytes += bytes;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
- if (s->requested_bytes > 0 && s->write_callback)
- s->write_callback(s, s->requested_bytes, s->write_userdata);
- }
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
finish:
pa_context_unref(c);
@@ -318,6 +584,21 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->smoother)
+ if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->state == PA_STREAM_READY) {
if (command == PA_COMMAND_OVERFLOW) {
@@ -333,34 +614,7 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
pa_context_unref(c);
}
-static void request_auto_timing_update(pa_stream *s, int force) {
- pa_assert(s);
- pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
- if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
- return;
-
- if (s->state == PA_STREAM_READY &&
- (force || !s->auto_timing_update_requested)) {
- pa_operation *o;
-
-/* pa_log("automatically requesting new timing data"); */
-
- if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
- pa_operation_unref(o);
- s->auto_timing_update_requested = 1;
- }
- }
-
- 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) {
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -373,7 +627,7 @@ static void invalidate_indexes(pa_stream *s, int r, int w) {
s->write_index_not_before = s->context->ctag;
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
/* pa_log("write_index invalidated"); */
}
@@ -382,16 +636,12 @@ static void invalidate_indexes(pa_stream *s, int r, int w) {
s->read_index_not_before = s->context->ctag;
if (s->timing_info_valid)
- s->timing_info.read_index_corrupt = 1;
+ s->timing_info.read_index_corrupt = TRUE;
/* pa_log("read_index invalidated"); */
}
- if ((s->direction == PA_STREAM_PLAYBACK && r) ||
- (s->direction == PA_STREAM_RECORD && w))
- s->cached_time_valid = 0;
-
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
@@ -400,10 +650,8 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
-/* pa_log("time event"); */
-
pa_stream_ref(s);
- request_auto_timing_update(s, 0);
+ request_auto_timing_update(s, FALSE);
pa_stream_unref(s);
}
@@ -423,9 +671,38 @@ static void create_stream_complete(pa_stream *s) {
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);
+
+ request_auto_timing_update(s, TRUE);
}
}
+static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_sample_spec *ss) {
+ pa_assert(s);
+ pa_assert(attr);
+ pa_assert(ss);
+
+ if (s->context->version >= 13)
+ return;
+
+ /* Version older than 0.9.10 didn't do server side buffer_attr
+ * selection, hence we have to fake it on the client side */
+
+ if (!attr->maxlength <= 0)
+ attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
+
+ if (!attr->tlength <= 0)
+ attr->tlength = pa_bytes_per_second(ss)*2; /* 2s of buffering */
+
+ if (!attr->minreq <= 0)
+ attr->minreq = (2*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */
+
+ if (!attr->prebuf)
+ attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
+
+ if (!attr->fragsize)
+ attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
+}
+
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
@@ -437,7 +714,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -445,13 +722,14 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
- ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
+ s->channel == PA_INVALID_INDEX ||
+ ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
}
- if (pa_context_get_server_protocol_version(s->context) >= 9) {
+ if (s->context->version >= 9) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
@@ -469,6 +747,54 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
}
}
+ if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ const char *dn = NULL;
+ pa_bool_t suspended;
+
+ if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!dn || s->device_index == PA_INVALID_INDEX ||
+ ss.channels != cm.channels ||
+ !pa_channel_map_valid(&cm) ||
+ !pa_sample_spec_valid(&ss) ||
+ (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+ (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+ (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pa_xfree(s->device_name);
+ s->device_name = pa_xstrdup(dn);
+ s->suspended = suspended;
+
+ s->channel_map = cm;
+ s->sample_spec = ss;
+ }
+
+ if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
+ pa_usec_t usec;
+
+ if (pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+ }
+
if (!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
@@ -484,22 +810,14 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
pa_frame_size(&s->sample_spec),
1,
0,
+ 0,
NULL);
}
- s->channel_valid = 1;
+ s->channel_valid = TRUE;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, 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;
-
- } else
- create_stream_complete(s);
+ create_stream_complete(s);
finish:
pa_stream_unref(s);
@@ -519,13 +837,34 @@ static int create_stream(
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ?
- PA_STREAM_START_CORKED|
- PA_STREAM_INTERPOLATE_TIMING|
- PA_STREAM_NOT_MONOTONOUS|
- PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
+ PA_STREAM_INTERPOLATE_TIMING|
+ PA_STREAM_NOT_MONOTONOUS|
+ PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_NO_REMAP_CHANNELS|
+ PA_STREAM_NO_REMIX_CHANNELS|
+ PA_STREAM_FIX_FORMAT|
+ PA_STREAM_FIX_RATE|
+ PA_STREAM_FIX_CHANNELS|
+ PA_STREAM_DONT_MOVE|
+ PA_STREAM_VARIABLE_RATE|
+ PA_STREAM_PEAK_DETECT|
+ PA_STREAM_START_MUTED|
+ PA_STREAM_ADJUST_LATENCY)), PA_ERR_INVALID);
+
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
+ /* Althought some of the other flags are not supported on older
+ * version, we don't check for them here, because it doesn't hurt
+ * when they are passed but actually not supported. This makes
+ * client development easier */
+
+ PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
@@ -539,13 +878,19 @@ static int create_stream(
if (attr)
s->buffer_attr = *attr;
- else {
- /* 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/50;
- s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
- s->buffer_attr.fragsize = s->buffer_attr.tlength/50;
+ automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+
+ if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+ pa_usec_t x;
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS), SMOOTHER_MIN_HISTORY);
+
+ x = pa_rtclock_usec();
+ pa_smoother_set_time_offset(s->smoother, x);
+ pa_smoother_pause(s->smoother, x);
}
if (!dev)
@@ -556,9 +901,11 @@ static int create_stream(
s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
&tag);
+ if (s->context->version < 13)
+ pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
pa_tagstruct_put(
t,
- PA_TAG_STRING, s->name,
PA_TAG_SAMPLE_SPEC, &s->sample_spec,
PA_TAG_CHANNEL_MAP, &s->channel_map,
PA_TAG_U32, PA_INVALID_INDEX,
@@ -585,6 +932,36 @@ static int create_stream(
} else
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+ if (s->context->version >= 12) {
+ pa_tagstruct_put(
+ t,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
+ PA_TAG_INVALID);
+ }
+
+ if (s->context->version >= 13) {
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+ else
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT);
+
+ pa_tagstruct_put(
+ t,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY,
+ PA_TAG_PROPLIST, s->proplist,
+ PA_TAG_INVALID);
+
+ if (s->direction == PA_STREAM_RECORD)
+ pa_tagstruct_putu32(t, s->direct_on_input);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@@ -629,6 +1006,10 @@ int pa_stream_write(
pa_seek_mode_t seek) {
pa_memchunk chunk;
+ pa_seek_mode_t t_seek;
+ int64_t t_offset;
+ size_t t_length;
+ const void *t_data;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -642,21 +1023,42 @@ int pa_stream_write(
if (length <= 0)
return 0;
- 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);
- tdata = pa_memblock_acquire(chunk.memblock);
- memcpy(tdata, data, length);
- pa_memblock_release(chunk.memblock);
- }
+ t_seek = seek;
+ t_offset = offset;
+ t_length = length;
+ t_data = data;
- chunk.index = 0;
- chunk.length = length;
+ while (t_length > 0) {
- pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
- pa_memblock_unref(chunk.memblock);
+ chunk.index = 0;
+
+ if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+ chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
+ chunk.length = t_length;
+ } else {
+ void *d;
+
+ chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
+ chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+ d = pa_memblock_acquire(chunk.memblock);
+ memcpy(d, t_data, chunk.length);
+ pa_memblock_release(chunk.memblock);
+ }
+
+ pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+ t_offset = 0;
+ t_seek = PA_SEEK_RELATIVE;
+
+ t_data = (const uint8_t*) t_data + chunk.length;
+ t_length -= chunk.length;
+
+ pa_memblock_unref(chunk.memblock);
+ }
+
+ if (free_cb && pa_pstream_get_shm(s->context->pstream))
+ free_cb((void*) data);
if (length < s->requested_bytes)
s->requested_bytes -= length;
@@ -669,31 +1071,31 @@ int pa_stream_write(
if (s->write_index_corrections[s->current_write_index_correction].valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
- s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+ s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
s->write_index_corrections[s->current_write_index_correction].value = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
s->write_index_corrections[s->current_write_index_correction].value += offset + length;
} else
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
}
/* Update the write index in the already available latency data */
if (s->timing_info_valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->timing_info.write_index_corrupt = 0;
+ s->timing_info.write_index_corrupt = FALSE;
s->timing_info.write_index = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->timing_info.write_index_corrupt)
s->timing_info.write_index += offset + length;
} else
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
}
if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
return 0;
@@ -742,9 +1144,7 @@ int pa_stream_drop(pa_stream *s) {
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;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
return 0;
}
@@ -790,10 +1190,71 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
return o;
}
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+ pa_usec_t usec;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_READY);
+ pa_assert(s->direction != PA_STREAM_UPLOAD);
+ pa_assert(s->timing_info_valid);
+ pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+ pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Because the latency info took a little time to come
+ * to us, we assume that the real output time is actually
+ * a little ahead */
+ usec += s->timing_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Add transport latency */
+ usec += s->timing_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->timing_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+ }
+
+ return usec;
+}
+
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
struct timeval local, remote, now;
pa_timing_info *i;
+ pa_bool_t playing = FALSE;
+ uint64_t underrun_for = 0, playing_for = 0;
pa_assert(pd);
pa_assert(o);
@@ -806,29 +1267,48 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
- o->stream->timing_info_valid = 0;
- i->write_index_corrupt = 0;
- i->read_index_corrupt = 0;
+ o->stream->timing_info_valid = FALSE;
+ i->write_index_corrupt = FALSE;
+ i->read_index_corrupt = FALSE;
/* pa_log("timing update %u\n", tag); */
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
- } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
- pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
- pa_tagstruct_get_boolean(t, &i->playing) < 0 ||
- pa_tagstruct_get_timeval(t, &local) < 0 ||
- pa_tagstruct_get_timeval(t, &remote) < 0 ||
- pa_tagstruct_gets64(t, &i->write_index) < 0 ||
- pa_tagstruct_gets64(t, &i->read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_context_fail(o->context, PA_ERR_PROTOCOL);
- goto finish;
-
} else {
- o->stream->timing_info_valid = 1;
+
+ if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &playing) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->context->version >= 13 &&
+ o->stream->direction == PA_STREAM_PLAYBACK)
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ o->stream->timing_info_valid = TRUE;
+ i->playing = (int) playing;
+ i->since_underrun = playing ? playing_for : underrun_for;
pa_gettimeofday(&now);
@@ -841,22 +1321,22 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
else
i->transport_usec = pa_timeval_diff(&now, &remote);
- i->synchronized_clocks = 1;
+ i->synchronized_clocks = TRUE;
i->timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
i->transport_usec = pa_timeval_diff(&now, &local)/2;
- i->synchronized_clocks = 0;
+ i->synchronized_clocks = FALSE;
i->timestamp = local;
pa_timeval_add(&i->timestamp, i->transport_usec);
}
/* Invalidate read and write indexes if necessary */
if (tag < o->stream->read_index_not_before)
- i->read_index_corrupt = 1;
+ i->read_index_corrupt = TRUE;
if (tag < o->stream->write_index_not_before)
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
if (o->stream->direction == PA_STREAM_PLAYBACK) {
/* Write index correction */
@@ -882,11 +1362,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
if (o->stream->write_index_corrections[j].corrupt) {
/* A corrupting seek was made */
i->write_index = 0;
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
} else if (o->stream->write_index_corrections[j].absolute) {
/* An absolute seek was made */
i->write_index = o->stream->write_index_corrections[j].value;
- i->write_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
} else if (!i->write_index_corrupt) {
/* A relative seek was made */
i->write_index += o->stream->write_index_corrections[j].value;
@@ -901,28 +1381,56 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
}
- o->stream->cached_time_valid = 0;
- }
-
- o->stream->auto_timing_update_requested = 0;
/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
- /* Clear old correction entries */
- if (o->stream->direction == PA_STREAM_PLAYBACK) {
- int n;
+ /* Clear old correction entries */
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ int n;
+
+ for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+ if (!o->stream->write_index_corrections[n].valid)
+ continue;
+
+ if (o->stream->write_index_corrections[n].tag <= tag)
+ o->stream->write_index_corrections[n].valid = FALSE;
+ }
+ }
+
+ /* Update smoother */
+ if (o->stream->smoother) {
+ pa_usec_t u, x;
+
+ u = x = pa_rtclock_usec() - i->transport_usec;
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK &&
+ o->context->version >= 13) {
+ pa_usec_t su;
+
+ /* If we weren't playing then it will take some time
+ * until the audio will actually come out through the
+ * speakers. Since we follow that timing here, we need
+ * to try to fix this up */
+
+ su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+ if (su < i->sink_usec)
+ x += i->sink_usec - su;
+ }
+
+ if (!i->playing)
+ pa_smoother_pause(o->stream->smoother, x);
- for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
- if (!o->stream->write_index_corrections[n].valid)
- continue;
+ /* Update the smoother */
+ if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+ (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+ pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
- if (o->stream->write_index_corrections[n].tag <= tag)
- o->stream->write_index_corrections[n].valid = 0;
+ if (i->playing)
+ pa_smoother_resume(o->stream->smoother, x);
}
}
- /* First, let's complete the initialization, if necessary. */
- if (o->stream->state == PA_STREAM_CREATING)
- create_stream_complete(o->stream);
+ o->stream->auto_timing_update_requested = FALSE;
if (o->stream->latency_update_callback)
o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
@@ -972,15 +1480,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t
if (s->direction == PA_STREAM_PLAYBACK) {
/* Fill in initial correction data */
- o->stream->current_write_index_correction = cidx;
- o->stream->write_index_corrections[cidx].valid = 1;
- o->stream->write_index_corrections[cidx].tag = tag;
- o->stream->write_index_corrections[cidx].absolute = 0;
- o->stream->write_index_corrections[cidx].value = 0;
- o->stream->write_index_corrections[cidx].corrupt = 0;
- }
-/* pa_log("requesting update %u\n", tag); */
+ s->current_write_index_correction = cidx;
+
+ s->write_index_corrections[cidx].valid = TRUE;
+ s->write_index_corrections[cidx].absolute = FALSE;
+ s->write_index_corrections[cidx].corrupt = FALSE;
+ s->write_index_corrections[cidx].tag = tag;
+ s->write_index_corrections[cidx].value = 0;
+ }
return o;
}
@@ -995,7 +1503,7 @@ void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -1040,6 +1548,9 @@ void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->read_callback = cb;
s->read_userdata = userdata;
}
@@ -1048,6 +1559,9 @@ void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->write_callback = cb;
s->write_userdata = userdata;
}
@@ -1056,6 +1570,9 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->state_callback = cb;
s->state_userdata = userdata;
}
@@ -1064,6 +1581,9 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
@@ -1072,6 +1592,9 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
@@ -1080,10 +1603,46 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
+void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->moved_callback = cb;
+ s->moved_userdata = userdata;
+}
+
+void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->suspended_callback = cb;
+ s->suspended_userdata = userdata;
+}
+
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->started_callback = cb;
+ s->started_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
@@ -1096,7 +1655,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -1139,8 +1698,18 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (s->direction == PA_STREAM_PLAYBACK)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1171,23 +1740,34 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
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);
if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_index_corrections[s->current_write_index_correction].valid)
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
if (s->buffer_attr.prebuf > 0)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
else
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
+
+ if (s->smoother && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
} else
- invalidate_indexes(s, 0, 1);
+ invalidate_indexes(s, FALSE, TRUE);
}
return o;
@@ -1199,11 +1779,12 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
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);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1214,19 +1795,18 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
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);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1235,22 +1815,32 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
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);
- o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ if (s->context->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
- t = pa_tagstruct_command(
- s->context,
- s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
- &tag);
- pa_tagstruct_putu32(t, s->channel);
- pa_tagstruct_puts(t, name);
- pa_pstream_send_tagstruct(s->context->pstream, t);
- pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ }
return o;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
- pa_usec_t usec = 0;
+ pa_usec_t usec;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1261,65 +1851,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
- if (s->cached_time_valid)
- /* We alredy calculated the time value for this timing info, so let's reuse it */
- usec = s->cached_time;
- else {
- if (s->direction == PA_STREAM_PLAYBACK) {
- /* The last byte that was written into the output device
- * had this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Because the latency info took a little time to come
- * to us, we assume that the real output time is actually
- * a little ahead */
- usec += s->timing_info.transport_usec;
-
- /* However, the output device usually maintains a buffer
- too, hence the real sample currently played is a little
- back */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
-
- } else if (s->direction == PA_STREAM_RECORD) {
- /* The last byte written into the server side queue had
- * this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Add transport latency */
- usec += s->timing_info.transport_usec;
-
- /* Add latency of data in device buffer */
- usec += s->timing_info.source_usec;
-
- /* If this is a monitor source, we need to correct the
- * time by the playback device buffer */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
- }
-
- s->cached_time = usec;
- s->cached_time_valid = 1;
- }
-
- /* Interpolate if requested */
- if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
-
- /* We just add the time that passed since the latency info was
- * current */
- if (!s->corked && s->timing_info.playing) {
- struct timeval now;
- usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
- }
- }
+ if (s->smoother)
+ usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+ else
+ usec = calc_time(s, FALSE);
/* Make sure the time runs monotonically */
if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
@@ -1420,8 +1955,304 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(s->context,
- pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
return &s->buffer_attr;
}
+
+static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else {
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ } else if (o->stream->direction == PA_STREAM_RECORD) {
+ if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (o->callback) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
+pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(attr);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+
+ pa_tagstruct_putu32(t, attr->maxlength);
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put(
+ t,
+ PA_TAG_U32, attr->tlength,
+ PA_TAG_U32, attr->prebuf,
+ PA_TAG_U32, attr->minreq,
+ PA_TAG_INVALID);
+ else
+ pa_tagstruct_putu32(t, attr->fragsize);
+
+ if (s->context->version >= 13)
+ pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY));
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+uint32_t pa_stream_get_device_index(pa_stream *s) {
+ 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);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+ return s->device_index;
+}
+
+const char *pa_stream_get_device_name(pa_stream *s) {
+ 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);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
+
+ return s->device_name;
+}
+
+int pa_stream_is_suspended(pa_stream *s) {
+ 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);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ return s->suspended;
+}
+
+int pa_stream_is_corked(pa_stream *s) {
+ 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);
+
+ return s->corked;
+}
+
+static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else {
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
+ pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
+
+ if (o->callback) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, rate > 0 && rate <= PA_RATE_MAX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ o->private = PA_UINT_TO_PTR(rate);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_putu32(t, rate);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const*k;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ s->direct_on_input = sink_input_idx;
+
+ return 0;
+}
+
+uint32_t pa_stream_get_monitor_stream(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+ return s->direct_on_input;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 65603262..55f36b7f 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -1,8 +1,6 @@
#ifndef foostreamhfoo
#define foostreamhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -271,18 +269,30 @@ typedef struct pa_stream pa_stream;
typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
/** A generic request callback */
-typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t length, void *userdata);
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata);
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
-/** Create a new, unconnected stream with the specified name and sample type */
+/** Create a new, unconnected stream with the specified name and
+ * sample type. It is recommended to use pa_stream_new_with_proplist()
+ * instead and specify some initial properties. */
pa_stream* pa_stream_new(
pa_context *c /**< The context to create this stream in */,
const char *name /**< A name for this stream */,
const pa_sample_spec *ss /**< The desired sample format */,
const pa_channel_map *map /**< The desired channel map, or NULL for default */);
+/** Create a new, unconnected stream with the specified name and
+ * sample type, and specify the the initial stream property
+ * list. \since 0.9.11 */
+pa_stream* pa_stream_new_with_proplist(
+ pa_context *c /**< The context to create this stream in */,
+ const char *name /**< A name for this stream */,
+ const pa_sample_spec *ss /**< The desired sample format */,
+ const pa_channel_map *map /**< The desired channel map, or NULL for default */,
+ pa_proplist *p /**< The initial property list */);
+
/** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s);
@@ -295,9 +305,42 @@ pa_stream_state_t pa_stream_get_state(pa_stream *p);
/** Return the context this stream is attached to */
pa_context* pa_stream_get_context(pa_stream *p);
-/** Return the device (sink input or source output) index this stream is connected to */
+/** Return the sink input resp. source output index this stream is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_sink_input_info()
+ * resp. pa_context_get_source_output_info(). */
uint32_t pa_stream_get_index(pa_stream *s);
+/** Return the index of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_index()
+ * resp. pa_context_get_source_info_by_index(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+uint32_t pa_stream_get_device_index(pa_stream *s);
+
+/** Return the name of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_name()
+ * resp. pa_context_get_source_info_by_name(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+const char *pa_stream_get_device_name(pa_stream *s);
+
+/** Return 1 if the sink or source this stream is connected to has
+ * been suspended. This will return 0 if not, and negative on
+ * error. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+int pa_stream_is_suspended(pa_stream *s);
+
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and negative on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
/** Connect the stream to a sink */
int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */,
@@ -327,7 +370,7 @@ int pa_stream_disconnect(pa_stream *s);
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
- size_t length /**< The length of the data to write */,
+ size_t nbytes /**< The length of the data to write in bytes*/,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@@ -336,20 +379,20 @@ int pa_stream_write(
* data will point to the actual data and length will contain the size
* of the data in bytes (which can be less than a complete framgnet).
* Use pa_stream_drop() to actually remove the data from the
- * buffer. If no data is available will return a NULL pointer \since 0.8 */
+ * buffer. If no data is available will return a NULL pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
- size_t *length /**< The length of the data read */);
+ size_t *nbytes /**< The length of the data read in bytes */);
/** Remove the current fragment on record streams. It is invalid to do this without first
- * calling pa_stream_peek(). \since 0.8 */
+ * calling pa_stream_peek(). */
int pa_stream_drop(pa_stream *p);
-/** Return the nember of bytes that may be written using pa_stream_write() */
+/** Return the number of bytes that may be written using pa_stream_write() */
size_t pa_stream_writable_size(pa_stream *p);
-/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
+/** Return the number of bytes that may be read using pa_stream_read()*/
size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */
@@ -369,36 +412,63 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
/** Set the callback function that is called when new data is available from the stream.
- * Return the number of bytes read. \since 0.8 */
+ * Return the number of bytes read.*/
void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
-/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */
+/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */
void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
+/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio startet
+ * to reach the speakers already. (Only for playback streams). \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a latency
+ * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
+ * streams only. (Only for playback streams) */
void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
+/** Set the callback function that is called whenever the stream is
+ * moved to a different sink/source. Use pa_stream_get_device_name()or
+ * pa_stream_get_device_index() to query the new sink/source. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the sink/source
+ * this stream is connected to is suspended or resumed. Use
+ * pa_stream_is_suspended() to query the new suspend status. Please
+ * note that the suspend status might also change when the stream is
+ * moved between devices. Thus if you call this function you very
+ * likely want to call pa_stream_set_moved_callback, too. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
/** Flush the playback buffer of this stream. Most of the time you're
- * better off using the parameter delta of pa_stream_write() instead of this
- * function. Available on both playback and recording streams. \since 0.3 */
+ * better off using the parameter delta of pa_stream_write() instead
+ * of this function. Available on both playback and recording
+ * streams. */
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Reenable prebuffering as specified in the pa_buffer_attr
- * structure. Available for playback streams only. \since 0.6 */
+ * structure. Available for playback streams only. */
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request immediate start of playback on this stream. This disables
- * prebuffering as specified in the pa_buffer_attr
- * structure, temporarily. Available for playback streams only. \since 0.3 */
+ * prebuffering as specified in the pa_buffer_attr structure,
+ * temporarily. Available for playback streams only. */
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
-/** Rename the stream. \since 0.5 */
+/** Rename the stream. */
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the
@@ -415,13 +485,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
* be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
- * able to deal with time going 'backwards'. \since 0.6 */
+ * able to deal with time going 'backwards'. */
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
/** Return the total stream latency. This function is based on
* pa_stream_get_time(). In case the stream is a monitoring stream the
* result can be negative, i.e. the captured samples are not yet
- * played. In this case *negative is set to 1. \since 0.6 */
+ * played. In this case *negative is set to 1. */
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
/** Return the latest raw timing data structure. The returned pointer
@@ -433,13 +503,13 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
* function will fail with PA_ERR_NODATA. Please note that the
* write_index member field (and only this field) is updated on each
* pa_stream_write() call, not just when a timing update has been
- * recieved. \since 0.8 */
+ * recieved. */
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
-/** Return a pointer to the stream's sample specification. \since 0.6 */
+/** Return a pointer to the stream's sample specification. */
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
-/** Return a pointer to the stream's channel map. \since 0.8 */
+/** Return a pointer to the stream's channel map. */
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
/** Return the buffer metrics of the stream. Only valid after the
@@ -447,6 +517,43 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
* PulseAudio 0.9. \since 0.9.0 */
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
+/** Change the buffer metrics of the stream during playback. The
+ * server might have chosen different buffer metrics then
+ * requested. The selected metrics may be queried with
+ * pa_stream_get_buffer_attr() as soon as the callback is called. Only
+ * valid after the stream has been connected successfully and if the
+ * server is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata);
+
+/** Change the stream sampling rate during playback. You need to pass
+ * PA_STREAM_VARIABLE_RATE in the flags parameter of
+ * pa_stream_connect() if you plan to use this function. Only valid
+ * after the stream has been connected successfully and if the server
+ * is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, adding new entries. Please note that it is highly
+ * recommended to set as much properties initially via
+ * pa_stream_new_with_proplist() as possible instead a posteriori with
+ * this function, since that information may then be used to route
+ * this stream to the right device. \since 0.9.11 */
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, remove entries. \since 0.9.11 */
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
+
+/** For record streams connected to a monitor source: monitor only a
+ * very specific sink input of the sink. Thus function needs to be
+ * called before pa_stream_connect_record() is called. \since
+ * 0.9.11 */
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx);
+
+/** Return what has been set with pa_stream_set_monitor_stream()
+ * ebfore. \since 0.9.11 */
+uint32_t pa_stream_get_monitor_stream(pa_stream *s);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index 580038cc..d9c06b7e 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,8 @@
#include <stdio.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
@@ -87,6 +86,9 @@ void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
c->subscribe_callback = cb;
c->subscribe_userdata = userdata;
}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index c37ead57..0e4be8c3 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -1,8 +1,6 @@
#ifndef foosubscribehfoo
#define foosubscribehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index 9dd47ae3..6b66696c 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -103,7 +101,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
return NULL;
}
- m->mutex = pa_mutex_new(TRUE, FALSE);
+ m->mutex = pa_mutex_new(TRUE, TRUE);
m->cond = pa_cond_new();
m->accept_cond = pa_cond_new();
m->thread = NULL;
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index ea08f72a..521e29b0 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -1,8 +1,6 @@
#ifndef foothreadmainloophfoo
#define foothreadmainloophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c
index 70ceb71e..9708a735 100644
--- a/src/pulse/timeval.c
+++ b/src/pulse/timeval.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -140,7 +138,7 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
tv->tv_usec += (suseconds_t) v;
/* Normalize */
- while (tv->tv_usec >= PA_USEC_PER_SEC) {
+ while ((unsigned) tv->tv_usec >= PA_USEC_PER_SEC) {
tv->tv_sec++;
tv->tv_usec -= PA_USEC_PER_SEC;
}
@@ -148,6 +146,24 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
return tv;
}
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {
+ unsigned long secs;
+ pa_assert(tv);
+
+ secs = (unsigned long) (v/PA_USEC_PER_SEC);
+ tv->tv_sec -= secs;
+ v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
+
+ if (tv->tv_usec >= (suseconds_t) v)
+ tv->tv_usec -= (suseconds_t) v;
+ else {
+ tv->tv_sec --;
+ tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v;
+ }
+
+ return tv;
+}
+
struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {
pa_assert(tv);
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
index 65a0e513..ee398296 100644
--- a/src/pulse/timeval.h
+++ b/src/pulse/timeval.h
@@ -1,8 +1,6 @@
#ifndef footimevalhfoo
#define footimevalhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
***/
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \file
@@ -33,10 +32,12 @@
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
+#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL)
+#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL)
struct timeval;
@@ -54,7 +55,10 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE
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) PA_GCC_PURE;
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
+
+/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
/** Store the specified uec value in the timeval struct. \since 0.9.7 */
struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v);
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index a02c5d1d..119be542 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -67,7 +65,7 @@
#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 */
@@ -76,7 +74,7 @@ static inline int is_unicode_valid(uint32_t ch) {
return 0;
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
return 0;
-
+
return 1;
}
@@ -99,7 +97,7 @@ static char* utf8_validate(const char *str, char *output) {
uint8_t *o;
pa_assert(str);
-
+
o = (uint8_t*) output;
for (p = (const uint8_t*) str; *p; p++) {
if (*p < 128) {
@@ -208,7 +206,7 @@ static char* iconv_simple(const char *str, const char *to, const char *from) {
pa_assert(str);
pa_assert(to);
pa_assert(from);
-
+
cd = iconv_open(to, from);
if (cd == (iconv_t)-1)
return NULL;
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 1e08047c..6c7e7a5b 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -1,8 +1,6 @@
#ifndef fooutf8hfoo
#define fooutf8hfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
***/
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \file
* UTF8 Validation functions
diff --git a/src/pulse/util.c b/src/pulse/util.c
index a5f0e8e2..c0911b51 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,6 +53,7 @@
#include <sys/prctl.h>
#endif
+#include <pulse/xmalloc.h>
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
@@ -64,7 +63,7 @@
#include "util.h"
char *pa_get_user_name(char *s, size_t l) {
- char *p;
+ const char *p;
char buf[1024];
#ifdef HAVE_PWD_H
@@ -74,7 +73,10 @@ char *pa_get_user_name(char *s, size_t l) {
pa_assert(s);
pa_assert(l > 0);
- if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
+ if (!(p = (getuid() == 0 ? "root" : NULL)) &&
+ !(p = getenv("USER")) &&
+ !(p = getenv("LOGNAME")) &&
+ !(p = getenv("USERNAME"))) {
#ifdef HAVE_PWD_H
#ifdef HAVE_GETPWUID_R
@@ -110,12 +112,12 @@ char *pa_get_host_name(char *s, size_t l) {
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;
}
@@ -172,13 +174,13 @@ char *pa_get_binary_name(char *s, size_t l) {
#ifdef __linux__
{
- int i;
- char path[PATH_MAX];
+ char *rp;
/* This works on Linux only */
- if ((i = readlink("/proc/self/exe", path, sizeof(path)-1)) >= 0) {
- path[i] = 0;
- return pa_strlcpy(s, pa_path_get_filename(path), l);
+ if ((rp = pa_readlink("/proc/self/exe"))) {
+ pa_strlcpy(s, pa_path_get_filename(rp), l);
+ pa_xfree(rp);
+ return s;
}
}
@@ -224,7 +226,7 @@ char *pa_get_fqdn(char *s, size_t l) {
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 764678e5..cf06d4fd 100644
--- a/src/pulse/util.h
+++ b/src/pulse/util.h
@@ -1,8 +1,6 @@
#ifndef fooutilhfoo
#define fooutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,6 +26,7 @@
#include <stddef.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \file
* Assorted utility functions */
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
index 20c7a9c0..e6226c44 100644
--- a/src/pulse/version.h.in
+++ b/src/pulse/version.h.in
@@ -1,24 +1,22 @@
#ifndef fooversionhfoo /*-*-C-*-*/
#define fooversionhfoo
-/* $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
@@ -39,7 +37,8 @@ a macro and not a function, so it is impossible to get the pointer of
it. */
#define pa_get_headers_version() ("@PACKAGE_VERSION@")
-/** Return the version of the library the current application is linked to. */
+/** Return the version of the library the current application is
+ * linked to. */
const char* pa_get_library_version(void);
/** The current API version. Version 6 relates to Polypaudio
@@ -47,8 +46,8 @@ const char* pa_get_library_version(void);
* PA_API_VERSION undefined. */
#define PA_API_VERSION @PA_API_VERSION@
-/** The current protocol version. Version 8 relates to Polypaudio 0.8/PulseAudio 0.9.
- * \since 0.8 */
+/** The current protocol version. Version 8 relates to Polypaudio
+ * 0.8/PulseAudio 0.9. */
#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
PA_C_DECL_END
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 3688b847..70d6f86a 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -80,10 +78,10 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
}
-#define USER_DECIBEL_RANGE 30
+#define USER_DECIBEL_RANGE 60
pa_volume_t pa_sw_volume_from_dB(double dB) {
- if (dB <= -USER_DECIBEL_RANGE)
+ if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)
return PA_VOLUME_MUTED;
return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 22e5b8a4..3befb1da 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -1,8 +1,6 @@
#ifndef foovolumehfoo
#define foovolumehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,7 +24,9 @@
***/
#include <inttypes.h>
+
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \page volume Volume Control
@@ -101,10 +101,10 @@ PA_C_DECL_BEGIN
typedef uint32_t pa_volume_t;
/** Normal volume (100%) */
-#define PA_VOLUME_NORM (0x10000)
+#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
/** Muted volume (0%) */
-#define PA_VOLUME_MUTED (0)
+#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
@@ -149,25 +149,25 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE
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_GCC_PURE;
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
-/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */
+/** Convert a decibel value to a volume. This is only valid for software volumes! */
pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
-/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */
+/** Convert a volume to a decibel value. This is only valid for software volumes! */
double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
-/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */
+/** Convert a linear factor to a volume. This is only valid for software volumes! */
pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
-/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */
+/** Convert a volume to a linear factor. This is only valid for software volumes! */
double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
#ifdef INFINITY
-#define PA_DECIBEL_MININFTY (-INFINITY)
+#define PA_DECIBEL_MININFTY ((double) -INFINITY)
#else
-/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
-#define PA_DECIBEL_MININFTY (-200)
+/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */
+#define PA_DECIBEL_MININFTY ((double) -200.0)
#endif
PA_C_DECL_END
diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c
index 5348dda4..90237013 100644
--- a/src/pulse/xmalloc.c
+++ b/src/pulse/xmalloc.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,10 @@
#include <signal.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "xmalloc.h"
@@ -123,8 +122,12 @@ char *pa_xstrndup(const char *s, size_t l) {
}
void pa_xfree(void *p) {
+ int saved_errno;
+
if (!p)
return;
+ saved_errno = errno;
free(p);
+ errno = saved_errno;
}
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
index 62a450dc..c453138b 100644
--- a/src/pulse/xmalloc.h
+++ b/src/pulse/xmalloc.h
@@ -1,8 +1,6 @@
#ifndef foomemoryhfoo
#define foomemoryhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index 96b43a71..5c7af2a8 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -136,7 +134,7 @@ void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const vo
/* 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_asyncq_post(a->asyncq, i);
pa_mutex_unlock(a->mutex);
}
@@ -163,7 +161,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
/* 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_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
pa_mutex_unlock(a->mutex);
pa_semaphore_wait(i.semaphore);
@@ -174,7 +172,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
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) {
+int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_assert(!a->current);
@@ -276,22 +274,40 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
return 1;
}
-int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_read_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- return pa_asyncq_get_fd(a->asyncq);
+ return pa_asyncq_write_fd(a->asyncq);
}
-int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- return pa_asyncq_before_poll(a->asyncq);
+ pa_asyncq_write_before_poll(a->asyncq);
}
-void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- pa_asyncq_after_poll(a->asyncq);
+ pa_asyncq_write_after_poll(a->asyncq);
}
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
index 5d3867ba..1f38207a 100644
--- a/src/pulsecore/asyncmsgq.h
+++ b/src/pulsecore/asyncmsgq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncmsgqhfoo
#define foopulseasyncmsgqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,20 +54,26 @@ 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_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t 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);
+/* For the reading side */
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
+
+/* For the write side */
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index 22381248..03e9f0df 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -33,14 +31,16 @@
#include <pulsecore/thread.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
#include <pulse/xmalloc.h>
#include "asyncq.h"
#include "fdsem.h"
-#define ASYNCQ_SIZE 128
+#define ASYNCQ_SIZE 256
-/* For debugging purposes we can define _Y to put and extra thread
+/* For debugging purposes we can define _Y to put an extra thread
* yield between each operation. */
/* #define PROFILE */
@@ -51,18 +51,25 @@
#define _Y do { } while(0)
#endif
+struct localq {
+ void *data;
+ PA_LLIST_FIELDS(struct localq);
+};
+
struct pa_asyncq {
unsigned size;
unsigned read_idx;
unsigned write_idx;
pa_fdsem *read_fdsem, *write_fdsem;
+
+ PA_LLIST_HEAD(struct localq, localq);
+ struct localq *last_localq;
+ pa_bool_t waiting_for_post;
};
-#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
-static int is_power_of_two(unsigned size) {
- return !(size & (size - 1));
-}
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
static int reduce(pa_asyncq *l, int value) {
return value & (unsigned) (l->size - 1);
@@ -74,12 +81,16 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
if (!size)
size = ASYNCQ_SIZE;
- pa_assert(is_power_of_two(size));
+ pa_assert(pa_is_power_of_two(size));
l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
l->size = size;
+ PA_LLIST_HEAD_INIT(struct localq, l->localq);
+ l->last_localq = NULL;
+ l->waiting_for_post = FALSE;
+
if (!(l->read_fdsem = pa_fdsem_new())) {
pa_xfree(l);
return NULL;
@@ -95,6 +106,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
}
void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+ struct localq *q;
pa_assert(l);
if (free_cb) {
@@ -104,12 +116,22 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
free_cb(p);
}
+ while ((q = l->localq)) {
+ if (free_cb)
+ free_cb(q->data);
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
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) {
+static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
int idx;
pa_atomic_ptr_t *cells;
@@ -127,7 +149,7 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return -1;
/* pa_log("sleeping on push"); */
-
+
do {
pa_fdsem_wait(l->read_fdsem);
} while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
@@ -141,13 +163,69 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return 0;
}
-void* pa_asyncq_pop(pa_asyncq*l, int wait) {
+static pa_bool_t flush_postq(pa_asyncq *l) {
+ struct localq *q;
+
+ pa_assert(l);
+
+ while ((q = l->last_localq)) {
+
+ if (push(l, q->data, FALSE) < 0)
+ return FALSE;
+
+ l->last_localq = q->prev;
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
+ return TRUE;
+}
+
+int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
+ pa_assert(l);
+
+ if (!flush_postq(l))
+ return -1;
+
+ return push(l, p, wait);
+}
+
+void pa_asyncq_post(pa_asyncq*l, void *p) {
+ struct localq *q;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ if (pa_asyncq_push(l, p, FALSE) >= 0)
+ return;
+
+ /* OK, we couldn't push anything in the queue. So let's queue it
+ * locally and push it later */
+
+ pa_log("q overrun, queuing locally");
+
+ if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
+ q = pa_xnew(struct localq, 1);
+
+ q->data = p;
+ PA_LLIST_PREPEND(struct localq, l->localq, q);
+
+ if (!l->last_localq)
+ l->last_localq = q;
+
+ return;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
int idx;
void *ret;
pa_atomic_ptr_t *cells;
pa_assert(l);
-
+
cells = PA_ASYNCQ_CELLS(l);
_Y;
@@ -159,7 +237,7 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
return NULL;
/* pa_log("sleeping on pop"); */
-
+
do {
pa_fdsem_wait(l->write_fdsem);
} while (!(ret = pa_atomic_ptr_load(&cells[idx])));
@@ -174,17 +252,17 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
l->read_idx++;
pa_fdsem_post(l->read_fdsem);
-
+
return ret;
}
-int pa_asyncq_get_fd(pa_asyncq *q) {
+int pa_asyncq_read_fd(pa_asyncq *q) {
pa_assert(q);
return pa_fdsem_get(q->write_fdsem);
}
-int pa_asyncq_before_poll(pa_asyncq *l) {
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
int idx;
pa_atomic_ptr_t *cells;
@@ -206,8 +284,38 @@ int pa_asyncq_before_poll(pa_asyncq *l) {
return 0;
}
-void pa_asyncq_after_poll(pa_asyncq *l) {
+void pa_asyncq_read_after_poll(pa_asyncq *l) {
pa_assert(l);
pa_fdsem_after_poll(l->write_fdsem);
}
+
+int pa_asyncq_write_fd(pa_asyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->read_fdsem);
+}
+
+void pa_asyncq_write_before_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ for (;;) {
+
+ if (flush_postq(l))
+ break;
+
+ if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
+ l->waiting_for_post = TRUE;
+ break;
+ }
+ }
+}
+
+void pa_asyncq_write_after_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ if (l->waiting_for_post) {
+ pa_fdsem_after_poll(l->read_fdsem);
+ l->waiting_for_post = FALSE;
+ }
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
index 53d45866..e6847ab8 100644
--- a/src/pulsecore/asyncq.h
+++ b/src/pulsecore/asyncq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncqhfoo
#define foopulseasyncqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
#include <sys/types.h>
#include <pulse/def.h>
+#include <pulsecore/macro.h>
/* A simple, asynchronous, lock-free (if requested also wait-free)
* queue. Not multiple-reader/multiple-writer safe. If that is
@@ -46,11 +45,21 @@ 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);
+void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
+int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
+
+/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
+ * locally and delay until pa_asyncq_before_poll_post() */
+void pa_asyncq_post(pa_asyncq*l, void *p);
+
+/* For the reading side */
+int pa_asyncq_read_fd(pa_asyncq *q);
+int pa_asyncq_read_before_poll(pa_asyncq *a);
+void pa_asyncq_read_after_poll(pa_asyncq *a);
-int pa_asyncq_get_fd(pa_asyncq *q);
-int pa_asyncq_before_poll(pa_asyncq *a);
-void pa_asyncq_after_poll(pa_asyncq *a);
+/* For the writing side */
+int pa_asyncq_write_fd(pa_asyncq *q);
+void pa_asyncq_write_before_poll(pa_asyncq *a);
+void pa_asyncq_write_after_poll(pa_asyncq *a);
#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 25d600c0..a91c4d56 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -1,12 +1,11 @@
#ifndef foopulseatomichfoo
#define foopulseatomichfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
+ Copyright 2008 Nokia Corporation
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -28,7 +27,7 @@
* 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.
@@ -36,7 +35,7 @@
* On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
* libatomic_ops
*/
-
+#
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
@@ -128,7 +127,7 @@ static inline void pa_atomic_store(pa_atomic_t *a, int 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));
@@ -150,11 +149,11 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {
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));
-
+ : "r" (new_i), "m" (a->value), "0" (old_i));
+
return result == oldval;
}
@@ -174,14 +173,243 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *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));
+ : "r" (new_p), "m" (a->value), "0" (old_p));
return result;
}
+#elif defined(ATOMIC_ARM_INLINE_ASM)
+
+/*
+ These should only be enabled if we have ARMv6 or better.
+*/
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline void pa_memory_barrier(void) {
+#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ asm volatile ("mcr p15, 0, r0, c7, c10, 5 @ dmb");
+#endif
+}
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "add %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "sub %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+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) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_i), "r" (new_i)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+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) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_p), "r" (new_p)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+#elif defined(ATOMIC_ARM_LINUX_HELPERS)
+
+/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more
+ information about these functions. The arm kernel helper functions first
+ appeared in 2.6.16.
+ Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere
+ inline asm implementation or you have an obsolete Linux kernel.
+*/
+/* Memory barrier */
+typedef void (__kernel_dmb_t)(void);
+#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+
+static inline void pa_memory_barrier(void) {
+#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ __kernel_dmb();
+#endif
+}
+
+/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+/* This is just to get rid of all warnings */
+typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr);
+#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0)
+
+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) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val + i, &a->value));
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val - i, &a->value));
+ return old_val;
+}
+
+/* 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) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
+ } while(failed && a->value == old_i);
+ return !failed;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+ } while(failed && a->value == (unsigned long) old_p);
+ return !failed;
+}
+
#else
/* libatomic_ops based implementation */
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
index 179c16ca..a953bf7d 100644
--- a/src/pulsecore/authkey-prop.c
+++ b/src/pulsecore/authkey-prop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,7 +41,7 @@ struct authkey_data {
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
struct authkey_data *a;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(data);
@@ -54,13 +52,13 @@ int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t 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;
-
+
pa_assert(c);
pa_assert(name);
@@ -91,7 +89,7 @@ void pa_authkey_prop_ref(pa_core *c, const char *name) {
void pa_authkey_prop_unref(pa_core *c, const char *name) {
struct authkey_data *a;
-
+
pa_assert(c);
pa_assert(name);
diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h
index 247202f3..de02d2aa 100644
--- a/src/pulsecore/authkey-prop.h
+++ b/src/pulsecore/authkey-prop.h
@@ -1,8 +1,6 @@
#ifndef fooauthkeyprophfoo
#define fooauthkeyprophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index d422d6a2..f3f40f80 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,7 @@
/* 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;
-
+
pa_assert(fd >= 0);
pa_assert(ret_data);
pa_assert(length > 0);
@@ -82,13 +80,13 @@ static int load(const char *fn, void *data, size_t length) {
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
-
+
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;
@@ -161,7 +159,7 @@ 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;
@@ -181,7 +179,7 @@ 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;
-
+
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
@@ -199,7 +197,7 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
ssize_t r;
char path[PATH_MAX];
const char *p;
-
+
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h
index 18e5157d..8301db1e 100644
--- a/src/pulsecore/authkey.h
+++ b/src/pulsecore/authkey.h
@@ -1,8 +1,6 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
index 0caaa295..26c294b2 100644
--- a/src/pulsecore/autoload.c
+++ b/src/pulsecore/autoload.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -61,7 +59,7 @@ 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;
-
+
pa_core_assert_ref(c);
pa_assert(name);
@@ -91,7 +89,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(module);
@@ -112,7 +110,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
@@ -126,7 +124,7 @@ 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;
-
+
pa_assert(c);
pa_assert(idx != PA_IDXSET_INVALID);
@@ -140,7 +138,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
@@ -167,7 +165,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;
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
index 2899586c..3926351f 100644
--- a/src/pulsecore/autoload.h
+++ b/src/pulsecore/autoload.h
@@ -1,8 +1,6 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,7 +35,7 @@ typedef struct pa_autoload_entry {
uint32_t index;
char *name;
pa_namereg_type_t type; /* Type of the autoload entry */
- int in_action; /* Currently loaded */
+ int in_action; /* The module is currently being loaded */
char *module, *argument;
} pa_autoload_entry;
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index fae54810..d5f40d83 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
index 1e20ec38..7d8995bb 100644
--- a/src/pulsecore/avahi-wrap.h
+++ b/src/pulsecore/avahi-wrap.h
@@ -1,8 +1,6 @@
#ifndef fooavahiwrapperhfoo
#define fooavahiwrapperhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 2e674cc2..cbdb602a 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,6 +29,7 @@
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
+#include <ltdl.h>
#include <pulse/xmalloc.h>
@@ -58,7 +57,7 @@
struct command {
const char *name;
- int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
+ int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, pa_bool_t *fail);
const char *help;
unsigned args;
};
@@ -77,46 +76,47 @@ enum {
};
/* Prototypes for all available commands */
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-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);
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-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);
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -135,6 +135,7 @@ static const struct command commands[] = {
{ "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},
+ { "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 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},
@@ -154,10 +155,10 @@ static const struct command commands[] = {
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
- { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
- { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
- { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
- { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
+ { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
+ { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
+ { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
+ { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
@@ -181,7 +182,7 @@ static uint32_t parse_index(const char *n) {
return idx;
}
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -191,7 +192,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const struct command*command;
pa_core_assert_ref(c);
@@ -207,7 +208,7 @@ static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -221,7 +222,7 @@ static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -235,7 +236,7 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -249,7 +250,7 @@ static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -263,7 +264,7 @@ static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -277,7 +278,7 @@ static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -291,7 +292,7 @@ static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char s[256];
const pa_mempool_stat *stat;
unsigned k;
@@ -352,7 +353,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -366,14 +367,14 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
- pa_cli_command_autoload_list(c, t, buf, fail);
+/* pa_cli_command_autoload_list(c, t, buf, fail); */
return 0;
}
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
const char *name;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -392,12 +393,12 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -418,7 +419,46 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, in
return 0;
}
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *name;
+ pa_modinfo *i;
+
+ 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.\n");
+ return -1;
+ }
+
+ if ((i = pa_modinfo_get_by_name(name))) {
+
+ pa_strbuf_printf(buf, "Name: %s\n", name);
+
+ if (!i->description && !i->version && !i->author && !i->usage)
+ pa_strbuf_printf(buf, "No module information available\n");
+ else {
+ if (i->version)
+ pa_strbuf_printf(buf, "Version: %s\n", i->version);
+ if (i->description)
+ pa_strbuf_printf(buf, "Description: %s\n", i->description);
+ if (i->author)
+ pa_strbuf_printf(buf, "Author: %s\n", i->author);
+ if (i->usage)
+ pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
+ pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+ }
+
+ pa_modinfo_free(i);
+ } else
+ pa_strbuf_puts(buf, "Failed to open module.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
@@ -435,7 +475,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
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");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -454,7 +494,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
@@ -477,7 +517,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
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");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -496,7 +536,7 @@ 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, int *fail) {
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_source *source;
uint32_t volume;
@@ -513,7 +553,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
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");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -532,7 +572,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-static int pa_cli_command_sink_mute(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, pa_bool_t *fail) {
const char *n, *m;
pa_sink *sink;
int mute;
@@ -552,7 +592,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -566,7 +606,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return 0;
}
-static int pa_cli_command_source_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, pa_bool_t *fail) {
const char *n, *m;
pa_source *source;
int mute;
@@ -586,7 +626,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -600,7 +640,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-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_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
uint32_t idx;
@@ -622,11 +662,11 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
}
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");
+ pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
return -1;
}
- if (pa_atoi(v, &mute) < 0) {
+ if ((mute = pa_parse_boolean(v)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -640,7 +680,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_sink_default(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, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -657,7 +697,7 @@ 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, int *fail) {
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -674,7 +714,7 @@ 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, int *fail) {
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_client *client;
uint32_t idx;
@@ -703,7 +743,7 @@ 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, int *fail) {
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
@@ -732,7 +772,7 @@ 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, int *fail) {
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
@@ -761,7 +801,7 @@ 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, int *fail) {
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -772,13 +812,14 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
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) {
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *sink_name;
pa_sink *sink;
+ uint32_t idx;
pa_core_assert_ref(c);
pa_assert(t);
@@ -795,15 +836,17 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
+ if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
+ pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
+
return 0;
}
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -824,7 +867,7 @@ static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *n;
int r;
@@ -849,7 +892,7 @@ static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *pname;
pa_core_assert_ref(c);
@@ -870,10 +913,10 @@ static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *sink_name;
pa_sink *sink;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -893,7 +936,7 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return pa_play_file(sink, fname, NULL);
}
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *a, *b;
pa_core_assert_ref(c);
@@ -901,6 +944,8 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
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");
return -1;
@@ -911,7 +956,7 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *name;
pa_core_assert_ref(c);
@@ -919,6 +964,8 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
@@ -932,7 +979,7 @@ 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, int *fail) {
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -940,14 +987,16 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
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, int *fail) {
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -957,7 +1006,7 @@ static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf
return 0;
}
-static int pa_cli_command_vacuum(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, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -968,7 +1017,7 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, in
return 0;
}
-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_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *k;
pa_sink_input *si;
pa_sink *sink;
@@ -1004,14 +1053,14 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
return 0;
}
-static int pa_cli_command_move_source_output(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, pa_bool_t *fail) {
const char *n, *k;
pa_source_output *so;
pa_source *source;
@@ -1054,7 +1103,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_suspend_sink(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, pa_bool_t *fail) {
const char *n, *m;
pa_sink *sink;
int suspend;
@@ -1074,7 +1123,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1088,7 +1137,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_suspend_source(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, pa_bool_t *fail) {
const char *n, *m;
pa_source *source;
int suspend;
@@ -1108,7 +1157,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1122,7 +1171,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_suspend(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, pa_bool_t *fail) {
const char *m;
int suspend;
int ret;
@@ -1137,7 +1186,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1148,11 +1197,11 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
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) {
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
pa_source *source;
@@ -1201,7 +1250,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
}
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));
+ pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
+ pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
@@ -1214,7 +1264,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
}
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));
+ pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
+ pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
@@ -1261,7 +1312,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate) {
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
const char *cs;
pa_assert(c);
@@ -1293,9 +1344,9 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
if (ifstate && *ifstate == IFSTATE_FALSE)
return 0;
if (!strcmp(cs, META_FAIL))
- *fail = 1;
+ *fail = TRUE;
else if (!strcmp(cs, META_NOFAIL))
- *fail = 0;
+ *fail = FALSE;
else {
size_t l;
l = strcspn(cs, whitespace);
@@ -1315,8 +1366,35 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
} else {
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");
+ /* Search DL_SEARCH_PATH unless the filename is absolute */
+ if (filename[0] == PA_PATH_SEP_CHAR) {
+
+ *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 {
+ const char *paths, *state = NULL;
+ char *p;
+
+ if (!(paths = lt_dlgetsearchpath()))
+ return -1;
+
+ while ((p = pa_split(paths, ":", &state))) {
+ char *pathname;
+
+ pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename);
+ pa_xfree(p);
+
+ *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+ pa_xfree(pathname);
+
+ if (*ifstate == IFSTATE_TRUE)
+ break;
+ }
+ }
+
}
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
@@ -1358,20 +1436,49 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
return 0;
}
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
}
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
- char line[256];
- FILE *f = NULL;
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
+ char line[1024];
int ifstate = IFSTATE_NONE;
int ret = -1;
+ pa_bool_t _fail = TRUE;
+
+ pa_assert(c);
+ pa_assert(f);
+ pa_assert(buf);
+
+ if (!fail)
+ fail = &_fail;
+
+ while (fgets(line, sizeof(line), f)) {
+ pa_strip_nl(line);
+
+ if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+
+ return ret;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
+ FILE *f = NULL;
+ int ret = -1;
+ pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(fn);
pa_assert(buf);
+ if (!fail)
+ fail = &_fail;
+
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
if (!*fail)
@@ -1379,13 +1486,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int
goto fail;
}
- while (fgets(line, sizeof(line), f)) {
- char *e = line + strcspn(line, linebreak);
- *e = 0;
-
- if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
- goto fail;
- }
+ ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
ret = 0;
@@ -1396,14 +1497,18 @@ fail:
return ret;
}
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
const char *p;
int ifstate = IFSTATE_NONE;
+ pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(s);
pa_assert(buf);
+ if (!fail)
+ fail = &_fail;
+
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
@@ -1421,4 +1526,3 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)
return 0;
}
-
diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h
index 01bca8be..9bf35dc3 100644
--- a/src/pulsecore/cli-command.h
+++ b/src/pulsecore/cli-command.h
@@ -1,8 +1,6 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,15 +29,18 @@
* buffer *buf. If *fail is non-zero the function will return -1 when
* one or more of the executed commands failed. *fail
* may be modified by the function call. */
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
/* Execute a whole file of CLI commands */
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
/* Same as pa_cli_command_execute_line() but also take ifstate var. */
-int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate);
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate);
#endif
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 6683e697..c92fca20 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,6 +27,7 @@
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
@@ -41,6 +40,7 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "cli-text.h"
@@ -56,12 +56,12 @@ char *pa_module_list_to_string(pa_core *c) {
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
pa_strbuf_printf(s, " index: %u\n"
- "\tname: <%s>\n"
- "\targument: <%s>\n"
- "\tused: %i\n"
- "\tauto unload: %s\n",
- m->index, m->name, m->argument ? m->argument : "", m->n_used,
- m->auto_unload ? "yes" : "no");
+ "\tname: <%s>\n"
+ "\targument: <%s>\n"
+ "\tused: %i\n"
+ "\tauto unload: %s\n",
+ m->index, m->name, m->argument ? m->argument : "", m->n_used,
+ pa_yes_no(m->auto_unload));
}
return pa_strbuf_tostring_free(s);
@@ -78,10 +78,20 @@ char *pa_client_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
- pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
+ char *t;
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tdriver: <%s>\n",
+ client->index,
+ client->driver);
- if (client->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
+ if (client->module)
+ pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
+
+ t = pa_proplist_to_string(client->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -92,6 +102,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SINK_INIT] = "INIT",
[PA_SINK_RUNNING] = "RUNNING",
[PA_SINK_SUSPENDED] = "SUSPENDED",
[PA_SINK_IDLE] = "IDLE",
@@ -104,34 +115,46 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+
+ pa_sink_get_latency_range(sink, &min_latency, &max_latency);
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s\n"
+ "\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tvolume: <%s>\n"
- "\tmute: <%i>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tmonitor source: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tused by: <%u>\n"
- "\tlinked by: <%u>\n",
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+ "\tmax request: %lu KiB\n"
+ "\tmax rewind: %lu KiB\n"
+ "\tmonitor source: %u\n"
+ "\tsample spec: %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->driver,
+ sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+ sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
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),
+ pa_yes_no(pa_sink_get_mute(sink)),
+ (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_sink_get_max_request(sink) / 1024,
+ (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
@@ -139,9 +162,11 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink_linked_by(sink));
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);
+ pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
+
+ t = pa_proplist_to_string(sink->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -152,6 +177,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SOURCE_INIT] = "INIT",
[PA_SOURCE_RUNNING] = "RUNNING",
[PA_SOURCE_SUSPENDED] = "SUSPENDED",
[PA_SOURCE_IDLE] = "IDLE",
@@ -164,45 +190,56 @@ char *pa_source_list_to_string(pa_core *c) {
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], cv[PA_CVOLUME_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(source, &min_latency, &max_latency);
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s\n"
+ "\tflags: %s%s%s%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"
- "\tused by: <%u>\n"
- "\tlinked by: <%u>\n",
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+ "\tmax rewind: %lu KiB\n"
+ "\tsample spec: %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_HARDWARE ? "HARDWARE " : "",
+ source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+ source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
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_yes_no(pa_source_get_mute(source)),
+ (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
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);
+ pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->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);
+ pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
+
+ t = pa_proplist_to_string(source->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -214,6 +251,7 @@ 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[] = {
+ [PA_SOURCE_OUTPUT_INIT] = "INIT",
[PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
[PA_SOURCE_OUTPUT_CORKED] = "CORKED",
[PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
@@ -225,37 +263,55 @@ char *pa_source_output_list_to_string(pa_core *c) {
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];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: '%s'\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tsource: <%u> '%s'\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
+ "\tsource: %u <%s>\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
o->index,
- o->name,
o->driver,
o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
- (double) pa_source_output_get_latency(o),
+ (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
+ clt,
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)));
if (o->module)
- pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index);
+ pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
if (o->client)
- pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
+ if (o->direct_on_input)
+ pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
+
+ t = pa_proplist_to_string(o->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -266,6 +322,7 @@ 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[] = {
+ [PA_SINK_INPUT_INIT] = "INIT",
[PA_SINK_INPUT_RUNNING] = "RUNNING",
[PA_SINK_INPUT_DRAINED] = "DRAINED",
[PA_SINK_INPUT_CORKED] = "CORKED",
@@ -278,42 +335,58 @@ char *pa_sink_input_list_to_string(pa_core *c) {
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];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%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"
+ "\tsink: %u <%s>\n"
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
i->index,
- i->name,
i->driver,
i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
+ i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+ i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+ i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+ i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
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_yes_no(pa_sink_input_get_mute(i)),
+ (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
+ clt,
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, "\tmodule: <%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);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
+
+ t = pa_proplist_to_string(i->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -333,7 +406,7 @@ char *pa_scache_list_to_string(pa_core *c) {
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@@ -344,14 +417,14 @@ char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
" name: <%s>\n"
- "\tindex: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tlength: <%lu>\n"
- "\tduration: <%0.1fs>\n"
- "\tvolume: <%s>\n"
+ "\tindex: %u\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tlength: %lu\n"
+ "\tduration: %0.1f s\n"
+ "\tvolume: %s\n"
"\tlazy: %s\n"
- "\tfilename: %s\n",
+ "\tfilename: <%s>\n",
e->name,
e->index,
ss,
@@ -359,8 +432,12 @@ char *pa_scache_list_to_string(pa_core *c) {
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
- e->lazy ? "yes" : "no",
+ pa_yes_no(e->lazy),
e->filename ? e->filename : "n/a");
+
+ t = pa_proplist_to_string(e->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
}
@@ -381,7 +458,12 @@ char *pa_autoload_list_to_string(pa_core *c) {
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
- s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
+ s,
+ " name: <%s>\n"
+ "\ttype: %s\n"
+ "\tindex: %u\n"
+ "\tmodule_name: <%s>\n"
+ "\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index 9e5bf081..f4cb97a5 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -1,8 +1,6 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 9fff7347..b3c639f8 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -59,7 +57,8 @@ struct pa_cli {
pa_client *client;
- int fail, kill_requested, defer_kill;
+ pa_bool_t fail, kill_requested;
+ int defer_kill;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
@@ -81,19 +80,20 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
- c->client->owner = m;
+ c->client->module = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
- c->fail = c->kill_requested = c->defer_kill = 0;
+ c->fail = c->kill_requested = FALSE;
+ c->defer_kill = 0;
return c;
}
void pa_cli_free(pa_cli *c) {
pa_assert(c);
-
+
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
@@ -102,13 +102,13 @@ void pa_cli_free(pa_cli *c) {
static void client_kill(pa_client *client) {
pa_cli *c;
-
+
pa_assert(client);
pa_assert_se(c = client->userdata);
pa_log_debug("CLI client killed.");
if (c->defer_kill)
- c->kill_requested = 1;
+ c->kill_requested = TRUE;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
@@ -119,7 +119,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
-
+
pa_assert(line);
pa_assert(c);
@@ -147,7 +147,7 @@ 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) {
pa_assert(c);
-
+
c->eof_callback = cb;
c->userdata = userdata;
}
diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h
index 2b58d458..6077a8e8 100644
--- a/src/pulsecore/cli.h
+++ b/src/pulsecore/cli.h
@@ -1,8 +1,6 @@
#ifndef fooclihfoo
#define fooclihfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 99cbeb83..0ffd2330 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,26 +33,29 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "client.h"
pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_client *c;
-
+
pa_core_assert_ref(core);
c = pa_xnew(pa_client, 1);
- c->name = pa_xstrdup(name);
- c->driver = pa_xstrdup(driver);
- c->owner = NULL;
c->core = core;
+ c->proplist = pa_proplist_new();
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+ c->driver = pa_xstrdup(driver);
+ c->module = NULL;
c->kill = NULL;
c->userdata = NULL;
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, pa_strnull(name));
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
@@ -63,23 +64,26 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
}
void pa_client_free(pa_client *c) {
+ pa_core *core;
+
pa_assert(c);
pa_assert(c->core);
+ core = 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, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
- pa_xfree(c->name);
+ pa_proplist_free(c->proplist);
pa_xfree(c->driver);
pa_xfree(c);
+
+ pa_core_check_quit(core);
}
void pa_client_kill(pa_client *c) {
pa_assert(c);
-
+
if (!c->kill) {
pa_log_warn("kill() operation not implemented for client %u", c->index);
return;
@@ -91,10 +95,7 @@ void pa_client_kill(pa_client *c) {
void pa_client_set_name(pa_client *c, const char *name) {
pa_assert(c);
- 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);
-
+ pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 6d09b999..28d1fe5f 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -1,8 +1,6 @@
#ifndef foopulseclienthfoo
#define foopulseclienthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,6 +26,7 @@
typedef struct pa_client pa_client;
+#include <pulse/proplist.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -37,11 +36,12 @@ typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
-
- pa_module *owner;
- char *name, *driver;
pa_core *core;
+ pa_proplist *proplist;
+ pa_module *module;
+ char *driver;
+
void (*kill)(pa_client *c);
void *userdata;
};
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index ee43b05a..4aec45d7 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -110,7 +108,7 @@ 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;
-
+
pa_assert(filename);
pa_assert(t);
@@ -153,7 +151,7 @@ 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;
-
+
pa_assert(filename);
pa_assert(lvalue);
pa_assert(rvalue);
@@ -169,7 +167,8 @@ 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;
+ int k;
+ pa_bool_t *b = data;
pa_assert(filename);
pa_assert(lvalue);
@@ -181,7 +180,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
return -1;
}
- *b = k;
+ *b = !!k;
return 0;
}
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index b56d979e..7eb1fae2 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -1,8 +1,6 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index 64464132..3d6c2c3b 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -51,7 +49,7 @@ const char* pa_cstrerror(int errnum) {
if ((t = PA_STATIC_TLS_GET(cstrerror)))
pa_xfree(t);
-
+
#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
original = strerror_r(errnum, errbuf, sizeof(errbuf));
#elif defined(HAVE_STRERROR_R)
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
index 443c4883..b0c306c7 100644
--- a/src/pulsecore/core-error.h
+++ b/src/pulsecore/core-error.h
@@ -1,8 +1,6 @@
#ifndef foocoreerrorhfoo
#define foocoreerrorhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 1cd24aa2..75fa2ff1 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -63,12 +61,12 @@
#include "core-scache.h"
-#define UNLOAD_POLL_TIME 2
+#define UNLOAD_POLL_TIME 60
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
-
+
pa_assert(c);
pa_assert(c->mainloop == m);
pa_assert(c->scache_auto_unload_event == e);
@@ -82,19 +80,21 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
static void free_entry(pa_scache_entry *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);
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
+ if (e->proplist)
+ pa_proplist_free(e->proplist);
pa_xfree(e);
}
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(name);
@@ -103,6 +103,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
+ pa_proplist_clear(e->proplist);
pa_assert(e->core == c);
@@ -117,11 +118,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
e->name = pa_xstrdup(name);
e->core = c;
+ e->proplist = pa_proplist_new();
- if (!c->scache) {
+ if (!c->scache)
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- pa_assert(c->scache);
- }
pa_idxset_put(c->scache, e, &e->index);
@@ -129,25 +129,40 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
}
e->last_used_time = 0;
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
e->filename = NULL;
- e->lazy = 0;
+ e->lazy = FALSE;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
pa_channel_map_init(&e->channel_map);
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
+
return e;
}
-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) {
+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,
+ pa_proplist *p,
+ uint32_t *idx) {
+
pa_scache_entry *e;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
-
+ pa_channel_map tmap;
+
pa_assert(c);
pa_assert(name);
+ pa_assert(!ss || pa_sample_spec_valid(ss));
+ pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels));
+
+ if (ss && !map)
+ pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
@@ -155,9 +170,11 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
if (!(e = scache_add_item(c, name)))
return -1;
+ memset(&e->sample_spec, 0, sizeof(e->sample_spec));
+ pa_channel_map_init(&e->channel_map);
+
if (ss) {
e->sample_spec = *ss;
- pa_channel_map_init_auto(&e->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
e->volume.channels = e->sample_spec.channels;
}
@@ -169,6 +186,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
pa_memblock_ref(e->memchunk.memblock);
}
+ if (p)
+ pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
+
if (idx)
*idx = e->index;
@@ -184,6 +204,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_channel_map map;
pa_memchunk chunk;
int r;
+ pa_proplist *p;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
@@ -195,12 +216,15 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_assert(c);
pa_assert(name);
pa_assert(filename);
-
+
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
return -1;
- r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
+ p = pa_proplist_new();
+ pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+ r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
+ pa_proplist_free(p);
return r;
}
@@ -222,9 +246,11 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
if (!(e = scache_add_item(c, name)))
return -1;
- e->lazy = 1;
+ e->lazy = TRUE;
e->filename = pa_xstrdup(filename);
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
+
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
@@ -240,15 +266,14 @@ 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;
-
+
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)
- pa_assert(0);
+ pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
pa_log_debug("Removed sample \"%s\"", name);
@@ -260,7 +285,7 @@ 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;
pa_assert(e);
-
+
free_entry(e);
}
@@ -276,10 +301,10 @@ void pa_scache_free(pa_core *c) {
c->mainloop->time_free(c->scache_auto_unload_event);
}
-int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_scache_entry *e;
- char *t;
pa_cvolume r;
+ pa_proplist *merged;
pa_assert(c);
pa_assert(name);
@@ -303,17 +328,24 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
- t = pa_sprintf_malloc("sample:%s", name);
-
pa_cvolume_set(&r, e->volume.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
- if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
- pa_xfree(t);
+ merged = pa_proplist_new();
+
+ pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
+
+ pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
+
+ if (p)
+ pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
+
+ if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
+ pa_proplist_free(merged);
return -1;
}
- pa_xfree(t);
+ pa_proplist_free(merged);
if (e->lazy)
time(&e->last_used_time);
@@ -321,21 +353,21 @@ 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) {
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
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);
+ return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
}
-const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(id != PA_IDXSET_INVALID);
@@ -347,7 +379,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
@@ -357,10 +389,11 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
return e->index;
}
-uint32_t pa_scache_total_size(pa_core *c) {
+size_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
- uint32_t idx, sum = 0;
-
+ uint32_t idx;
+ size_t sum = 0;
+
pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
@@ -394,8 +427,7 @@ void pa_scache_unload_unused(pa_core *c) {
continue;
pa_memblock_unref(e->memchunk.memblock);
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
@@ -407,7 +439,7 @@ static void add_file(pa_core *c, const char *pathname) {
pa_core_assert_ref(c);
pa_assert(pathname);
-
+
e = pa_path_get_filename(pathname);
if (stat(pathname, &st) < 0) {
@@ -423,7 +455,7 @@ static void add_file(pa_core *c, const char *pathname) {
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
-
+
pa_core_assert_ref(c);
pa_assert(pathname);
@@ -458,8 +490,9 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
+
+ closedir(dir);
}
- closedir(dir);
return 0;
}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index ab7ec0ef..80e0fd04 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -1,8 +1,6 @@
#ifndef foocorescachehfoo
#define foocorescachehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,11 +27,12 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
-#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
+#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
typedef struct pa_scache_entry {
- pa_core *core;
uint32_t index;
+ pa_core *core;
+
char *name;
pa_cvolume volume;
@@ -43,25 +42,27 @@ typedef struct pa_scache_entry {
char *filename;
- int lazy;
+ pa_bool_t lazy;
time_t last_used_time;
+
+ pa_proplist *proplist;
} pa_scache_entry;
-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);
+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, pa_proplist *p, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
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);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
void pa_scache_free(pa_core *c);
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);
-uint32_t pa_scache_total_size(pa_core *c);
+size_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 06c5a4ad..6107002b 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
index 2b6863f9..2f9730d9 100644
--- a/src/pulsecore/core-subscribe.h
+++ b/src/pulsecore/core-subscribe.h
@@ -1,8 +1,6 @@
#ifndef foocoresubscribehfoo
#define foocoresubscribehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index a644b664..d259fb16 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -41,6 +39,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <dirent.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@@ -103,12 +102,6 @@
#define MSG_NOSIGNAL 0
#endif
-#ifndef OS_IS_WIN32
-#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#else
-#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
-#endif
-
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -221,7 +214,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
goto fail;
}
#else
- pa_log_warn("secure directory creation not supported on Win32.");
+ pa_log_warn("Secure directory creation not supported on Win32.");
#endif
return 0;
@@ -512,8 +505,10 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
return b;
}
-/* Make the current thread a realtime thread*/
-void pa_make_realtime(void) {
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
#ifdef _POSIX_PRIORITY_SCHEDULING
struct sched_param sp;
@@ -524,55 +519,169 @@ void pa_make_realtime(void) {
if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
- return;
+ return -1;
+ }
+
+ if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) {
+ pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority);
+ return 0;
}
- sp.sched_priority = 1;
+ sp.sched_priority = rtprio;
if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+
+ while (sp.sched_priority > 1) {
+ sp.sched_priority --;
+
+ if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) {
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio);
+ return 0;
+ }
+ }
+
pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
- return;
+ return -1;
+ }
+
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+ if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
}
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
- pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread.");
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
#endif
+ return FALSE;
}
-#define NICE_LEVEL (-11)
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_high_priority(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
-/* Raise the priority of the current process as much as possible and
-sensible: set the nice level to -15.*/
-void pa_raise_priority(void) {
+ if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+ if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
+
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
+/* Raise the priority of the current process as much as possible that
+ * is <= the specified nice level..*/
+int pa_raise_priority(int nice_level) {
#ifdef HAVE_SYS_RESOURCE_H
- if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
+ if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) {
+ int n;
+
+ for (n = nice_level+1; n < 0; n++) {
+
+ if (setpriority(PRIO_PROCESS, 0, n) == 0) {
+ pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
+ return 0;
+ }
+ }
+
pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
- else
- pa_log_info("Successfully gained nice level %i.", NICE_LEVEL);
+ return -1;
+ }
+
+ pa_log_info("Successfully gained nice level %i.", nice_level);
#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.");
+ if (nice_level < 0) {
+ if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
+ pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+ return .-1;
+ } else
+ pa_log_info("Successfully gained high priority class.");
+ }
#endif
+
+ return 0;
}
/* Reset the priority to normal, inverting the changes made by
- * pa_raise_priority() */
+ * pa_raise_priority() and pa_make_realtime()*/
void pa_reset_priority(void) {
-#ifdef OS_IS_WIN32
- SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
-#endif
-
#ifdef HAVE_SYS_RESOURCE_H
+ struct sched_param sp;
+
setpriority(PRIO_PROCESS, 0, 0);
+
+ memset(&sp, 0, sizeof(sp));
+ pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0);
+#endif
+
+#ifdef OS_IS_WIN32
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
#endif
}
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
+ pa_assert(v);
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
@@ -1033,12 +1142,13 @@ fail:
/* Unlock a temporary lcok file */
int pa_unlock_lockfile(const char *fn, int fd) {
int r = 0;
- pa_assert(fn);
pa_assert(fd >= 0);
- if (unlink(fn) < 0) {
- pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
- r = -1;
+ if (fn) {
+ if (unlink(fn) < 0) {
+ pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
+ r = -1;
+ }
}
if (pa_lock_fd(fd, 0) < 0) {
@@ -1054,16 +1164,58 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r;
}
+static char *get_dir(mode_t m, const char *env_name) {
+ const char *e;
+ char *d;
+
+ if ((e = getenv(env_name)))
+ d = pa_xstrdup(e);
+ else {
+ char h[PATH_MAX];
+ struct stat st;
+
+ if (!pa_get_home_dir(h, sizeof(h))) {
+ pa_log_error("Failed to get home directory.");
+ return NULL;
+ }
+
+ if (stat(h, &st) < 0) {
+ pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
+ return NULL;
+ }
+
+ if (st.st_uid != getuid()) {
+ pa_log_error("Home directory %s not ours.", d);
+ return NULL;
+ }
+
+ d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+ }
+
+ if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ return NULL;
+ }
+
+ return d;
+}
+
+char *pa_get_runtime_dir(void) {
+ return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH");
+}
+
+char *pa_get_state_dir(void) {
+ return get_dir(0700, "PULSE_STATE_PATH");
+}
+
/* Try to open a configuration file. If "env" is specified, open the
* value of the specified environment variable. Otherwise look for a
* file "local" in the home directory or a file "global" in global
* file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
const char *fn;
- char h[PATH_MAX];
-
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
@@ -1072,68 +1224,152 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
#endif
if (env && (fn = getenv(env))) {
+ FILE *f;
+
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(fn);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
- return fopen(fn, mode);
+ return f;
+ }
+
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ return NULL;
}
if (local) {
const char *e;
- char *lfn = NULL;
+ char *lfn;
+ char h[PATH_MAX];
+ FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
- fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
- fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
-
- if (lfn) {
- FILE *f;
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
- return NULL;
- fn = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- f = fopen(fn, mode);
- if (f != NULL) {
- if (result)
- *result = pa_xstrdup(fn);
- pa_xfree(lfn);
- return f;
- }
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
- if (errno != ENOENT)
- pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return f;
+ }
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
+ return NULL;
}
+
+ pa_xfree(lfn);
}
- if (!global) {
- if (result)
- *result = NULL;
+ if (global) {
+ FILE *f;
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if ((f = fopen(global, "r"))) {
+
+ if (result)
+ *result = pa_xstrdup(global);
+
+ return f;
+ }
+ } else
errno = ENOENT;
+
+ return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+ const char *fn;
+#ifdef OS_IS_WIN32
+ char buf[PATH_MAX];
+
+ if (!getenv(PULSE_ROOTENV))
+ pa_set_root(NULL);
+#endif
+
+ if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(fn);
+
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
+ if (local) {
+ const char *e;
+ char *lfn;
+ char h[PATH_MAX];
+
+ if ((e = getenv("PULSE_CONFIG_PATH")))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+ else if (pa_get_home_dir(h, sizeof(h)))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
- return NULL;
- global = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(global);
+ if (access(fn, R_OK) == 0) {
+ char *r = pa_xstrdup(fn);
+ pa_xfree(lfn);
+ return r;
+ }
+
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
+ }
+
+ pa_xfree(lfn);
+ }
+
+ if (global) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
- return fopen(global, mode);
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(global);
+ } else
+ errno = ENOENT;
+
+ return NULL;
}
/* Format the specified data as a hexademical string */
@@ -1200,7 +1436,7 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
}
/* Returns nonzero when *s starts with *pfx */
-int pa_startswith(const char *s, const char *pfx) {
+pa_bool_t pa_startswith(const char *s, const char *pfx) {
size_t l;
pa_assert(s);
@@ -1212,7 +1448,7 @@ int pa_startswith(const char *s, const char *pfx) {
}
/* Returns nonzero when *s ends with *sfx */
-int pa_endswith(const char *s, const char *sfx) {
+pa_bool_t pa_endswith(const char *s, const char *sfx) {
size_t l1, l2;
pa_assert(s);
@@ -1224,45 +1460,62 @@ int pa_endswith(const char *s, const char *sfx) {
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
}
-/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
- const char *e;
+pa_bool_t pa_is_path_absolute(const char *fn) {
+ pa_assert(fn);
#ifndef OS_IS_WIN32
- if (fn && *fn == '/')
+ return *fn == '/';
#else
- if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
+ return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
#endif
- return pa_strlcpy(s, fn, l);
+}
- if ((e = getenv("PULSE_RUNTIME_PATH"))) {
+char *pa_make_path_absolute(const char *p) {
+ char *r;
+ char *cwd;
- if (fn)
- pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
- else
- pa_snprintf(s, l, "%s", e);
+ pa_assert(p);
- } else {
- char u[256];
+ if (pa_is_path_absolute(p))
+ return pa_xstrdup(p);
- if (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
- pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
- }
+ if (!(cwd = pa_getcwd()))
+ return pa_xstrdup(p);
+
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+ pa_xfree(cwd);
+ return r;
+}
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+static char *get_path(const char *fn, pa_bool_t rt) {
+ char *rtp;
-#ifdef OS_IS_WIN32
- {
- char buf[l];
- strcpy(buf, s);
- ExpandEnvironmentStrings(buf, s, l);
- }
-#endif
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
- return s;
+ rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+ if (!rtp)
+ return NULL;
+
+ if (fn) {
+ char *r;
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+ pa_xfree(rtp);
+ return r;
+ } else
+ return rtp;
+}
+
+char *pa_runtime_path(const char *fn) {
+ return get_path(fn, 1);
+}
+
+char *pa_state_path(const char *fn) {
+ return get_path(fn, 0);
}
/* Convert the string s to a signed integer in *ret_i */
@@ -1317,13 +1570,13 @@ static void c_locale_destroy(void) {
}
#endif
-int pa_atof(const char *s, float *ret_f) {
+int pa_atod(const char *s, double *ret_d) {
char *x = NULL;
- float f;
+ double f;
int r = 0;
pa_assert(s);
- pa_assert(ret_f);
+ pa_assert(ret_d);
/* This should be locale independent */
@@ -1338,22 +1591,18 @@ int pa_atof(const char *s, float *ret_f) {
if (c_locale) {
errno = 0;
- f = strtof_l(s, &x, c_locale);
+ f = strtod_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;
+ *ret_d = f;
return r;
}
@@ -1368,12 +1617,28 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) {
pa_assert(format);
va_start(ap, format);
- ret = vsnprintf(str, size, format, ap);
+ ret = pa_vsnprintf(str, size, format, ap);
va_end(ap);
+ return ret;
+}
+
+/* Same as vsnprintf, but guarantees NUL-termination on every platform */
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ int ret;
+
+ pa_assert(str);
+ pa_assert(size > 0);
+ pa_assert(format);
+
+ ret = vsnprintf(str, size, format, ap);
+
str[size-1] = 0;
- return ret;
+ if (ret < 0)
+ ret = strlen(str);
+
+ return PA_MIN((int) size-1, ret);
}
/* Truncate the specified string, but guarantee that the string
@@ -1409,23 +1674,6 @@ char *pa_getcwd(void) {
}
}
-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;
@@ -1507,3 +1755,291 @@ void pa_close_pipe(int fds[2]) {
fds[0] = fds[1] = -1;
}
+
+char *pa_readlink(const char *p) {
+ size_t l = 100;
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ c = pa_xnew(char, l);
+
+ if ((n = readlink(p, c, l-1)) < 0) {
+ pa_xfree(c);
+ return NULL;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ return c;
+ }
+
+ pa_xfree(c);
+ l *= 2;
+ }
+}
+
+int pa_close_all(int except_fd, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except_fd);
+
+ if (except_fd >= 0)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except_fd);
+
+ i = 0;
+ if (except_fd >= 0) {
+ int fd;
+ p[i++] = except_fd;
+
+ while ((fd = va_arg(ap, int)) >= 0)
+ p[i++] = fd;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_close_allv(p);
+ free(p);
+
+ return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+ struct rlimit rl;
+ int fd;
+ int saved_errno;
+
+#ifdef __linux__
+
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ pa_bool_t found;
+ long l;
+ char *e = NULL;
+ int i;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = (int) l;
+
+ if ((long) fd != l) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ found = FALSE;
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd) {
+ found = TRUE;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ if (pa_close(fd) < 0) {
+ saved_errno = errno;
+ closedir(d);
+ errno = saved_errno;
+
+ return -1;
+ }
+ }
+
+ closedir(d);
+ return 0;
+ }
+
+#endif
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return -1;
+
+ for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+ int i;
+
+ if (fd <= 3)
+ continue;
+
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd)
+ continue;
+
+ if (close(fd) < 0 && errno != EBADF)
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ int sig;
+ p[i++] = except;
+
+ while ((sig = va_arg(ap, int)) >= 0)
+ p[i++] = sig;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_unblock_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+ int i;
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -1;
+
+ for (i = 0; except[i] > 0; i++)
+ if (sigaddset(&ss, except[i]) < 0)
+ return -1;
+
+ return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ p[i++] = except;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_reset_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ pa_bool_t reset = TRUE;
+
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ reset = FALSE;
+ break;
+
+ default: {
+ int i;
+
+ for (i = 0; except[i] > 0; i++) {
+ if (sig == except[i]) {
+ reset = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (reset) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ putenv(pa_sprintf_malloc("%s=%s", key, value));
+}
+
+pa_bool_t pa_in_system_mode(void) {
+ const char *e;
+
+ if (!(e = getenv("PULSE_SYSTEM")))
+ return FALSE;
+
+ return !!atoi(e);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 44c7574e..2ed81fc5 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -1,8 +1,6 @@
#ifndef foocoreutilhfoo
#define foocoreutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,10 +28,27 @@
#include <stdarg.h>
#include <stdio.h>
-#include <pulsecore/gccmacro.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
struct timeval;
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
void pa_make_fd_nonblock(int fd);
void pa_make_fd_cloexec(int fd);
@@ -56,12 +71,27 @@ 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);
+int pa_make_realtime(int rtprio);
+int pa_raise_priority(int nice_level);
void pa_reset_priority(void);
+pa_bool_t pa_can_realtime(void);
+pa_bool_t pa_can_high_priority(void);
+
int pa_parse_boolean(const char *s) PA_GCC_PURE;
+static inline const char *pa_yes_no(pa_bool_t b) {
+ return b ? "yes" : "no";
+}
+
+static inline const char *pa_strnull(const char *x) {
+ return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+ return x ? x : "";
+}
+
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
@@ -79,26 +109,32 @@ int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
-
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) PA_GCC_PURE;
-int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+char *pa_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn);
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_atod(const char *s, double *ret_d);
int pa_snprintf(char *str, size_t size, const char *format, ...);
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
char *pa_truncate_utf8(char *c, size_t l);
char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
void *pa_will_need(const void *p, size_t l);
@@ -111,7 +147,7 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
if (pa_is_power_of_two(n))
return n;
-
+
while (j) {
j = j >> 1;
n = n | j;
@@ -120,6 +156,32 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
return n + 1;
}
+static inline unsigned pa_ulog2(unsigned n) {
+ unsigned r = 0;
+
+ while (n) {
+ r++;
+ n = n >> 1;
+ }
+
+ return r;
+}
+
void pa_close_pipe(int fds[2]);
+char *pa_readlink(const char *p);
+
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
+
+pa_bool_t pa_in_system_mode(void);
+
+#define pa_streq(a,b) (!strcmp((a),(b)))
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index c5386522..b2638b10 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -72,7 +70,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_core* c;
pa_mempool *pool;
int j;
-
+
pa_assert(m);
if (shared) {
@@ -107,7 +105,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->scache = NULL;
c->autoload_idxset = NULL;
c->autoload_hashmap = NULL;
- c->running_as_daemon = 0;
+ c->running_as_daemon = FALSE;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
@@ -125,6 +123,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->subscription_event_last = NULL;
c->mempool = pool;
+ pa_silence_cache_init(&c->silence_cache);
c->quit_event = NULL;
@@ -132,12 +131,12 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->module_idle_time = 20;
c->scache_idle_time = 20;
- c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE;
-
- c->is_system_instance = 0;
- c->disallow_module_loading = 0;
- c->high_priority = 0;
+ c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+ c->disallow_module_loading = FALSE;
+ c->realtime_scheduling = FALSE;
+ c->realtime_priority = 5;
+ c->disable_remixing = FALSE;
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
pa_hook_init(&c->hooks[j], c);
@@ -187,6 +186,7 @@ static void core_free(pa_object *o) {
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
+ pa_silence_cache_done(&c->silence_cache);
pa_mempool_free(c->mempool);
pa_property_cleanup(c);
@@ -207,14 +207,18 @@ static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED con
void pa_core_check_quit(pa_core *c) {
pa_assert(c);
- if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
+ if (!c->quit_event &&
+ c->exit_idle_time >= 0 &&
+ pa_idxset_size(c->clients) == 0) {
+
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec+= c->exit_idle_time;
+
c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+
} else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->quit_event);
c->quit_event = NULL;
}
}
-
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index dfa80f8d..d9ed46f6 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -1,8 +1,6 @@
#ifndef foocorehfoo
#define foocorehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulsecore/llist.h>
#include <pulsecore/hook-list.h>
#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/sample-util.h>
typedef struct pa_core pa_core;
@@ -43,32 +42,38 @@ typedef struct pa_core pa_core;
#include <pulsecore/msgobject.h>
typedef enum pa_core_hook {
- PA_CORE_HOOK_SINK_NEW_POST,
+ PA_CORE_HOOK_SINK_NEW,
+ PA_CORE_HOOK_SINK_FIXATE,
+ PA_CORE_HOOK_SINK_PUT,
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_SINK_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_NEW,
+ PA_CORE_HOOK_SOURCE_FIXATE,
+ PA_CORE_HOOK_SOURCE_PUT,
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_SOURCE_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_NEW,
+ PA_CORE_HOOK_SINK_INPUT_FIXATE,
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_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+ PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
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_SOURCE_OUTPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_MAX
} pa_core_hook_t;
@@ -106,6 +111,7 @@ struct pa_core {
pa_subscription_event *subscription_event_last;
pa_mempool *mempool;
+ pa_silence_cache silence_cache;
int exit_idle_time, module_idle_time, scache_idle_time;
@@ -113,10 +119,11 @@ struct pa_core {
pa_time_event *scache_auto_unload_event;
- int disallow_module_loading, running_as_daemon;
+ pa_bool_t disallow_module_loading, running_as_daemon;
pa_resample_method_t resample_method;
- int is_system_instance;
- int high_priority;
+ pa_bool_t realtime_scheduling;
+ int realtime_priority;
+ pa_bool_t disable_remixing;
/* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX];
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index 51dfc33d..c15c469b 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -1,8 +1,6 @@
#ifndef foocredshfoo
#define foocredshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
index 52cbf9e2..269de604 100644
--- a/src/pulsecore/dllmain.c
+++ b/src/pulsecore/dllmain.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
index f5e73ed0..a1fcd8a3 100644
--- a/src/pulsecore/dynarray.c
+++ b/src/pulsecore/dynarray.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -88,7 +86,7 @@ unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
unsigned i;
pa_assert(a);
-
+
i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
@@ -96,7 +94,7 @@ unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
pa_assert(a);
-
+
if (i >= a->n_entries)
return NULL;
@@ -106,6 +104,6 @@ void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
unsigned pa_dynarray_size(pa_dynarray*a) {
pa_assert(a);
-
+
return a->n_entries;
}
diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
index 0f222e10..82b42082 100644
--- a/src/pulsecore/dynarray.h
+++ b/src/pulsecore/dynarray.h
@@ -1,8 +1,6 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 8f7cfade..26336918 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -1,8 +1,6 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,12 +45,20 @@
#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
+static inline float PA_FLOAT32_SWAP(float x) {
+ uint32_t i = *(uint32_t*) &x;
+ i = PA_UINT32_SWAP(i);
+ return *(float*) &i;
+}
+
#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 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)
+#define PA_MAYBE_FLOAT32_SWAP(c,x) ((c) ? PA_FLOAT32_SWAP(x) : x)
+
#ifdef WORDS_BIGENDIAN
#define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
#define PA_INT16_FROM_BE(x) ((int16_t)(x))
@@ -63,14 +69,23 @@
#define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
#define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_BE(x) ((uint16_t)(x))
+
#define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
#define PA_INT32_FROM_BE(x) ((int32_t)(x))
+ #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_BE(x) ((int32_t)(x))
+
#define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
#define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_BE(x) ((uint32_t)(x))
+
+ #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
+ #define PA_FLOAT32_TO_BE(x) ((float) (x))
#else
#define PA_INT16_FROM_LE(x) ((int16_t)(x))
#define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
@@ -81,14 +96,23 @@
#define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
#define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
+
#define PA_INT32_FROM_LE(x) ((int32_t)(x))
#define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_LE(x) ((int32_t)(x))
+ #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
+
#define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
#define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_LE(x) ((uint32_t)(x))
#define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_FLOAT32_TO_LE(x) ((float) (x))
+ #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
#endif
#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
new file mode 100644
index 00000000..e2691611
--- /dev/null
+++ b/src/pulsecore/envelope.c
@@ -0,0 +1,781 @@
+/***
+ 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/endianmacros.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/g711.h>
+
+#include "envelope.h"
+
+/*
+ Envelope subsystem for applying linear interpolated volume
+ envelopes on audio data. If multiple enevelopes shall be applied
+ at the same time, the "minimum" envelope is determined and
+ applied.
+
+ Envelopes are defined in a statically allocated constant structure
+ pa_envelope_def. It may be activated using pa_envelope_add(). And
+ already active envelope may be replaced with pa_envelope_replace()
+ and removed with pa_envelope_remove().The combined "minimum"
+ envelope can be applied to audio data with pa_envelope_apply().
+
+ _apply() on one hand and _add()/_replace()/_remove() on the other
+ can be executed in seperate threads, in which case no locking is
+ used.
+*/
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct pa_envelope_item {
+ PA_LLIST_FIELDS(pa_envelope_item);
+ const pa_envelope_def *def;
+ pa_usec_t start_x;
+ union {
+ int32_t i;
+ float f;
+ } start_y;
+ unsigned j;
+};
+
+enum envelope_state {
+ STATE_VALID0,
+ STATE_VALID1,
+ STATE_READ0,
+ STATE_READ1,
+ STATE_WAIT0,
+ STATE_WAIT1,
+ STATE_WRITE0,
+ STATE_WRITE1
+};
+
+struct pa_envelope {
+ pa_sample_spec sample_spec;
+
+ PA_LLIST_HEAD(pa_envelope_item, items);
+
+ pa_atomic_t state;
+
+ size_t x;
+
+ struct {
+ unsigned n_points, n_allocated, n_current;
+
+ size_t *x;
+ union {
+ int32_t *i;
+ float *f;
+ } y;
+
+ size_t cached_dx;
+ int32_t cached_dy_i;
+ float cached_dy_dx;
+ pa_bool_t cached_valid;
+ } points[2];
+
+ pa_bool_t is_float;
+
+ pa_semaphore *semaphore;
+};
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss) {
+ pa_envelope *e;
+ pa_assert(ss);
+
+ e = pa_xnew(pa_envelope, 1);
+
+ e->sample_spec = *ss;
+ PA_LLIST_HEAD_INIT(pa_envelope_item, e->items);
+
+ e->x = 0;
+
+ e->points[0].n_points = e->points[1].n_points = 0;
+ e->points[0].n_allocated = e->points[1].n_allocated = 0;
+ e->points[0].n_current = e->points[1].n_current = 0;
+ e->points[0].x = e->points[1].x = NULL;
+ e->points[0].y.i = e->points[1].y.i = NULL;
+ e->points[0].cached_valid = e->points[1].cached_valid = FALSE;
+
+ pa_atomic_store(&e->state, STATE_VALID0);
+
+ e->is_float =
+ ss->format == PA_SAMPLE_FLOAT32LE ||
+ ss->format == PA_SAMPLE_FLOAT32BE;
+
+ e->semaphore = pa_semaphore_new(0);
+
+ return e;
+}
+
+void pa_envelope_free(pa_envelope *e) {
+ pa_assert(e);
+
+ while (e->items)
+ pa_envelope_remove(e, e->items);
+
+ pa_xfree(e->points[0].x);
+ pa_xfree(e->points[1].x);
+ pa_xfree(e->points[0].y.i);
+ pa_xfree(e->points[1].y.i);
+
+ pa_semaphore_free(e->semaphore);
+
+ pa_xfree(e);
+}
+
+static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) {
+ return (int32_t) (_y1 + (x3 - x1) * (float) (y2 - _y1) / (float) (x2 - x1));
+}
+
+static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) {
+ return _y1 + (x3 - x1) * (y2 - _y1) / (x2 - x1);
+}
+
+static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.i;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_int(0, i->start_y.i,
+ i->def->points_x[0], i->def->points_y.i[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.i[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.i[i->j], x);
+}
+
+static float item_get_float(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.f;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_float(0, i->start_y.f,
+ i->def->points_x[0], i->def->points_y.f[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.f[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.f[i->j], x);
+}
+
+static void envelope_begin_write(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t wait_sem;
+
+ pa_assert(e);
+ pa_assert(v);
+
+ for (;;) {
+ do {
+ wait_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ *v = 1;
+ new_state = STATE_WRITE0;
+ break;
+ case STATE_VALID1:
+ *v = 0;
+ new_state = STATE_WRITE1;
+ break;
+ case STATE_READ0:
+ new_state = STATE_WAIT0;
+ wait_sem = TRUE;
+ break;
+ case STATE_READ1:
+ new_state = STATE_WAIT1;
+ wait_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (!wait_sem)
+ break;
+
+ pa_semaphore_wait(e->semaphore);
+ }
+}
+
+static pa_bool_t envelope_commit_write(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+
+ pa_assert(e);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_WRITE0:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WRITE1:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_VALID0:
+ case STATE_VALID1:
+ case STATE_READ0:
+ case STATE_READ1:
+ return FALSE;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ return TRUE;
+}
+
+static void envelope_begin_read(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_assert(e);
+ pa_assert(v);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ case STATE_WRITE0:
+ *v = 0;
+ new_state = STATE_READ0;
+ break;
+ case STATE_VALID1:
+ case STATE_WRITE1:
+ *v = 1;
+ new_state = STATE_READ1;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+}
+
+static void envelope_commit_read(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t post_sem;
+
+ pa_assert(e);
+
+ do {
+ post_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_READ0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_READ1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WAIT0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ post_sem = TRUE;
+ break;
+ case STATE_WAIT1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ post_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (post_sem)
+ pa_semaphore_post(e->semaphore);
+}
+
+static void envelope_merge(pa_envelope *e, int v) {
+
+ e->points[v].n_points = 0;
+
+ if (e->items) {
+ pa_envelope_item *i;
+ pa_usec_t x = (pa_usec_t) -1;
+
+ for (i = e->items; i; i = i->next)
+ i->j = 0;
+
+ for (;;) {
+ pa_bool_t min_is_set;
+ pa_envelope_item *s = NULL;
+
+ /* Let's find the next spot on the X axis to analyze */
+ for (i = e->items; i; i = i->next) {
+
+ for (;;) {
+
+ if (i->j >= i->def->n_points)
+ break;
+
+ if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) {
+ i->j++;
+ continue;
+ }
+
+ if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j]))
+ s = i;
+
+ break;
+ }
+ }
+
+ if (!s)
+ break;
+
+ if (e->points[v].n_points >= e->points[v].n_allocated) {
+ e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
+
+ e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
+ e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
+ }
+
+ x = s->start_x + s->def->points_x[s->j];
+ e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec);
+
+ min_is_set = FALSE;
+
+ /* Now let's find the lowest value */
+ if (e->is_float) {
+ float min_f;
+
+ for (i = e->items; i; i = i->next) {
+ float f = item_get_float(i, x);
+ if (!min_is_set || f < min_f) {
+ min_f = f;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.f[e->points[v].n_points] = min_f;
+ } else {
+ int32_t min_k;
+
+ for (i = e->items; i; i = i->next) {
+ int32_t k = item_get_int(i, x);
+ if (!min_is_set || k < min_k) {
+ min_k = k;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.i[e->points[v].n_points] = min_k;
+ }
+
+ pa_assert_se(min_is_set);
+ e->points[v].n_points++;
+ }
+ }
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+}
+
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) {
+ pa_envelope_item *i;
+ int v;
+
+ pa_assert(e);
+ pa_assert(def);
+ pa_assert(def->n_points > 0);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_envelope_item, 1);
+
+ i->def = def;
+
+ if (e->is_float)
+ i->start_y.f = def->points_y.f[0];
+ else
+ i->start_y.i = def->points_y.i[0];
+
+ PA_LLIST_PREPEND(pa_envelope_item, e->items, i);
+
+ envelope_begin_write(e, &v);
+
+ do {
+
+ i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec);
+ envelope_merge(e, v);
+
+ } while (!envelope_commit_write(e, v));
+
+ return i;
+}
+
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) {
+ pa_usec_t x;
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+ pa_assert(def->n_points > 0);
+
+ envelope_begin_write(e, &v);
+
+ for (;;) {
+ float saved_f;
+ int32_t saved_i;
+ uint64_t saved_start_x;
+ const pa_envelope_def *saved_def;
+
+ x = pa_bytes_to_usec(e->x, &e->sample_spec);
+
+ if (e->is_float) {
+ saved_f = i->start_y.f;
+ i->start_y.f = item_get_float(i, x);
+ } else {
+ saved_i = i->start_y.i;
+ i->start_y.i = item_get_int(i, x);
+ }
+
+ saved_start_x = i->start_x;
+ saved_def = i->def;
+
+ i->start_x = x;
+ i->def = def;
+
+ envelope_merge(e, v);
+
+ if (envelope_commit_write(e, v))
+ break;
+
+ i->start_x = saved_start_x;
+ i->def = saved_def;
+
+ if (e->is_float)
+ i->start_y.f = saved_f;
+ else
+ i->start_y.i = saved_i;
+ }
+
+ return i;
+}
+
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+
+ PA_LLIST_REMOVE(pa_envelope_item, e->items, i);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ envelope_begin_write(e, &v);
+ do {
+ envelope_merge(e, v);
+ } while (!envelope_commit_write(e, v));
+}
+
+static int32_t linear_get_int(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ /* The repeated division could be replaced by Bresenham, as an
+ * optimization */
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.i[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.i[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current];
+ e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current];
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.i[e->points[v].n_current] + (e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx;
+}
+
+static float linear_get_float(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.f[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.f[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dy_dx =
+ (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) /
+ (e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]);
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.f[e->points[v].n_current] + (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;
+}
+
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(chunk);
+
+ envelope_begin_read(e, &v);
+
+ if (e->points[v].n_points > 0) {
+ void *p;
+ size_t fs, n;
+
+ pa_memchunk_make_writable(chunk, 0);
+ p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
+ fs = pa_frame_size(&e->sample_spec);
+ n = chunk->length;
+
+ switch (e->sample_spec.format) {
+
+
+
+ case PA_SAMPLE_U8: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (uint8_t) (((factor * ((int16_t) *t - 0x80)) / 0x10000) + 0x80);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_ulaw2linear16(*t);
+ *t = (uint8_t) st_14linear2ulaw(((factor * k) / 0x10000) >> 2);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_alaw2linear16(*t);
+ *t = (uint8_t) st_13linear2alaw(((factor * k) / 0x10000) >> 3);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (factor * *t) / 0x10000;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16RE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t r = (factor * PA_INT16_SWAP(*t)) / 0x10000;
+ *t = PA_INT16_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (int32_t) (((int64_t) factor * (int64_t) *t) / 0x10000);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int32_t r = (int32_t) (((int64_t) factor * (int64_t) PA_INT32_SWAP(*t)) / 0x10000);
+ *t = PA_INT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = *t * factor;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32RE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ float r = PA_FLOAT32_SWAP(*t) * factor;
+ *t = PA_FLOAT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_MAX:
+ case PA_SAMPLE_INVALID:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(chunk->memblock);
+
+ e->x += chunk->length;
+ } else {
+ /* When we have no envelope to apply we reset our origin */
+ e->x = 0;
+ }
+
+ envelope_commit_read(e, v);
+}
+
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) {
+ int v;
+
+ pa_assert(e);
+
+ envelope_begin_read(e, &v);
+
+ if (n_bytes < e->x)
+ e->x -= n_bytes;
+ else
+ e->x = 0;
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+
+ envelope_commit_read(e, v);
+}
diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h
new file mode 100644
index 00000000..5296415a
--- /dev/null
+++ b/src/pulsecore/envelope.h
@@ -0,0 +1,53 @@
+#ifndef foopulseenvelopehfoo
+#define foopulseenvelopehfoo
+
+/***
+ 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 <pulsecore/memchunk.h>
+
+#include <pulse/sample.h>
+
+#define PA_ENVELOPE_POINTS_MAX 4U
+
+typedef struct pa_envelope pa_envelope;
+typedef struct pa_envelope_item pa_envelope_item;
+
+typedef struct pa_envelope_def {
+ unsigned n_points;
+
+ pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX];
+ struct {
+ int32_t i[PA_ENVELOPE_POINTS_MAX];
+ float f[PA_ENVELOPE_POINTS_MAX];
+ } points_y;
+} pa_envelope_def;
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss);
+void pa_envelope_free(pa_envelope *e);
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def);
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def);
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i);
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk);
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes);
+
+#endif
diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h
index ea6a5665..79322ae4 100644
--- a/src/pulsecore/esound.h
+++ b/src/pulsecore/esound.h
@@ -1,8 +1,6 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 927bf00c..1531e3db 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -53,6 +51,10 @@
#define __NR_eventfd 284
#endif
+#if !defined(__NR_eventfd) && defined(__arm__)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
#if !defined(SYS_eventfd) && defined(__NR_eventfd)
#define SYS_eventfd __NR_eventfd
#endif
@@ -74,21 +76,19 @@ struct pa_fdsem {
#ifdef HAVE_EVENTFD
int efd;
#endif
- pa_atomic_t waiting;
- pa_atomic_t signalled;
- pa_atomic_t in_pipe;
+
+ pa_fdsem_data *data;
};
pa_fdsem *pa_fdsem_new(void) {
pa_fdsem *f;
- f = pa_xnew(pa_fdsem, 1);
+ f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
#ifdef HAVE_EVENTFD
if ((f->efd = eventfd(0)) >= 0) {
pa_make_fd_cloexec(f->efd);
f->fds[0] = f->fds[1] = -1;
-
} else
#endif
{
@@ -101,9 +101,57 @@ pa_fdsem *pa_fdsem_new(void) {
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);
+ f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd >= 0);
+
+#ifdef HAVE_EVENTFD
+ f = pa_xnew(pa_fdsem, 1);
+
+ f->efd = event_fd;
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+#endif
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd);
+
+#ifdef HAVE_EVENTFD
+
+ f = pa_xnew(pa_fdsem, 1);
+
+ if ((f->efd = eventfd(0)) < 0) {
+ pa_xfree(f);
+ return NULL;
+ }
+
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+#endif
return f;
}
@@ -124,7 +172,7 @@ static void flush(pa_fdsem *f) {
ssize_t r;
pa_assert(f);
- if (pa_atomic_load(&f->in_pipe) <= 0)
+ if (pa_atomic_load(&f->data->in_pipe) <= 0)
return;
do {
@@ -147,19 +195,19 @@ static void flush(pa_fdsem *f) {
continue;
}
- } while (pa_atomic_sub(&f->in_pipe, r) > r);
+ } while (pa_atomic_sub(&f->data->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_cmpxchg(&f->data->signalled, 0, 1)) {
- if (pa_atomic_load(&f->waiting)) {
+ if (pa_atomic_load(&f->data->waiting)) {
ssize_t r;
char x = 'x';
- pa_atomic_inc(&f->in_pipe);
+ pa_atomic_inc(&f->data->in_pipe);
for (;;) {
@@ -190,12 +238,12 @@ void pa_fdsem_wait(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return;
- pa_atomic_inc(&f->waiting);
+ pa_atomic_inc(&f->data->waiting);
- while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
+ while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
char x[10];
ssize_t r;
@@ -217,10 +265,10 @@ void pa_fdsem_wait(pa_fdsem *f) {
continue;
}
- pa_atomic_sub(&f->in_pipe, r);
+ pa_atomic_sub(&f->data->in_pipe, r);
}
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
}
int pa_fdsem_try(pa_fdsem *f) {
@@ -228,7 +276,7 @@ int pa_fdsem_try(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return 1;
return 0;
@@ -250,13 +298,13 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return -1;
- pa_atomic_inc(&f->waiting);
+ pa_atomic_inc(&f->data->waiting);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
return -1;
}
return 0;
@@ -265,11 +313,11 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
int pa_fdsem_after_poll(pa_fdsem *f) {
pa_assert(f);
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return 1;
return 0;
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
index f38ef205..48a77c49 100644
--- a/src/pulsecore/fdsem.h
+++ b/src/pulsecore/fdsem.h
@@ -1,8 +1,6 @@
#ifndef foopulsefdsemhfoo
#define foopulsefdsemhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,15 @@
typedef struct pa_fdsem pa_fdsem;
+typedef struct pa_fdsem_data {
+ pa_atomic_t waiting;
+ pa_atomic_t signalled;
+ pa_atomic_t in_pipe;
+} pa_fdsem_data;
+
pa_fdsem *pa_fdsem_new(void);
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd);
void pa_fdsem_free(pa_fdsem *f);
void pa_fdsem_post(pa_fdsem *f);
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
index bf8b84ea..696fc986 100644
--- a/src/pulsecore/ffmpeg/avcodec.h
+++ b/src/pulsecore/ffmpeg/avcodec.h
@@ -43,7 +43,7 @@
static inline void av_freep(void *k) {
void **p = k;
-
+
if (p) {
free(*p);
*p = NULL;
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index d9740777..f166ee33 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
index daf0fec4..c040667d 100644
--- a/src/pulsecore/flist.h
+++ b/src/pulsecore/flist.h
@@ -1,8 +1,6 @@
#ifndef foopulseflisthfoo
#define foopulseflisthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,9 +23,9 @@
***/
#include <pulse/def.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/once.h>
-#include <pulsecore/gccmacro.h>
/* A multiple-reader multipler-write lock-free free list implementation */
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 8366181c..b7f4109b 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,7 +69,7 @@ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_f
return h;
}
-static void remove(pa_hashmap *h, struct hashmap_entry *e) {
+static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
pa_assert(h);
pa_assert(e);
@@ -93,7 +91,7 @@ static void remove(pa_hashmap *h, struct hashmap_entry *e) {
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
-
+
h->n_entries--;
}
@@ -103,7 +101,7 @@ void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), v
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
}
pa_xfree(h->data);
@@ -134,7 +132,7 @@ int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
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;
@@ -182,7 +180,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
return NULL;
data = e->value;
- remove(h, e);
+ remove_entry(h, e);
return data;
}
@@ -191,24 +189,36 @@ unsigned pa_hashmap_size(pa_hashmap *h) {
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
+ struct hashmap_entry *e;
+
pa_assert(h);
pa_assert(state);
- if (!*state)
- *state = h->first_entry;
+ if (*state == (void*) -1)
+ goto at_end;
+
+ if ((!*state && !h->first_entry))
+ goto at_end;
+
+ e = *state ? *state : h->first_entry;
+
+ if (e->next)
+ *state = e->next;
else
- *state = ((struct hashmap_entry*) *state)->next;
+ *state = (void*) -1;
- if (!*state) {
- if (key)
- *key = NULL;
- return NULL;
- }
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_end:
+ *state = (void *) -1;
if (key)
- *key = ((struct hashmap_entry*) *state)->key;
+ *key = NULL;
- return ((struct hashmap_entry*) *state)->value;
+ return NULL;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
@@ -220,7 +230,7 @@ void* pa_hashmap_steal_first(pa_hashmap *h) {
return NULL;
data = h->first_entry->value;
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
return data;
}
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 98df4502..a4505c4c 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -1,8 +1,6 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -51,9 +49,10 @@ unsigned pa_hashmap_size(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
- modified during iteration. The key of the entry is returned in
- *key, if key is non-NULL. After the last entry in the hashmap NULL
- is returned. */
+ modified during iteration -- except for deleting the current entry
+ via pa_hashmap_remove(). The key of the entry is returned in *key,
+ if key is non-NULL. After the last entry in the hashmap NULL is
+ returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
void *pa_hashmap_steal_first(pa_hashmap *h);
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 3a6874c4..0aac4759 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,8 +31,7 @@ void pa_hook_init(pa_hook *hook, void *data) {
pa_assert(hook);
PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
- hook->last = NULL;
- hook->n_dead = hook->firing = 0;
+ hook->n_dead = hook->n_firing = 0;
hook->data = data;
}
@@ -42,9 +39,6 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
pa_assert(hook);
pa_assert(slot);
- if (hook->last == slot)
- hook->last = slot->prev;
-
PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot);
pa_xfree(slot);
@@ -52,7 +46,7 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
void pa_hook_free(pa_hook *hook) {
pa_assert(hook);
- pa_assert(!hook->firing);
+ pa_assert(hook->n_firing == 0);
while (hook->slots)
slot_free(hook, hook->slots);
@@ -60,19 +54,26 @@ void pa_hook_free(pa_hook *hook) {
pa_hook_init(hook, NULL);
}
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {
- pa_hook_slot *slot;
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+ pa_hook_slot *slot, *where, *prev;
pa_assert(cb);
slot = pa_xnew(pa_hook_slot, 1);
slot->hook = hook;
- slot->dead = 0;
+ slot->dead = FALSE;
slot->callback = cb;
slot->data = data;
+ slot->priority = prio;
- PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, hook->last, slot);
- hook->last = slot;
+ prev = NULL;
+ for (where = hook->slots; where; where = where->next) {
+ if (prio < where->priority)
+ break;
+ prev = where;
+ }
+
+ PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot);
return slot;
}
@@ -81,8 +82,8 @@ void pa_hook_slot_free(pa_hook_slot *slot) {
pa_assert(slot);
pa_assert(!slot->dead);
- if (slot->hook->firing > 0) {
- slot->dead = 1;
+ if (slot->hook->n_firing > 0) {
+ slot->dead = TRUE;
slot->hook->n_dead++;
} else
slot_free(slot->hook, slot);
@@ -94,7 +95,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
pa_assert(hook);
- hook->firing ++;
+ hook->n_firing ++;
for (slot = hook->slots; slot; slot = slot->next) {
if (slot->dead)
@@ -104,7 +105,8 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
break;
}
- hook->firing --;
+ hook->n_firing --;
+ pa_assert(hook->n_firing >= 0);
for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) {
next = slot->next;
@@ -115,6 +117,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
}
}
+ pa_assert(hook->n_dead == 0);
+
return result;
}
-
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index b3bd600a..cf85aca5 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -1,8 +1,6 @@
#ifndef foohooklistfoo
#define foohooklistfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,9 +22,10 @@
USA.
***/
-#include <pulsecore/llist.h>
#include <pulse/xmalloc.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/llist.h>
typedef struct pa_hook_slot pa_hook_slot;
typedef struct pa_hook pa_hook;
@@ -37,14 +36,21 @@ typedef enum pa_hook_result {
PA_HOOK_CANCEL = -1
} pa_hook_result_t;
+typedef enum pa_hook_priority {
+ PA_HOOK_EARLY = -100,
+ PA_HOOK_NORMAL = 0,
+ PA_HOOK_LATE = 100
+} pa_hook_priority_t;
+
typedef pa_hook_result_t (*pa_hook_cb_t)(
void *hook_data,
void *call_data,
void *slot_data);
struct pa_hook_slot {
- int dead;
+ pa_bool_t dead;
pa_hook *hook;
+ pa_hook_priority_t priority;
pa_hook_cb_t callback;
void *data;
PA_LLIST_FIELDS(pa_hook_slot);
@@ -52,8 +58,7 @@ struct pa_hook_slot {
struct pa_hook {
PA_LLIST_HEAD(pa_hook_slot, slots);
- pa_hook_slot *last;
- int firing, n_dead;
+ int n_firing, n_dead;
void *data;
};
@@ -61,7 +66,7 @@ struct pa_hook {
void pa_hook_init(pa_hook *hook, void *data);
void pa_hook_free(pa_hook *hook);
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t, void *data);
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
void pa_hook_slot_free(pa_hook_slot *slot);
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 773d7d3a..7c9520a4 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -156,7 +154,7 @@ static void extend_array(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;
@@ -240,7 +238,7 @@ 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;
struct idxset_entry *e;
-
+
pa_assert(s);
pa_assert(p);
@@ -259,7 +257,7 @@ void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
struct idxset_entry **a;
-
+
pa_assert(s);
pa_assert(e);
@@ -338,7 +336,7 @@ 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) {
struct idxset_entry **a, *e = NULL;
-
+
pa_assert(s);
pa_assert(idx);
@@ -368,7 +366,7 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
struct idxset_entry **a, *e = NULL;
-
+
pa_assert(s);
pa_assert(idx);
@@ -386,7 +384,7 @@ 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) {
struct idxset_entry *e;
-
+
pa_assert(s);
pa_assert(func);
@@ -411,13 +409,13 @@ int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del,
unsigned pa_idxset_size(pa_idxset*s) {
pa_assert(s);
-
+
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
pa_assert(s);
-
+
return s->n_entries == 0;
}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 5b55cec2..0a4e528e 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -1,8 +1,6 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index 041bc09b..87551232 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_NTOP
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
index 7272e459..d191e550 100644
--- a/src/pulsecore/inet_pton.c
+++ b/src/pulsecore/inet_pton.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_PTON
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index 01f17ab3..b40c9815 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -267,9 +265,11 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
struct ucred *u;
- struct cmsghdr *cmsg;
pa_assert(io);
pa_assert(data);
@@ -280,13 +280,12 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
iov.iov_base = (void*) data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
- u = (struct ucred*) CMSG_DATA(cmsg);
+ u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
u->pid = getpid();
if (ucred) {
@@ -302,8 +301,8 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
@@ -318,7 +317,10 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
pa_assert(io);
pa_assert(data);
@@ -331,28 +333,28 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
iov.iov_base = data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
+ memset(&cmsg, 0, sizeof(cmsg));
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
- struct cmsghdr *cmsg;
+ struct cmsghdr *cmh;
*creds_valid = 0;
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
struct ucred u;
- pa_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
- memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
+ pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+ memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
creds->gid = u.gid;
creds->uid = u.uid;
@@ -420,3 +422,16 @@ int pa_iochannel_get_send_fd(pa_iochannel *io) {
return io->ofd;
}
+
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
+ pa_assert(io);
+
+ if (pa_socket_is_local(io->ifd))
+ return TRUE;
+
+ if (io->ifd != io->ofd)
+ if (pa_socket_is_local(io->ofd))
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
index c9794d99..9050df90 100644
--- a/src/pulsecore/iochannel.h
+++ b/src/pulsecore/iochannel.h
@@ -1,8 +1,6 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -85,6 +83,8 @@ void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io);
+
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
int pa_iochannel_get_recv_fd(pa_iochannel *io);
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index efb50722..90afaafd 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -45,11 +43,10 @@
struct pa_ioline {
PA_REFCNT_DECLARE;
-
+
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
- int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
@@ -57,10 +54,11 @@ struct pa_ioline {
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
- void (*callback)(pa_ioline*io, const char *s, void *userdata);
+ pa_ioline_cb_t callback;
void *userdata;
- int defer_close;
+ pa_bool_t dead:1;
+ pa_bool_t defer_close:1;
};
static void io_callback(pa_iochannel*io, void *userdata);
@@ -73,7 +71,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l = pa_xnew(pa_ioline, 1);
PA_REFCNT_INIT(l);
l->io = io;
- l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
@@ -89,7 +86,8 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
l->mainloop->defer_enable(l->defer_event, 0);
- l->defer_close = 0;
+ l->dead = FALSE;
+ l->defer_close = FALSE;
pa_iochannel_set_callback(io, io_callback, l);
@@ -130,7 +128,7 @@ void pa_ioline_close(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->dead = 1;
+ l->dead = TRUE;
if (l->io) {
pa_iochannel_free(l->io);
@@ -166,11 +164,13 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
size_t n = l->wbuf_valid_length+len;
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
+
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
pa_xfree(l->wbuf);
}
+
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
@@ -191,15 +191,18 @@ 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) {
+void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ if (l->dead)
+ return;
+
l->callback = callback;
l->userdata = userdata;
}
-static void failure(pa_ioline *l, int process_leftover) {
+static void failure(pa_ioline *l, pa_bool_t process_leftover) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
pa_assert(!l->dead);
@@ -247,7 +250,7 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
l->rbuf_index = 0;
if (l->callback)
- l->callback(l, p, l->userdata);
+ l->callback(l, pa_strip_nl(p), l->userdata);
skip = 0;
}
@@ -282,7 +285,7 @@ static int do_read(pa_ioline *l) {
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
/* Enlarge the buffer */
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
@@ -299,11 +302,15 @@ static int do_read(pa_ioline *l) {
/* Read some data */
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
+
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
if (r < 0 && errno != ECONNRESET) {
pa_log("read(): %s", pa_cstrerror(errno));
- failure(l, 0);
+ failure(l, FALSE);
} else
- failure(l, 1);
+ failure(l, TRUE);
return -1;
}
@@ -328,11 +335,14 @@ static int do_write(pa_ioline *l) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
if (r < 0 && errno != EPIPE)
pa_log("write(): %s", pa_cstrerror(errno));
- failure(l, 0);
-
+ failure(l, FALSE);
+
return -1;
}
@@ -363,14 +373,14 @@ static void do_work(pa_ioline *l) {
do_write(l);
if (l->defer_close && !l->wbuf_valid_length)
- failure(l, 1);
+ failure(l, TRUE);
pa_ioline_unref(l);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
-
+
pa_assert(io);
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
@@ -393,7 +403,7 @@ void pa_ioline_defer_close(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->defer_close = 1;
+ l->defer_close = TRUE;
if (!l->wbuf_valid_length)
l->mainloop->defer_enable(l->defer_event, 1);
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index 8475b798..b9a3d9f4 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -1,8 +1,6 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,8 @@
typedef struct pa_ioline pa_ioline;
+typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
pa_ioline* pa_ioline_ref(pa_ioline *l);
@@ -45,7 +45,7 @@ void pa_ioline_puts(pa_ioline *s, const char *c);
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
/* Set the callback function that is called for every recieved line */
-void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
+void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
index 9b22e8f5..7b5f865b 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 175f54e0..7b7ffa61 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index e62f15b4..46b54eb3 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -1,8 +1,6 @@
#ifndef foollistfoo
#define foollistfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 53f8974c..5eda4f65 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,6 +28,7 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
@@ -80,7 +79,7 @@ void pa_log_set_ident(const char *p) {
}
/* To make valgrind shut up. */
-static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
static void ident_destructor(void) {
pa_xfree(log_ident);
pa_xfree(log_ident_local);
@@ -88,13 +87,13 @@ static void ident_destructor(void) {
void pa_log_set_maximal_level(pa_log_level_t l) {
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)) {
pa_assert(t == PA_LOG_USER || !func);
-
+
log_target = t;
user_log_func = func;
}
@@ -108,7 +107,12 @@ void pa_log_levelv_meta(
va_list ap) {
const char *e;
- char *text, *t, *n, *location;
+ char *t, *n;
+ int saved_errno = errno;
+
+ /* We don't use dynamic memory allocation here to minimize the hit
+ * in RT threads */
+ char text[1024], location[128];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
@@ -116,17 +120,19 @@ void pa_log_levelv_meta(
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
- if (level > maximal_level)
+ if (level > maximal_level) {
+ errno = saved_errno;
return;
+ }
- text = pa_vsprintf_malloc(format, ap);
+ pa_vsnprintf(text, sizeof(text), format, ap);
if (getenv(ENV_LOGMETA) && file && line > 0 && func)
- location = pa_sprintf_malloc("[%s:%i %s()] ", file, line, func);
+ pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
else if (file)
- location = pa_sprintf_malloc("%s: ", pa_path_get_filename(file));
+ pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
else
- location = pa_xstrdup("");
+ location[0] = 0;
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
@@ -158,6 +164,8 @@ void pa_log_levelv_meta(
}
#endif
+ /* We shouldn't be using dynamic allocation here to
+ * minimize the hit in RT threads */
local_t = pa_utf8_to_locale(t);
if (!local_t)
fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix);
@@ -189,11 +197,10 @@ void pa_log_levelv_meta(
#endif
case PA_LOG_USER: {
- char *x;
+ char x[1024];
- x = pa_sprintf_malloc("%s%s", location, t);
+ pa_snprintf(x, sizeof(x), "%s%s", location, t);
user_log_func(level, x);
- pa_xfree(x);
break;
}
@@ -204,8 +211,7 @@ void pa_log_levelv_meta(
}
}
- pa_xfree(text);
- pa_xfree(location);
+ errno = saved_errno;
}
void pa_log_level_meta(
@@ -227,7 +233,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/log.h b/src/pulsecore/log.h
index b0711dca..2047696e 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -1,8 +1,6 @@
#ifndef foologhfoo
#define foologhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,7 @@
#include <stdarg.h>
#include <stdlib.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
/* A simple logging subsystem */
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index bdce18ef..0d4c22f8 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,12 +40,14 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
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))))
+
+ if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
return f;
+ if (!module)
+ return NULL;
+
/* As the .la files might have been cleansed from the system, we should
* try with the ltdl prefix as well. */
@@ -57,7 +57,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
if (!isalnum(*c))
*c = '_';
- f = (pa_void_func_t) (long) lt_dlsym(handle, sn);
+ f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
pa_xfree(sn);
return f;
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
index 5c7388a1..ea73de54 100644
--- a/src/pulsecore/ltdl-helper.h
+++ b/src/pulsecore/ltdl-helper.h
@@ -1,8 +1,6 @@
#ifndef foopulsecoreltdlhelperhfoo
#define foopulsecoreltdlhelperhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index c6bba437..fd33b7bb 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -1,8 +1,6 @@
#ifndef foopulsemacrohfoo
#define foopulsemacrohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,13 +27,26 @@
#include <assert.h>
#include <limits.h>
#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <pulsecore/log.h>
+#include <pulse/gccmacro.h>
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
+#ifndef PA_LIKELY
+#ifdef __GNUC__
+#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
+#define PA_UNLIKELY(x) (__builtin_expect((x),0))
+#else
+#define PA_LIKELY(x) (x)
+#define PA_UNLIKELY(x) (x)
+#endif
+#endif
+
#if defined(PAGE_SIZE)
#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
#elif defined(PAGESIZE)
@@ -64,18 +75,57 @@ static inline size_t pa_page_align(size_t l) {
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
+/* The users of PA_MIN and PA_MAX should be aware that these macros on
+ * non-GCC executed code with side effects twice. It is thus
+ * considered misuse to use code with side effects as arguments to MIN
+ * and MAX. */
+
+#ifdef __GNUC__
+#define PA_MAX(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+#else
+#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#ifdef __GNUC__
+#define PA_MIN(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+#else
+#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#endif
-#ifndef CLAMP
-#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#ifdef __GNUC__
+#define PA_CLAMP_UNLIKELY(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
#endif
+/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
+ * make sense: we cannot know if it is more likely that the values is
+ * lower or greater than the boundaries.*/
+
/* This type is not intended to be used in exported APIs! Use classic "int" there! */
#ifdef HAVE_STD_BOOL
typedef _Bool pa_bool_t;
@@ -97,35 +147,47 @@ typedef int pa_bool_t;
#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_if_fail(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return; \
+ } \
+ } while(FALSE)
+
+#define pa_return_val_if_fail(expr, val) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return (val); \
+ } \
+ } while(FALSE)
#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 */
+/* An assert which guarantees side effects of x, i.e. is never
+ * optimized away */
+#define pa_assert_se(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ abort(); \
+ } \
+ } while (FALSE)
+
+/* An assert that may be optimized away by defining NDEBUG */
#ifdef NDEBUG
-#define pa_assert_se(x) x
+#define pa_assert(expr) do {} while (FALSE)
#else
-#define pa_assert_se(x) pa_assert(x)
+#define pa_assert(expr) pa_assert_se(expr)
#endif
+#define pa_assert_not_reached() \
+ do { \
+ pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ abort(); \
+ } while (FALSE)
+
#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
@@ -146,4 +208,17 @@ typedef int pa_bool_t;
#define PA_PATH_SEP_CHAR '/'
#endif
+#ifdef __GNUC__
+
+#define PA_WARN_REFERENCE(sym, msg) \
+ __asm__(".section .gnu.warning." #sym); \
+ __asm__(".asciz \"" msg "\""); \
+ __asm__(".previous")
+
+#else
+
+#define PA_WARN_REFERENCE(sym, msg)
+
+#endif
+
#endif
diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c
index 8ca7c962..a03d5ae7 100644
--- a/src/pulsecore/mcalign.c
+++ b/src/pulsecore/mcalign.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -197,7 +195,6 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
/* There's simply nothing */
return -1;
-
}
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
@@ -211,3 +208,11 @@ size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
return (l/m->base)*m->base;
}
+
+void pa_mcalign_flush(pa_mcalign *m) {
+ pa_memchunk chunk;
+ pa_assert(m);
+
+ while (pa_mcalign_pop(m, &chunk) >= 0)
+ pa_memblock_unref(chunk.memblock);
+}
diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h
index 6ff8f94e..e82eb007 100644
--- a/src/pulsecore/mcalign.h
+++ b/src/pulsecore/mcalign.h
@@ -1,8 +1,6 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -79,4 +77,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
/* If we pass l bytes in now, how many bytes would we get out? */
size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
+/* Flush what's still stored in the aligner */
+void pa_mcalign_flush(pa_mcalign *m);
+
#endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index e05304ba..c2ee1360 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,8 +44,12 @@
#include "memblock.h"
-#define PA_MEMPOOL_SLOTS_MAX 128
-#define PA_MEMPOOL_SLOT_SIZE (16*1024)
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
#define PA_MEMEXPORT_SLOTS_MAX 128
@@ -59,7 +61,9 @@ struct pa_memblock {
pa_mempool *pool;
pa_memblock_type_t type;
- int read_only; /* boolean */
+
+ pa_bool_t read_only:1;
+ pa_bool_t is_silence:1;
pa_atomic_ptr_t data;
size_t length;
@@ -125,11 +129,6 @@ struct pa_memexport {
PA_LLIST_FIELDS(pa_memexport);
};
-struct mempool_slot {
- PA_LLIST_FIELDS(struct mempool_slot);
- /* the actual data follows immediately hereafter */
-};
-
struct pa_mempool {
pa_semaphore *semaphore;
pa_mutex *mutex;
@@ -202,7 +201,7 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
if (!(b = pa_memblock_new_pool(p, length)))
b = memblock_new_appended(p, length);
@@ -215,18 +214,18 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(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));
-
+ length = p->block_size - 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;
+ b->read_only = b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -252,7 +251,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
if (!slot) {
- pa_log_debug("Pool full");
+ pa_log_info("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
@@ -261,11 +260,9 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
return slot;
}
-/* No lock necessary */
-static void* mempool_slot_data(struct mempool_slot *slot) {
- pa_assert(slot);
-
- return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+ return slot;
}
/* No lock necessary */
@@ -294,15 +291,15 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
struct mempool_slot *slot;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
/* 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 (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 (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
@@ -311,26 +308,26 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
b->type = PA_MEMBLOCK_POOL;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
- } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {
+ } else if (p->block_size >= length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
-
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
} else {
- 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_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
pa_atomic_inc(&p->stat.n_too_large_for_pool);
return NULL;
}
PA_REFCNT_INIT(b);
b->pool = p;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
@@ -340,13 +337,13 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
pa_assert(length != (size_t) -1);
- pa_assert(length > 0);
+ pa_assert(length);
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
@@ -354,6 +351,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -364,12 +362,12 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
}
/* 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 *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
- pa_assert(length > 0);
+ pa_assert(length);
pa_assert(length != (size_t) -1);
pa_assert(free_cb);
@@ -379,6 +377,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
b->pool = p;
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -391,7 +390,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
}
/* No lock necessary */
-int pa_memblock_is_read_only(pa_memblock *b) {
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) > 0);
@@ -399,13 +398,27 @@ int pa_memblock_is_read_only(pa_memblock *b) {
}
/* No lock necessary */
-int pa_memblock_ref_is_one(pa_memblock *b) {
+pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
int r;
-
pa_assert(b);
- r = PA_REFCNT_VALUE(b);
- pa_assert(r > 0);
+ pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
return r == 1;
}
@@ -475,7 +488,7 @@ static void memblock_free(pa_memblock *b) {
case PA_MEMBLOCK_APPENDED :
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
-
+
break;
case PA_MEMBLOCK_IMPORTED : {
@@ -567,7 +580,7 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
- if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {
+ if (b->length <= b->pool->block_size) {
struct mempool_slot *slot;
if ((slot = mempool_allocate_slot(b->pool))) {
@@ -579,7 +592,7 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_ptr_store(&b->data, new_data);
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
- b->read_only = 0;
+ b->read_only = FALSE;
goto finish;
}
@@ -590,7 +603,7 @@ static void memblock_make_local(pa_memblock *b) {
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->read_only = FALSE;
finish:
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -613,7 +626,7 @@ void pa_memblock_unref_fixed(pa_memblock *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);
@@ -651,11 +664,11 @@ static void memblock_replace_import(pa_memblock *b) {
if (-- seg->n_blocks <= 0) {
pa_mutex_unlock(seg->import->mutex);
segment_detach(seg);
- } else
+ } else
pa_mutex_unlock(seg->import->mutex);
}
-pa_mempool* pa_mempool_new(int shared) {
+pa_mempool* pa_mempool_new(pa_bool_t shared) {
pa_mempool *p;
p = pa_xnew(pa_mempool, 1);
@@ -669,8 +682,6 @@ pa_mempool* pa_mempool_new(int shared) {
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
- 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;
@@ -726,7 +737,7 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
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));
+ return p->block_size - PA_ALIGN(sizeof(pa_memblock));
}
/* No lock necessary */
@@ -743,9 +754,7 @@ void pa_mempool_vacuum(pa_mempool *p) {
;
while ((slot = pa_flist_pop(list))) {
- pa_shm_punch(&p->memory,
- (uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)),
- p->block_size - PA_ALIGN(sizeof(struct mempool_slot)));
+ pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
while (pa_flist_push(p->free_slots, slot))
;
@@ -767,7 +776,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
}
/* No lock necessary */
-int pa_mempool_is_shared(pa_mempool *p) {
+pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
pa_assert(p);
return !!p->memory.shared;
@@ -882,11 +891,12 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
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;
+ b->read_only = TRUE;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
b->length = size;
pa_atomic_store(&b->n_acquired, 0);
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index c704014a..efe55b02 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -1,8 +1,6 @@
#ifndef foopulsememblockhfoo
#define foopulsememblockhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -87,13 +85,13 @@ pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
-pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, void (*free_cb)(void *p), int read_only);
+pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only);
/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */
#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0)
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, int read_only);
+pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only);
void pa_memblock_unref(pa_memblock*b);
pa_memblock* pa_memblock_ref(pa_memblock*b);
@@ -106,8 +104,11 @@ 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);
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b);
+pa_bool_t pa_memblock_is_silence(pa_memblock *b);
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b);
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v);
+
void* pa_memblock_acquire(pa_memblock *b);
void pa_memblock_release(pa_memblock *b);
size_t pa_memblock_get_length(pa_memblock *b);
@@ -116,12 +117,12 @@ 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);
+pa_mempool* pa_mempool_new(pa_bool_t shared);
void pa_mempool_free(pa_mempool *p);
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
void pa_mempool_vacuum(pa_mempool *p);
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
-int pa_mempool_is_shared(pa_mempool *p);
+pa_bool_t pa_mempool_is_shared(pa_mempool *p);
size_t pa_mempool_block_size_max(pa_mempool *p);
/* For recieving blocks from other nodes */
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index a46155a9..a9f28a07 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -50,11 +48,12 @@ PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
struct pa_memblockq {
struct list_item *blocks, *blocks_tail;
+ struct list_item *current_read, *current_write;
unsigned n_blocks;
- size_t maxlength, tlength, base, prebuf, minreq;
+ size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
int64_t read_index, write_index;
pa_bool_t in_prebuf;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_mcalign *mcalign;
int64_t missing;
size_t requested;
@@ -67,52 +66,43 @@ pa_memblockq* pa_memblockq_new(
size_t base,
size_t prebuf,
size_t minreq,
- pa_memblock *silence) {
+ size_t maxrewind,
+ pa_memchunk *silence) {
pa_memblockq* bq;
pa_assert(base > 0);
- pa_assert(maxlength >= base);
bq = pa_xnew(pa_memblockq, 1);
bq->blocks = bq->blocks_tail = NULL;
+ bq->current_read = bq->current_write = NULL;
bq->n_blocks = 0;
bq->base = base;
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);
-
- bq->maxlength = ((maxlength+base-1)/base)*base;
- pa_assert(bq->maxlength >= base);
-
- bq->tlength = ((tlength+base-1)/base)*base;
- if (bq->tlength <= 0 || bq->tlength > bq->maxlength)
- bq->tlength = bq->maxlength;
-
- bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
- bq->prebuf = ((bq->prebuf+base-1)/base)*base;
- if (bq->prebuf > bq->maxlength)
- bq->prebuf = bq->maxlength;
-
- bq->minreq = (minreq/base)*base;
+ pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
- if (bq->minreq > bq->tlength - bq->prebuf)
- bq->minreq = bq->tlength - bq->prebuf;
+ bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
+ bq->in_prebuf = TRUE;
- if (!bq->minreq)
- bq->minreq = 1;
+ pa_memblockq_set_maxlength(bq, maxlength);
+ pa_memblockq_set_tlength(bq, tlength);
+ pa_memblockq_set_prebuf(bq, prebuf);
+ pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_maxrewind(bq, maxrewind);
- 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);
+ pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
- bq->in_prebuf = bq->prebuf > 0;
- bq->silence = silence ? pa_memblock_ref(silence) : NULL;
- bq->mcalign = NULL;
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
- bq->missing = bq->tlength;
- bq->requested = 0;
+ bq->mcalign = pa_mcalign_new(bq->base);
return bq;
}
@@ -122,8 +112,8 @@ void pa_memblockq_free(pa_memblockq* bq) {
pa_memblockq_flush(bq);
- if (bq->silence)
- pa_memblock_unref(bq->silence);
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
@@ -131,6 +121,62 @@ void pa_memblockq_free(pa_memblockq* bq) {
pa_xfree(bq);
}
+static void fix_current_read(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_read = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_read))
+ bq->current_read = bq->blocks;
+
+ /* Scan left */
+ while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
+
+ if (bq->current_read->prev)
+ bq->current_read = bq->current_read->prev;
+ else
+ break;
+
+ /* Scan right */
+ while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
+ bq->current_read = bq->current_read->next;
+
+ /* At this point current_read will either point at or left of the
+ next block to play. It may be NULL in case everything in
+ the queue was already played */
+}
+
+static void fix_current_write(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_write = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_write))
+ bq->current_write = bq->blocks_tail;
+
+ /* Scan right */
+ while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
+
+ if (bq->current_write->next)
+ bq->current_write = bq->current_write->next;
+ else
+ break;
+
+ /* Scan left */
+ while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
+ bq->current_write = bq->current_write->prev;
+
+ /* At this point current_write will either point at or right of
+ the next block to write data to. It may be NULL in case
+ everything in the queue is still to be played */
+}
+
static void drop_block(pa_memblockq *bq, struct list_item *q) {
pa_assert(bq);
pa_assert(q);
@@ -139,13 +185,23 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
if (q->prev)
q->prev->next = q->next;
- else
+ else {
+ pa_assert(bq->blocks == q);
bq->blocks = q->next;
+ }
if (q->next)
q->next->prev = q->prev;
- else
+ else {
+ pa_assert(bq->blocks_tail == q);
bq->blocks_tail = q->prev;
+ }
+
+ if (bq->current_write == q)
+ bq->current_write = q->prev;
+
+ if (bq->current_read == q)
+ bq->current_read = q->next;
pa_memblock_unref(q->chunk.memblock);
@@ -155,6 +211,16 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
bq->n_blocks--;
}
+static void drop_backlog(pa_memblockq *bq) {
+ int64_t boundary;
+ pa_assert(bq);
+
+ boundary = bq->read_index - bq->maxrewind;
+
+ while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
+ drop_block(bq, bq->blocks);
+}
+
static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
int64_t end;
@@ -169,10 +235,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
return TRUE;
}
- end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
+ end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
/* Make sure that the list doesn't get too long */
- if (bq->write_index + (int64_t)l > end)
+ if (bq->write_index + (int64_t) l > end)
if (bq->write_index + l - bq->read_index > bq->maxlength)
return FALSE;
@@ -199,28 +265,26 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
old = bq->write_index;
chunk = *uchunk;
- if (bq->read_index > bq->write_index) {
-
- /* We currently have a buffer underflow, we need to drop some
- * incoming data */
+ fix_current_write(bq);
+ q = bq->current_write;
- size_t d = bq->read_index - bq->write_index;
+ /* First we advance the q pointer right of where we want to
+ * write to */
- if (chunk.length > d) {
- chunk.index += d;
- chunk.length -= d;
- bq->write_index += d;
- } else {
- /* We drop the incoming data completely */
- bq->write_index += chunk.length;
- goto finish;
- }
+ if (q) {
+ while (bq->write_index + (int64_t) chunk.length > q->index)
+ if (q->next)
+ q = q->next;
+ else
+ break;
}
+ if (!q)
+ q = bq->blocks_tail;
+
/* We go from back to front to look for the right place to add
* this new entry. Drop data we will overwrite on the way */
- q = bq->blocks_tail;
while (q) {
if (bq->write_index >= q->index + (int64_t) q->chunk.length)
@@ -346,7 +410,7 @@ finish:
delta = bq->write_index - old;
- if (delta >= bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else {
@@ -359,7 +423,16 @@ finish:
return 0;
}
-static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (bq->in_prebuf)
+ return pa_memblockq_get_length(bq) < bq->prebuf;
+ else
+ return bq->prebuf > 0 && bq->read_index >= bq->write_index;
+}
+
+static pa_bool_t update_prebuf(pa_memblockq *bq) {
pa_assert(bq);
if (bq->in_prebuf) {
@@ -381,34 +454,42 @@ static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
}
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+ int64_t d;
pa_assert(bq);
pa_assert(chunk);
/* We need to pre-buffer */
- if (memblockq_check_prebuf(bq))
+ if (update_prebuf(bq))
return -1;
+ fix_current_read(bq);
+
/* Do we need to spit out silence? */
- if (!bq->blocks || bq->blocks->index > bq->read_index) {
+ if (!bq->current_read || bq->current_read->index > bq->read_index) {
size_t length;
/* How much silence shall we return? */
- length = bq->blocks ? bq->blocks->index - bq->read_index : 0;
+ if (bq->current_read)
+ length = bq->current_read->index - bq->read_index;
+ else if (bq->write_index > bq->read_index)
+ length = (size_t) (bq->write_index - bq->read_index);
+ else
+ length = 0;
/* We need to return silence, since no data is yet available */
- if (bq->silence) {
- chunk->memblock = pa_memblock_ref(bq->silence);
+ if (bq->silence.memblock) {
+ *chunk = bq->silence;
+ pa_memblock_ref(chunk->memblock);
- if (!length || length > pa_memblock_get_length(chunk->memblock))
- length = pa_memblock_get_length(chunk->memblock);
+ if (length > 0 && length < chunk->length)
+ chunk->length = length;
- chunk->length = length;
} else {
/* If the memblockq is empty, return -1, otherwise return
* the time to sleep */
- if (!bq->blocks)
+ if (length <= 0)
return -1;
chunk->memblock = NULL;
@@ -420,11 +501,14 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
}
/* Ok, let's pass real data to the caller */
- pa_assert(bq->blocks->index == bq->read_index);
-
- *chunk = bq->blocks->chunk;
+ *chunk = bq->current_read->chunk;
pa_memblock_ref(chunk->memblock);
+ pa_assert(bq->read_index >= bq->current_read->index);
+ d = bq->read_index - bq->current_read->index;
+ chunk->index += d;
+ chunk->length -= d;
+
return 0;
}
@@ -438,45 +522,26 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
while (length > 0) {
/* Do not drop any data when we are in prebuffering mode */
- if (memblockq_check_prebuf(bq))
+ if (update_prebuf(bq))
break;
- if (bq->blocks) {
- size_t d;
-
- pa_assert(bq->blocks->index >= bq->read_index);
-
- d = (size_t) (bq->blocks->index - bq->read_index);
-
- if (d >= length) {
- /* The first block is too far in the future */
-
- bq->read_index += length;
- break;
- } else {
+ fix_current_read(bq);
- length -= d;
- bq->read_index += d;
- }
-
- pa_assert(bq->blocks->index == bq->read_index);
+ if (bq->current_read) {
+ int64_t p, d;
- if (bq->blocks->chunk.length <= length) {
- /* We need to drop the full block */
+ /* We go through this piece by piece to make sure we don't
+ * drop more than allowed by prebuf */
- length -= bq->blocks->chunk.length;
- bq->read_index += bq->blocks->chunk.length;
+ p = bq->current_read->index + bq->current_read->chunk.length;
+ pa_assert(p >= bq->read_index);
+ d = p - bq->read_index;
- drop_block(bq, bq->blocks);
- } else {
- /* Only the start of this block needs to be dropped */
+ if (d > (int64_t) length)
+ d = length;
- bq->blocks->chunk.index += length;
- bq->blocks->chunk.length -= length;
- bq->blocks->index += length;
- bq->read_index += length;
- break;
- }
+ bq->read_index += d;
+ length -= d;
} else {
@@ -486,20 +551,32 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
}
}
+ drop_backlog(bq);
+
delta = bq->read_index - old;
bq->missing += delta;
}
-int pa_memblockq_is_readable(pa_memblockq *bq) {
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
pa_assert(bq);
+ pa_assert(length % bq->base == 0);
- if (memblockq_check_prebuf(bq))
- return 0;
+ /* This is kind of the inverse of pa_memblockq_drop() */
+
+ bq->read_index -= length;
+ bq->missing -= length;
+}
+
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (pa_memblockq_prebuf_active(bq))
+ return FALSE;
if (pa_memblockq_get_length(bq) <= 0)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
size_t pa_memblockq_get_length(pa_memblockq *bq) {
@@ -523,12 +600,6 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
return l >= bq->minreq ? l : 0;
}
-size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
- pa_assert(bq);
-
- return bq->minreq;
-}
-
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
int64_t old, delta;
pa_assert(bq);
@@ -552,9 +623,11 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
pa_assert_not_reached();
}
+ drop_backlog(bq);
+
delta = bq->write_index - old;
- if (delta >= bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
@@ -569,10 +642,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
int64_t old, delta;
pa_assert(bq);
- while (bq->blocks)
- drop_block(bq, bq->blocks);
-
- pa_assert(bq->n_blocks == 0);
+ pa_memblockq_silence(bq);
old = bq->write_index;
bq->write_index = bq->read_index;
@@ -581,7 +651,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
delta = bq->write_index - old;
- if (delta > bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
@@ -598,13 +668,21 @@ size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
return bq->tlength;
}
+size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->minreq;
+}
+
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
pa_assert(bq);
+
return bq->read_index;
}
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
pa_assert(bq);
+
return bq->write_index;
}
@@ -617,9 +695,6 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
if (bq->base == 1)
return pa_memblockq_push(bq, chunk);
- if (!bq->mcalign)
- bq->mcalign = pa_mcalign_new(bq->base);
-
if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
return -1;
@@ -630,23 +705,15 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
r = pa_memblockq_push(bq, &rchunk);
pa_memblock_unref(rchunk.memblock);
- if (r < 0)
+ if (r < 0) {
+ pa_mcalign_flush(bq->mcalign);
return -1;
+ }
}
return 0;
}
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
- size_t l;
- pa_assert(bq);
-
- l = pa_memblockq_get_length(bq);
-
- if (l > length)
- pa_memblockq_drop(bq, l - length);
-}
-
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
pa_assert(bq);
@@ -656,7 +723,7 @@ void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
pa_assert(bq);
- if (!bq->in_prebuf && bq->prebuf > 0)
+ if (bq->prebuf > 0)
bq->in_prebuf = TRUE;
}
@@ -688,3 +755,163 @@ size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
return l;
}
+
+void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
+ pa_assert(bq);
+
+ bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
+
+ if (bq->maxlength < bq->base)
+ bq->maxlength = bq->base;
+
+ if (bq->tlength > bq->maxlength)
+ pa_memblockq_set_tlength(bq, bq->maxlength);
+
+ if (bq->prebuf > bq->maxlength)
+ pa_memblockq_set_prebuf(bq, bq->maxlength);
+}
+
+void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
+ size_t old_tlength;
+ pa_assert(bq);
+
+ if (tlength <= 0)
+ tlength = bq->maxlength;
+
+ old_tlength = bq->tlength;
+ bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
+
+ if (bq->tlength > bq->maxlength)
+ bq->tlength = bq->maxlength;
+
+ if (bq->prebuf > bq->tlength)
+ pa_memblockq_set_prebuf(bq, bq->tlength);
+
+ if (bq->minreq > bq->tlength)
+ pa_memblockq_set_minreq(bq, bq->tlength);
+
+ bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
+}
+
+void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
+ pa_assert(bq);
+
+ if (prebuf == (size_t) -1)
+ prebuf = bq->tlength;
+
+ bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
+
+ if (prebuf > 0 && bq->prebuf < bq->base)
+ bq->prebuf = bq->base;
+
+ if (bq->prebuf > bq->tlength)
+ bq->prebuf = bq->tlength;
+
+ if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
+ bq->in_prebuf = FALSE;
+
+ if (bq->minreq > bq->prebuf)
+ pa_memblockq_set_minreq(bq, bq->prebuf);
+}
+
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq > bq->prebuf)
+ bq->minreq = bq->prebuf;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+}
+
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+ pa_assert(bq);
+
+ bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
+
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
+
+ pa_assert(bq);
+ pa_assert(source);
+
+ pa_memblockq_prebuf_disable(bq);
+
+ for (;;) {
+ pa_memchunk chunk;
+
+ if (pa_memblockq_peek(source, &chunk) < 0)
+ return 0;
+
+ pa_assert(chunk.length > 0);
+
+ if (chunk.memblock) {
+
+ if (pa_memblockq_push_align(bq, &chunk) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ return -1;
+ }
+
+ pa_memblock_unref(chunk.memblock);
+ } else
+ pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE);
+
+ pa_memblockq_drop(bq, chunk.length);
+ }
+}
+
+void pa_memblockq_willneed(pa_memblockq *bq) {
+ struct list_item *q;
+
+ pa_assert(bq);
+
+ fix_current_read(bq);
+
+ for (q = bq->current_read; q; q = q->next)
+ pa_memchunk_will_need(&q->chunk);
+}
+
+void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
+ pa_assert(bq);
+
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
+
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
+}
+
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return !bq->blocks;
+}
+
+void pa_memblockq_silence(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ while (bq->blocks)
+ drop_block(bq, bq->blocks);
+
+ pa_assert(bq->n_blocks == 0);
+}
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->n_blocks;
+}
+
+size_t pa_memblockq_get_base(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->base;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 8c3e70fc..81f7cbb8 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -1,8 +1,6 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -62,7 +60,9 @@ 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 when reading unitialized data
+ - maxrewind: how many bytes of history to keep in the queue
+
+ - silence: return this memchunk when reading unitialized data
*/
pa_memblockq* pa_memblockq_new(
int64_t idx,
@@ -71,7 +71,8 @@ pa_memblockq* pa_memblockq_new(
size_t base,
size_t prebuf,
size_t minreq,
- pa_memblock *silence);
+ size_t maxrewind,
+ pa_memchunk *silence);
void pa_memblockq_free(pa_memblockq*bq);
@@ -95,7 +96,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
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);
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq);
/* Return the length of the queue in bytes */
size_t pa_memblockq_get_length(pa_memblockq *bq);
@@ -107,6 +108,9 @@ size_t pa_memblockq_missing(pa_memblockq *bq);
* this function, reset the internal counter to 0. */
size_t pa_memblockq_pop_missing(pa_memblockq *bq);
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
+
/* Returns the minimal request value */
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
@@ -125,10 +129,8 @@ int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
/* Return the current write index */
int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
-/* Shorten the pa_memblockq to the specified length by dropping data
- * at the read end of the queue. The read index is increased until the
- * queue has the specified length */
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
+/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
/* Ignore prebuf for now */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
@@ -142,4 +144,29 @@ size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
/* Return the prebuffer length in bytes */
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
+/* Change metrics. Always call in order. */
+void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
+void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
+void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */
+void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
+void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */
+void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+
+/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
+void pa_memblockq_willneed(pa_memblockq *bq);
+
+/* Check whether the memblockq is completely empty, i.e. no data
+ * neither left nor right of the read pointer, and hence no buffered
+ * data for the future nor data in the backlog. */
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq);
+
+void pa_memblockq_silence(pa_memblockq *bq);
+
+/* Check whether we currently are in prebuf state */
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index 4e73b636..0bbf8590 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,17 +47,20 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memblock_get_length(c->memblock) >= c->index+min)
return c;
- l = c->length;
- if (l < min)
- l = min;
+ l = PA_MAX(c->length, min);
n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
- tdata = pa_memblock_acquire(n);
+
sdata = pa_memblock_acquire(c->memblock);
+ tdata = pa_memblock_acquire(n);
+
memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
- pa_memblock_release(n);
+
pa_memblock_release(c->memblock);
+ pa_memblock_release(n);
+
pa_memblock_unref(c->memblock);
+
c->memblock = n;
c->index = 0;
@@ -69,8 +70,7 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
pa_assert(c);
- c->memblock = NULL;
- c->length = c->index = 0;
+ memset(c, 0, sizeof(*c));
return c;
}
@@ -90,3 +90,23 @@ pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
return (pa_memchunk*) c;
}
+
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
+ void *p, *q;
+
+ pa_assert(dst);
+ pa_assert(src);
+ pa_assert(dst->length == src->length);
+
+ p = pa_memblock_acquire(dst->memblock);
+ q = pa_memblock_acquire(src->memblock);
+
+ memmove((uint8_t*) p + dst->index,
+ (uint8_t*) q + src->index,
+ dst->length);
+
+ pa_memblock_release(dst->memblock);
+ pa_memblock_release(src->memblock);
+
+ return dst;
+}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
index e6105ace..9458f4ff 100644
--- a/src/pulsecore/memchunk.h
+++ b/src/pulsecore/memchunk.h
@@ -1,8 +1,6 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,4 +47,7 @@ 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);
+/* Copy the data in the src memchunk to the dst memchunk */
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
+
#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 7ce3dd08..5f5902c9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -48,11 +46,17 @@ struct entry {
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
-
+
pa_assert(map);
pa_assert(key);
pa_assert(value);
+ if (pa_hashmap_get(map, key)) {
+ pa_xfree(key);
+ pa_xfree(value);
+ return -1;
+ }
+
if (valid_keys) {
const char*const* v;
for (v = valid_keys; *v; v++)
@@ -70,7 +74,7 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
-
+
return 0;
}
@@ -80,7 +84,15 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
if (args) {
- enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
+ enum {
+ WHITESPACE,
+ KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_TICKS
+ } state;
+
const char *p, *key, *value;
size_t key_len = 0, value_len = 0;
@@ -100,6 +112,8 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
case KEY:
if (*p == '=')
state = VALUE_START;
+ else if (isspace(*p))
+ goto fail;
else
key_len++;
break;
@@ -172,7 +186,7 @@ fail:
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
pa_assert(e);
-
+
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
@@ -225,7 +239,7 @@ int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
return 0;
}
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value) {
const char *v;
int r;
@@ -250,7 +264,7 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
-
+
pa_assert(ma);
pa_assert(rss);
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index aa175885..23766cfc 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -1,8 +1,6 @@
#ifndef foomodargshfoo
#define foomodargshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,6 +26,7 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
typedef struct pa_modargs pa_modargs;
@@ -44,7 +43,7 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de
/* Return a module argument as unsigned 32bit value in *value */
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);
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *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);
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 9f53887b..ac4ca88a 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,10 +38,12 @@
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
pa_modinfo *i;
const char* (*func)(void);
+ pa_bool_t (*func2) (void);
pa_assert(dl);
@@ -61,13 +61,16 @@ pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
+ if ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
+ i->load_once = func2();
+
return i;
}
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
-
+
pa_assert(name);
if (!(dl = lt_dlopenext(name))) {
@@ -83,7 +86,7 @@ pa_modinfo *pa_modinfo_get_by_name(const char *name) {
void pa_modinfo_free(pa_modinfo *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 02e536c6..605637c4 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -1,8 +1,6 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,12 +23,14 @@
***/
/* Some functions for reading module meta data from PulseAudio modules */
+#include <pulsecore/macro.h>
typedef struct pa_modinfo {
char *author;
char *description;
char *usage;
char *version;
+ pa_bool_t load_once;
} pa_modinfo;
/* Read meta data from an libtool handle */
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index e6d7fcaf..f1eeb762 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,6 +44,7 @@
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define UNLOAD_POLL_TIME 2
@@ -66,6 +65,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
+ pa_bool_t (*load_once)(void);
pa_assert(c);
pa_assert(name);
@@ -82,6 +82,22 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
goto fail;
}
+ if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
+
+ if (load_once() && c->modules) {
+ pa_module *i;
+ uint32_t idx;
+ /* OK, the module only wants to be loaded once, let's make sure it is */
+
+ for (i = pa_idxset_first(c->modules, &idx); i; i = pa_idxset_next(c->modules, &idx)) {
+ if (strcmp(name, i->name) == 0) {
+ pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
+ goto fail;
+ }
+ }
+ }
+ }
+
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;
@@ -91,8 +107,8 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
m->userdata = NULL;
m->core = c;
m->n_used = -1;
- m->auto_unload = 0;
- m->unload_requested = 0;
+ m->auto_unload = FALSE;
+ m->unload_requested = FALSE;
if (m->init(m) < 0) {
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
@@ -102,7 +118,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
- if (!c->module_auto_unload_event) {
+ if (m->auto_unload && !c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
@@ -186,7 +202,7 @@ static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
void pa_module_unload_all(pa_core *c) {
pa_module *m;
-
+
pa_assert(c);
if (!c->modules)
@@ -212,7 +228,7 @@ 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;
-
+
pa_assert(m);
pa_assert(del);
pa_assert(now);
@@ -263,7 +279,7 @@ static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
void pa_module_unload_request(pa_module *m) {
pa_assert(m);
- m->unload_requested = 1;
+ m->unload_requested = TRUE;
if (!m->core->module_defer_unload_event)
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 7a93a071..ec582f25 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -1,8 +1,6 @@
#ifndef foomodulehfoo
#define foomodulehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -45,10 +43,10 @@ struct pa_module {
void *userdata;
int n_used;
- int auto_unload;
+ pa_bool_t auto_unload;
time_t last_used_time;
- int unload_requested;
+ pa_bool_t unload_requested;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
@@ -62,10 +60,25 @@ 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_VERSION(s) const char * pa__get_version(void) { return s; }
+#define PA_MODULE_AUTHOR(s) \
+ const char *pa__get_author(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DESCRIPTION(s) \
+ const char *pa__get_description(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_USAGE(s) \
+ const char *pa__get_usage(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_VERSION(s) \
+ const char * pa__get_version(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_LOAD_ONCE(b) \
+ pa_bool_t pa__load_once(void) { return b; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
pa_modinfo *pa_module_get_info(pa_module *m);
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index f54e69f2..81417ea4 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
index 8221cc33..1a43fa35 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -1,8 +1,6 @@
#ifndef foopulsemsgobjecthfoo
#define foopulsemsgobjecthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 805f11de..35465b7b 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,7 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
int r;
pa_assert_se(pthread_mutexattr_init(&attr) == 0);
-
+
if (recursive)
pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
@@ -60,9 +58,9 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
m = pa_xnew(pa_mutex, 1);
-#ifndef HAVE_PTHREAD_PRIO_INHERIT
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
-
+
#else
if ((r = pthread_mutex_init(&m->mutex, &attr))) {
@@ -74,8 +72,8 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
}
-#endif
-
+#endif
+
return m;
}
@@ -92,6 +90,18 @@ void pa_mutex_lock(pa_mutex *m) {
pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
}
+pa_bool_t pa_mutex_try_lock(pa_mutex *m) {
+ int r;
+ pa_assert(m);
+
+ if ((r = pthread_mutex_trylock(&m->mutex)) != 0) {
+ pa_assert(r == EBUSY);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
void pa_mutex_unlock(pa_mutex *m) {
pa_assert(m);
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
index 77d63d15..5e884e7f 100644
--- a/src/pulsecore/mutex-win32.c
+++ b/src/pulsecore/mutex-win32.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index 72e88781..36e1d635 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -1,8 +1,6 @@
#ifndef foopulsemutexhfoo
#define foopulsemutexhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,6 +35,7 @@ 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);
+pa_bool_t pa_mutex_try_lock(pa_mutex *m);
void pa_mutex_unlock(pa_mutex *m);
typedef struct pa_cond pa_cond;
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index fe2be467..cc18adab 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,20 +54,20 @@ static int is_valid_char(char c) {
c == '_';
}
-static int is_valid_name(const char *name) {
+pa_bool_t pa_namereg_is_valid_name(const char *name) {
const char *c;
if (*name == 0)
- return 0;
+ return FALSE;
for (c = name; *c && (c-name < PA_NAME_MAX); c++)
if (!is_valid_char(*c))
- return 0;
+ return FALSE;
if (*c)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
static char* cleanup_name(const char *name) {
@@ -111,7 +109,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
return NULL;
if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
- !is_valid_name(name) ) {
+ !pa_namereg_is_valid_name(name) ) {
if (fail)
return NULL;
@@ -179,7 +177,7 @@ void pa_namereg_unregister(pa_core *c, const char *name) {
pa_xfree(e);
}
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
struct namereg_entry *e;
uint32_t idx;
pa_assert(c);
@@ -253,7 +251,7 @@ int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type)
if (name && *s && !strcmp(name, *s))
return 0;
- if (!is_valid_name(name))
+ if (!pa_namereg_is_valid_name(name))
return -1;
pa_xfree(*s);
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 350ba0f6..af0153ec 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -1,8 +1,6 @@
#ifndef foonamereghfoo
#define foonamereghfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,6 +23,7 @@
***/
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
#define PA_NAME_MAX 128
@@ -38,10 +37,12 @@ void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
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);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
const char *pa_namereg_get_default_sink_name(pa_core *c);
const char *pa_namereg_get_default_source_name(pa_core *c);
+pa_bool_t pa_namereg_is_valid_name(const char *name);
+
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 9defc4a5..809d6c75 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -1,8 +1,6 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -36,10 +34,10 @@ enum {
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
- /* Commands from client to server */
- PA_COMMAND_CREATE_PLAYBACK_STREAM,
+ /* CLIENT->SERVER */
+ PA_COMMAND_CREATE_PLAYBACK_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_PLAYBACK_STREAM,
- PA_COMMAND_CREATE_RECORD_STREAM,
+ PA_COMMAND_CREATE_RECORD_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_AUTH,
@@ -64,8 +62,8 @@ enum {
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
- PA_COMMAND_GET_SINK_INPUT_INFO,
- PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
+ PA_COMMAND_GET_SINK_INPUT_INFO, /* Payload changed in v11 (0.9.7) */
+ PA_COMMAND_GET_SINK_INPUT_INFO_LIST, /* Payload changed in v11 (0.9.7) */
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
@@ -92,18 +90,21 @@ enum {
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
+
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
+
PA_COMMAND_ADD_AUTOLOAD,
PA_COMMAND_REMOVE_AUTOLOAD,
PA_COMMAND_GET_AUTOLOAD_INFO,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
- /* Commands from server to client */
+ /* SERVER->CLIENT */
PA_COMMAND_REQUEST,
PA_COMMAND_OVERFLOW,
PA_COMMAND_UNDERFLOW,
@@ -112,14 +113,41 @@ enum {
PA_COMMAND_SUBSCRIBE_EVENT,
/* A few more client->server commands */
+
+ /* Supported since protocol v10 (0.9.5) */
PA_COMMAND_MOVE_SINK_INPUT,
PA_COMMAND_MOVE_SOURCE_OUTPUT,
+ /* Supported since protocol v11 (0.9.7) */
PA_COMMAND_SET_SINK_INPUT_MUTE,
PA_COMMAND_SUSPEND_SINK,
PA_COMMAND_SUSPEND_SOURCE,
+ /* Supported since protocol v12 (0.9.8) */
+ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
+
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+ PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
+ PA_COMMAND_RECORD_STREAM_SUSPENDED,
+ PA_COMMAND_PLAYBACK_STREAM_MOVED,
+ PA_COMMAND_RECORD_STREAM_MOVED,
+
+ /* Supported since protocol v13 (0.9.10) */
+ PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_CLIENT_PROPLIST,
+ PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_STARTED,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index 6680d078..9a2f28f3 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,7 +37,7 @@ pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*chec
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;
@@ -67,6 +65,6 @@ void pa_object_unref(pa_object *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
index 562fd113..7dcfa2eb 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -1,8 +1,6 @@
#ifndef foopulseobjecthfoo
#define foopulseobjecthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
index 198bd41e..989741dc 100644
--- a/src/pulsecore/once.c
+++ b/src/pulsecore/once.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,7 +41,7 @@ int pa_once_begin(pa_once *control) {
/* 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))) {
@@ -69,7 +67,7 @@ int pa_once_begin(pa_once *control) {
void pa_once_end(pa_once *control) {
pa_mutex *m;
-
+
pa_assert(control);
pa_atomic_store(&control->done, 1);
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index a4a0b239..576d40fa 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -1,8 +1,6 @@
#ifndef foopulseoncehfoo
#define foopulseoncehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,18 +53,18 @@ void pa_once_end(pa_once *o);
} while(0)
/*
-
+
Usage of these macros is like this:
-
+
void foo() {
-
+
PA_ONCE_BEGIN {
-
+
... stuff to be called just once ...
-
+
} PA_ONCE_END;
}
-
+
*/
/* Same API but calls a function */
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
index 2706efea..cee468bd 100644
--- a/src/pulsecore/packet.c
+++ b/src/pulsecore/packet.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
index bcac4a7f..5989b1fa 100644
--- a/src/pulsecore/packet.h
+++ b/src/pulsecore/packet.h
@@ -1,8 +1,6 @@
#ifndef foopackethfoo
#define foopackethfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 0df37f4e..f2b6b2cf 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,7 @@
static char *parse_host(const char *s, uint16_t *ret_port) {
pa_assert(s);
pa_assert(ret_port);
-
+
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
@@ -72,10 +70,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;
-
+
pa_assert(name);
pa_assert(ret_p);
-
+
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
@@ -103,9 +101,12 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
else if (pa_startswith(p, "unix:")) {
ret_p->type = PA_PARSED_ADDRESS_UNIX;
p += sizeof("unix:")-1;
- } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
+ } else if (pa_startswith(p, "tcp:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP4;
p += sizeof("tcp:")-1;
+ } else if (pa_startswith(p, "tcp4:")) {
+ ret_p->type = PA_PARSED_ADDRESS_TCP4;
+ p += sizeof("tcp4:")-1;
} else if (pa_startswith(p, "tcp6:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP6;
p += sizeof("tcp6:")-1;
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index fd7cad3b..5fbcb9a7 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 737b1b98..e6a6ae4d 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,6 +36,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
#include "pdispatch.h"
@@ -98,6 +97,8 @@ static const char *command_names[PA_COMMAND_MAX] = {
#endif
+PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
+
struct reply_info {
pa_pdispatch *pdispatch;
PA_LLIST_FIELDS(struct reply_info);
@@ -129,7 +130,8 @@ static void reply_info_free(struct reply_info *r) {
PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
- pa_xfree(r);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
+ pa_xfree(r);
}
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
@@ -190,7 +192,7 @@ 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;
-
+
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
pa_assert(packet);
@@ -255,7 +257,7 @@ 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;
-
+
pa_assert(r);
pa_assert(r->time_event == e);
pa_assert(r->pdispatch);
@@ -268,12 +270,14 @@ 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;
-
+
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
pa_assert(cb);
- r = pa_xnew(struct reply_info, 1);
+ if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
+ r = pa_xnew(struct reply_info, 1);
+
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index de0aa3ec..5c31d80e 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -1,8 +1,6 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 55ff2088..addb17cc 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,6 +40,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@@ -139,20 +138,64 @@ fail:
return -1;
}
+static int proc_name_ours(pid_t pid, const char *procname) {
+#ifdef __linux__
+ char bn[PATH_MAX];
+ FILE *f;
+
+ pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
+
+ if (!(f = fopen(bn, "r"))) {
+ pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
+ return -1;
+ } else {
+ char *expected;
+ pa_bool_t good;
+ char stored[64];
+
+ if (!(fgets(stored, sizeof(stored), f))) {
+ pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+
+ expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+
+#if !defined(__OPTIMIZE__)
+ if (!good) {
+ /* libtool likes to rename our binary names ... */
+ expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+ }
+#endif
+
+ return !!good;
+ }
+#endif
+
+ return 1;
+}
+
/* Create a new PID file for the current process. */
-int pa_pid_file_create(void) {
+int pa_pid_file_create(const char *procname) {
int fd = -1;
int ret = -1;
- char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
+ char *fn;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
@@ -160,14 +203,24 @@ int pa_pid_file_create(void) {
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
pa_log_warn("Corrupt PID file, overwriting.");
else if (pid > 0) {
+
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
CloseHandle(process);
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
- pa_log("Daemon already running.");
- goto fail;
+ int ours = 1;
+
+ if (procname)
+ if ((ours = proc_name_ours(pid, procname)) < 0)
+ goto fail;
+
+ if (ours) {
+ pa_log("Daemon already running.");
+ ret = 1;
+ goto fail;
+ }
}
pa_log_warn("Stale PID file, overwriting.");
@@ -199,17 +252,20 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
@@ -231,7 +287,7 @@ int pa_pid_file_remove(void) {
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
fd = -1;
#endif
@@ -253,6 +309,8 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
@@ -260,8 +318,8 @@ fail:
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
-int pa_pid_file_check_running(pid_t *pid) {
- return pa_pid_file_kill(0, pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+ return pa_pid_file_kill(0, pid, procname);
}
#ifndef OS_IS_WIN32
@@ -269,16 +327,20 @@ int pa_pid_file_check_running(pid_t *pid) {
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t _pid;
+#ifdef __linux__
+ char *e = NULL;
+#endif
if (!pid)
pid = &_pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
@@ -286,6 +348,16 @@ int pa_pid_file_kill(int sig, pid_t *pid) {
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
+ if (procname) {
+ int ours;
+
+ if ((ours = proc_name_ours(*pid, procname)) < 0)
+ goto fail;
+
+ if (!ours)
+ goto fail;
+ }
+
ret = kill(*pid, sig);
fail:
@@ -295,13 +367,19 @@ fail:
pa_close(fd);
}
+#ifdef __linux__
+ pa_xfree(e);
+#endif
+
+ pa_xfree(fn);
+
return ret;
}
#else /* OS_IS_WIN32 */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
return -1;
}
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
index 0f25d1c8..3c8a9de3 100644
--- a/src/pulsecore/pid.h
+++ b/src/pulsecore/pid.h
@@ -1,8 +1,6 @@
#ifndef foopidhfoo
#define foopidhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,9 +22,9 @@
USA.
***/
-int pa_pid_file_create(void);
+int pa_pid_file_create(const char *procname);
int pa_pid_file_remove(void);
-int pa_pid_file_check_running(pid_t *pid);
-int pa_pid_file_kill(int sig, pid_t *pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname);
#endif
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
index e614c9c6..93d78a22 100644
--- a/src/pulsecore/pipe.c
+++ b/src/pulsecore/pipe.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
index e013a2e7..9a7e62c8 100644
--- a/src/pulsecore/pipe.h
+++ b/src/pulsecore/pipe.h
@@ -1,8 +1,6 @@
#ifndef foopipehfoo
#define foopipehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 5652ac2b..8b3e79b9 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -30,10 +28,11 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/sample-util.h>
#include "play-memblockq.h"
@@ -59,10 +58,9 @@ static void memblockq_stream_unlink(memblockq_stream *u) {
return;
pa_sink_input_unlink(u->sink_input);
-
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
-
+
memblockq_stream_unref(u);
}
@@ -70,8 +68,6 @@ static void memblockq_stream_free(pa_object *o) {
memblockq_stream *u = MEMBLOCKQ_STREAM(o);
pa_assert(u);
- memblockq_stream_unlink(u);
-
if (u->memblockq)
pa_memblockq_free(u->memblockq);
@@ -81,7 +77,7 @@ static void memblockq_stream_free(pa_object *o) {
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);
@@ -92,15 +88,34 @@ static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata
}
static void sink_input_kill_cb(pa_sink_input *i) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ memblockq_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ memblockq_stream *u;
+
pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
- memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
memblockq_stream *u;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
pa_assert(chunk);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
@@ -109,36 +124,58 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun
return -1;
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);
+
+ if (pa_sink_input_safe_to_remove(i)) {
+
+ 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;
}
+ chunk->length = PA_MIN(chunk->length, nbytes);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
memblockq_stream *u;
- pa_assert(i);
- pa_assert(length > 0);
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
if (!u->memblockq)
return;
-
- pa_memblockq_drop(u->memblockq, length);
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
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) {
+ pa_cvolume *volume,
+ pa_proplist *p) {
memblockq_stream *u = NULL;
pa_sink_input_new_data data;
@@ -149,48 +186,43 @@ pa_sink_input* pa_memblockq_sink_input_new(
/* 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 NULL;
- }
-
- if (volume && pa_cvolume_is_muted(volume)) {
- pa_memblockq_free(q);
- 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;
+ u->memblockq = NULL;
pa_sink_input_new_data_init(&data);
data.sink = sink;
- data.name = name;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ if (!u->sink_input)
goto fail;
-
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
+
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
if (q)
- pa_memblockq_prebuf_disable(q);
-
+ pa_memblockq_sink_input_set_queue(u->sink_input, q);
+
/* The reference to u is dangling here, because we want
* to keep this stream around until it is fully played. */
/* This sink input is not "put" yet, i.e. pa_sink_input_put() has
* not been called! */
-
+
return pa_sink_input_ref(u->sink_input);
fail:
@@ -202,11 +234,12 @@ fail:
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_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
pa_sink_input *i;
@@ -214,10 +247,14 @@ int pa_play_memblockq(
pa_assert(ss);
pa_assert(q);
- if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume)))
+ if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p)))
return -1;
pa_sink_input_put(i);
+
+ if (sink_input_index)
+ *sink_input_index = i->index;
+
pa_sink_input_unref(i);
return 0;
@@ -225,12 +262,17 @@ int pa_play_memblockq(
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;
+
+ if ((u->memblockq = q)) {
+ pa_memblockq_set_prebuf(q, 0);
+ pa_memblockq_set_silence(q, NULL);
+ pa_memblockq_willneed(q);
+ }
}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
index d8790316..1a42867b 100644
--- a/src/pulsecore/play-memblockq.h
+++ b/src/pulsecore/play-memblockq.h
@@ -1,8 +1,6 @@
#ifndef fooplaymemblockqhfoo
#define fooplaymemblockqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,20 +27,21 @@
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);
+ pa_cvolume *volume,
+ pa_proplist *p);
void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
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 *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
index fd931a24..0dd48251 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -30,167 +28,37 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/play-memblockq.h>
#include "play-memchunk.h"
-typedef struct memchunk_stream {
- pa_msgobject parent;
- pa_core *core;
- pa_sink_input *sink_input;
- pa_memchunk memchunk;
-} memchunk_stream;
-
-enum {
- MEMCHUNK_STREAM_MESSAGE_UNLINK,
-};
-
-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 void memchunk_stream_free(pa_object *o) {
- memchunk_stream *u = MEMCHUNK_STREAM(o);
- pa_assert(u);
-
- 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);
-
- switch (code) {
- case MEMCHUNK_STREAM_MESSAGE_UNLINK:
- memchunk_stream_unlink(u);
- break;
- }
-
- return 0;
-}
-
-static void sink_input_kill_cb(pa_sink_input *i) {
- pa_sink_input_assert_ref(i);
-
- memchunk_stream_unlink(MEMCHUNK_STREAM(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);
-
- 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;
- }
-
- pa_assert(u->memchunk.memblock);
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
-
- 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(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *volume) {
+ pa_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
- memchunk_stream *u = NULL;
- pa_sink_input_new_data data;
+ pa_memblockq *q;
+ int r;
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.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);
+ q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL);
+ pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
- 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;
-
- pa_sink_input_put(u->sink_input);
+ if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
+ pa_memblockq_free(q);
+ return r;
+ }
- /* 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/play-memchunk.h b/src/pulsecore/play-memchunk.h
index 5afb094c..c312ae82 100644
--- a/src/pulsecore/play-memchunk.h
+++ b/src/pulsecore/play-memchunk.h
@@ -1,8 +1,6 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,10 +27,11 @@
int pa_play_memchunk(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 288f7dfb..88ac21e4 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
index 6be6069b..86c37a08 100644
--- a/src/pulsecore/poll.h
+++ b/src/pulsecore/poll.h
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644
index 00000000..6005775e
--- /dev/null
+++ b/src/pulsecore/proplist-util.c
@@ -0,0 +1,118 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#include <pulse/proplist.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+
+#include "proplist-util.h"
+
+void pa_init_proplist(pa_proplist *p) {
+ int a, b;
+#ifndef HAVE_DECL_ENVIRON
+ extern char **environ;
+#endif
+ char **e;
+
+ pa_assert(p);
+
+ for (e = environ; *e; e++) {
+
+ if (pa_startswith(*e, "PULSE_PROP_")) {
+ size_t kl = strcspn(*e+11, "=");
+ char *k;
+
+ if ((*e)[11+kl] != '=')
+ continue;
+
+ if (!pa_utf8_valid(*e+11+kl+1))
+ continue;
+
+ k = pa_xstrndup(*e+11, kl);
+
+ if (pa_proplist_contains(p, k)) {
+ pa_xfree(k);
+ continue;
+ }
+
+ pa_proplist_sets(p, k, *e+11+kl+1);
+ pa_xfree(k);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+ char t[32];
+ pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
+ char t[64];
+ if (pa_get_user_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
+ char t[64];
+ if (pa_get_host_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
+ pa_xfree(c);
+ }
+ }
+
+ a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
+ b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
+
+ if (!a || !b) {
+ char t[PATH_MAX];
+ if (pa_get_binary_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+
+ if (!a)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+ if (!b)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+ const char *l;
+
+ if ((l = setlocale(LC_MESSAGES, NULL)))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+ }
+}
diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h
new file mode 100644
index 00000000..c6bdc103
--- /dev/null
+++ b/src/pulsecore/proplist-util.h
@@ -0,0 +1,29 @@
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
+
+#endif
diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c
index fc3dce9e..23d432ec 100644
--- a/src/pulsecore/props.c
+++ b/src/pulsecore/props.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,7 +37,7 @@ typedef struct pa_property {
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
-
+
pa_assert(name);
pa_assert(data);
@@ -60,7 +58,7 @@ static void property_free(pa_property *p) {
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(c->properties);
@@ -73,7 +71,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(data);
@@ -89,7 +87,7 @@ 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;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(c->properties);
@@ -123,7 +121,7 @@ void pa_property_cleanup(pa_core *c) {
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
-
+
pa_assert(c);
pa_assert(s);
diff --git a/src/pulsecore/props.h b/src/pulsecore/props.h
index 880325f6..95db229b 100644
--- a/src/pulsecore/props.h
+++ b/src/pulsecore/props.h
@@ -1,8 +1,6 @@
#ifndef foopropshfoo
#define foopropshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index d9d00734..30cb475d 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -48,7 +46,7 @@ struct pa_protocol_cli {
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
pa_assert(p);
-
+
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
@@ -56,7 +54,7 @@ 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;
-
+
pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -82,7 +80,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
p = pa_xnew(pa_protocol_cli, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
@@ -92,7 +90,7 @@ 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) {
pa_assert(p);
-
+
pa_cli_free(p);
}
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
index 3870def3..8922ac62 100644
--- a/src/pulsecore/protocol-cli.h
+++ b/src/pulsecore/protocol-cli.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 76ba9dd0..db1b4305 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -70,9 +68,11 @@
#define PLAYBACK_BUFFER_SECONDS (.25)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
-#define MAX_CACHE_SAMPLE_SIZE (1024000)
+#define MAX_CACHE_SAMPLE_SIZE (2048000)
+
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
#define SCACHE_PREFIX "esound."
@@ -102,8 +102,9 @@ typedef struct connection {
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
struct {
@@ -122,7 +123,7 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_esound {
pa_module *module;
pa_core *core;
- int public;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
@@ -149,8 +150,9 @@ typedef struct proto_handler {
const char *description;
} esd_proto_handler_info_t;
-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 int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_kill_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);
@@ -398,8 +400,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
}
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
@@ -410,34 +411,39 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
pa_assert(!c->sink_input && !c->input_memblockq);
pa_sink_input_new_data_init(&sdata);
- sdata.sink = sink;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
+ sdata.sink = sink;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+ pa_sink_input_new_data_done(&sdata);
+
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
- c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+ pa_iochannel_socket_set_rcvbuf(c->io, l);
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->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
@@ -497,8 +503,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
}
}
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
@@ -509,32 +514,37 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
pa_assert(!c->output_memblockq && !c->source_output);
pa_source_output_new_data_init(&sdata);
- sdata.source = source;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
+ sdata.source = source;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&sdata, &ss);
+
+ c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+ pa_source_output_new_data_done(&sdata);
- c->source_output = pa_source_output_new(c->protocol->core, &sdata, 9);
- CHECK_VALIDITY(c->source_output, "Failed to create source_output.");
+ CHECK_VALIDITY(c->source_output, "Failed to create source output.");
l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
+ pa_iochannel_socket_set_sndbuf(c->io, l);
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
@@ -638,8 +648,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
if (conn->original_name)
strncpy(name, conn->original_name, ESD_NAME_MAX);
- else if (conn->client && conn->client->name)
- strncpy(name, conn->client->name, ESD_NAME_MAX);
+ else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
+ strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
connection_write(c, name, ESD_NAME_MAX);
/* rate */
@@ -785,8 +795,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
@@ -800,7 +809,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
c->state = ESD_CACHING_SAMPLE;
- pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
+ pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
idx += 1;
connection_write(c, &idx, sizeof(uint32_t));
@@ -818,8 +827,7 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ
pa_assert(length == ESD_NAME_MAX);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
@@ -851,7 +859,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
pa_sink *sink;
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
- if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
ok = idx + 1;
} else {
pa_assert(request == ESD_PROTO_SAMPLE_FREE);
@@ -992,7 +1000,7 @@ static int do_read(connection *c) {
uint32_t idx;
c->scache.memchunk.index = 0;
- pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
+ pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
pa_memblock_unref(c->scache.memchunk.memblock);
c->scache.memchunk.memblock = NULL;
@@ -1012,6 +1020,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
+ size_t space;
pa_assert(c->input_memblockq);
@@ -1020,21 +1029,26 @@ static int do_read(connection *c) {
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) {
+
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
- if (c->playback.current_memblock)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
+ }
if (!c->playback.current_memblock) {
- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
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);
@@ -1122,12 +1136,11 @@ static void do_work(connection *c) {
if (c->dead)
return;
- if (pa_iochannel_is_readable(c->io)) {
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- }
- if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
+ if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
/* In case we are in capture mode we will never call read()
* on the socket, hence we need to detect the hangup manually
* here, instead of simply waiting for read() to return 0. */
@@ -1212,15 +1225,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* New data from the main loop */
pa_memblockq_push_align(c->input_memblockq, chunk);
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
return 0;
}
- case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+ 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;
@@ -1237,41 +1254,64 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
connection*c;
- int r;
- pa_assert(i);
+ pa_sink_input_assert_ref(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);
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+ c->playback.underrun = TRUE;
+
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+ return -1;
+ } else {
+ size_t m;
+
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- return r;
+ return 0;
+ }
}
/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
- connection*c;
- size_t old, new;
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
- pa_assert(length);
- /* pa_log("DROP"); */
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- old = pa_memblockq_missing(c->input_memblockq);
- pa_memblockq_drop(c->input_memblockq, length);
- new = pa_memblockq_missing(c->input_memblockq);
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
- 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 thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
static void sink_input_kill_cb(pa_sink_input *i) {
@@ -1286,7 +1326,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
connection *c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
pa_assert(chunk);
@@ -1303,7 +1343,7 @@ static void source_output_kill_cb(pa_source_output *o) {
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
connection*c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
@@ -1349,7 +1389,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- c->client->owner = p->module;
+ pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
@@ -1374,11 +1415,10 @@ 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;
+ c->playback.underrun = TRUE;
pa_atomic_store(&c->playback.missing, 0);
- c->scache.memchunk.length = c->scache.memchunk.index = 0;
- c->scache.memchunk.memblock = NULL;
+ pa_memchunk_reset(&c->scache.memchunk);
c->scache.name = NULL;
c->original_name = NULL;
@@ -1406,7 +1446,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_esound *p = NULL;
- int public = 0;
+ pa_bool_t public = FALSE;
const char *acl;
pa_assert(core);
@@ -1436,7 +1476,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
p->core = core;
p->module = m;
p->public = public;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
p->connections = pa_idxset_new(NULL, NULL);
@@ -1459,7 +1499,8 @@ void pa_protocol_esound_free(pa_protocol_esound *p) {
connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
- pa_socket_server_unref(p->server);
+ if (p->server)
+ pa_socket_server_unref(p->server);
if (p->auth_ip_acl)
pa_ip_acl_free(p->auth_ip_acl);
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
index 868ef5d2..0c9447d3 100644
--- a/src/pulsecore/protocol-esound.h
+++ b/src/pulsecore/protocol-esound.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 4a2836e7..03990435 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -65,7 +63,7 @@ struct pa_protocol_http {
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
-
+
pa_assert(c);
pa_assert(msg);
pa_assert(mime);
@@ -111,7 +109,7 @@ static void connection_free(struct connection *c, int del) {
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
-
+
pa_ioline_unref(c->line);
pa_xfree(c);
}
@@ -168,7 +166,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
- PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
+ PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
@@ -225,7 +223,7 @@ fail:
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
-
+
pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -248,14 +246,14 @@ 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;
-
+
pa_core_assert_ref(core);
pa_assert(server);
p = pa_xnew(pa_protocol_http, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index cf952476..e3372335 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 9ae0f083..923a5665 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,6 +69,9 @@
#define MAX_CONNECTIONS 64
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
+#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20 /* 20ms */
+#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
typedef struct connection connection;
struct pa_protocol_native;
@@ -84,6 +85,7 @@ typedef struct record_stream {
pa_source_output *source_output;
pa_memblockq *memblockq;
size_t fragment_size;
+ pa_usec_t source_latency;
} record_stream;
typedef struct output_stream {
@@ -98,17 +100,17 @@ typedef struct playback_stream {
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- int drain_request;
+ pa_bool_t drain_request;
uint32_t drain_tag;
uint32_t syncid;
- int underrun;
pa_atomic_t missing;
size_t minreq;
+ pa_usec_t sink_latency;
/* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
int64_t read_index, write_index;
- size_t resampled_chunk_length;
+ size_t render_memblockq_length;
} playback_stream;
typedef struct upload_stream {
@@ -122,12 +124,14 @@ typedef struct upload_stream {
char *name;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+ pa_proplist *proplist;
} upload_stream;
struct connection {
pa_msgobject parent;
- int authorized;
+ pa_bool_t authorized:1;
+ pa_bool_t is_local:1;
uint32_t version;
pa_protocol_native *protocol;
pa_client *client;
@@ -162,11 +166,11 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_native {
pa_module *module;
pa_core *core;
- int public;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
+ pa_bool_t auth_cookie_in_property;
#ifdef HAVE_CREDS
char *auth_group;
#endif
@@ -187,7 +191,8 @@ 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
+ PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+ PLAYBACK_STREAM_MESSAGE_STARTED
};
enum {
@@ -199,15 +204,21 @@ enum {
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 int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_kill_cb(pa_sink_input *i);
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
+static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+
static void 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 void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
+static void source_output_moved_cb(pa_source_output *o);
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);
@@ -248,6 +259,10 @@ static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint3
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 void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_proplist(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,
@@ -323,7 +338,21 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
- [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream
+ [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+ [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+ [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+ [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+
+ [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
+
+ [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
};
/* structure management */
@@ -347,6 +376,9 @@ static void upload_stream_free(pa_object *o) {
pa_xfree(s->name);
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
if (s->memchunk.memblock)
pa_memblock_unref(s->memchunk.memblock);
@@ -357,7 +389,9 @@ static upload_stream* upload_stream_new(
connection *c,
const pa_sample_spec *ss,
const pa_channel_map *map,
- const char *name, size_t length) {
+ const char *name,
+ size_t length,
+ pa_proplist *p) {
upload_stream *s;
@@ -365,6 +399,7 @@ static upload_stream* upload_stream_new(
pa_assert(ss);
pa_assert(name);
pa_assert(length > 0);
+ pa_assert(p);
s = pa_msgobject_new(upload_stream);
s->parent.parent.parent.free = upload_stream_free;
@@ -374,6 +409,8 @@ static upload_stream* upload_stream_new(
s->name = pa_xstrdup(name);
pa_memchunk_reset(&s->memchunk);
s->length = length;
+ s->proplist = pa_proplist_copy(p);
+ pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_idxset_put(c->output_streams, s, &s->index);
@@ -432,15 +469,72 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
return 0;
}
+static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+
+ if (*fragsize <= 0)
+ *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+
+ if (adjust_latency) {
+ pa_usec_t fragsize_usec;
+
+ /* So, the user asked us to adjust the latency according to
+ * what the source can provide. Half the latency will be
+ * spent on the hw buffer, half of it in the async buffer
+ * queue we maintain for each client. */
+
+ fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+ s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
+
+ if (fragsize_usec >= s->source_latency*2)
+ fragsize_usec -= s->source_latency;
+ else
+ fragsize_usec = s->source_latency;
+
+ *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ } else
+ s->source_latency = 0;
+}
+
+static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) {
+ size_t base;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+ base = pa_frame_size(&s->source_output->sample_spec);
+
+ s->fragment_size = (*fragsize/base)*base;
+ if (s->fragment_size <= 0)
+ s->fragment_size = base;
+
+ if (s->fragment_size > *maxlength)
+ s->fragment_size = *maxlength;
+
+ *fragsize = s->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,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ pa_bool_t peak_detect,
uint32_t *maxlength,
- uint32_t fragment_size,
- int corked) {
+ uint32_t *fragsize,
+ pa_source_output_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency,
+ pa_sink_input *direct_on_input) {
record_stream *s;
pa_source_output *source_output;
@@ -449,20 +543,28 @@ static record_stream* record_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
- pa_assert(*maxlength > 0);
+ pa_assert(p);
pa_source_output_new_data_init(&data);
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ data.driver = __FILE__;
data.module = c->protocol->module;
data.client = c->client;
data.source = source;
- data.driver = __FILE__;
- data.name = name;
+ data.direct_on_input = direct_on_input;
pa_source_output_new_data_set_sample_spec(&data, ss);
pa_source_output_new_data_set_channel_map(&data, map);
+ if (peak_detect)
+ data.resample_method = PA_RESAMPLER_PEAKS;
+
+ source_output = pa_source_output_new(c->protocol->core, &data, flags);
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))
+ pa_source_output_new_data_done(&data);
+
+ if (!source_output)
return NULL;
s = pa_msgobject_new(record_stream);
@@ -470,27 +572,38 @@ static record_stream* record_stream_new(
s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
+
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->get_latency = source_output_get_latency_cb;
+ s->source_output->moved = source_output_moved_cb;
+ s->source_output->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
+ fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize);
+
s->memblockq = pa_memblockq_new(
0,
*maxlength,
0,
- base = pa_frame_size(ss),
+ base = pa_frame_size(&source_output->sample_spec),
1,
0,
+ 0,
NULL);
- s->fragment_size = (fragment_size/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
- *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+ fix_record_buffer_attr_post(s, maxlength, fragsize);
+
+ *ss = s->source_output->sample_spec;
+ *map = s->source_output->channel_map;
pa_idxset_put(c->record_streams, s, &s->index);
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->source_latency / PA_USEC_PER_MSEC);
+
pa_source_output_put(s->source_output);
return s;
}
@@ -538,21 +651,14 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
uint32_t l = 0;
for (;;) {
- int32_t k;
-
- if ((k = pa_atomic_load(&s->missing)) <= 0)
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
break;
- l += k;
-
- if (l < s->minreq)
- break;
-
- if (pa_atomic_sub(&s->missing, k) <= k)
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
break;
}
- if (l < s->minreq)
+ if (l <= 0)
break;
t = pa_tagstruct_new(NULL, 0);
@@ -562,7 +668,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
pa_tagstruct_putu32(t, l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
-/* pa_log("Requesting %u bytes", l); */
+/* pa_log("Requesting %lu bytes", (unsigned long) l); */
break;
}
@@ -590,41 +696,172 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
break;
}
+ case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+ 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_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
-
}
return 0;
}
+static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ size_t frame_size;
+ pa_usec_t tlength_usec, minreq_usec, sink_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*tlength <= 0)
+ *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+
+ frame_size = pa_frame_size(&s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = frame_size;
+ if (*tlength < *minreq+frame_size)
+ *tlength = *minreq+frame_size;
+
+ tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+ minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+
+ pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+ (double) tlength_usec / PA_USEC_PER_MSEC,
+ (double) minreq_usec / PA_USEC_PER_MSEC);
+
+ if (adjust_latency) {
+
+ /* So, the user asked us to adjust the latency of the stream
+ * buffer according to the what the sink can provide. The
+ * tlength passed in shall be the overall latency. Roughly
+ * half the latency will be spent on the hw buffer, the other
+ * half of it in the async buffer queue we maintain for each
+ * client. In between we'll have a safety space of size
+ * 2*minreq. Why the 2*minreq? When the hw buffer is completey
+ * empty and needs to be filled, then our buffer must have
+ * enough data to fulfill this request immediatly and thus
+ * have at least the same tlength as the size of the hw
+ * buffer. It additionally needs space for 2 times minreq
+ * because if the buffer ran empty and a partial fillup
+ * happens immediately on the next iteration we need to be
+ * able to fulfill it and give the application also minreq
+ * time to fill it up again for the next request Makes 2 times
+ * minreq in plus.. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2)/2;
+ else
+ sink_usec = 0;
+
+ } else {
+
+ /* Ok, the user didn't ask us to adjust the latency, but we
+ * still need to make sure that the parameters from the user
+ * do make sense. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2);
+ else
+ sink_usec = 0;
+ }
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+ if (adjust_latency) {
+ /* Ok, we didn't necessarily get what we were asking for, so
+ * let's subtract from what we asked for for the remaining
+ * buffer space */
+
+ if (tlength_usec >= s->sink_latency)
+ tlength_usec -= s->sink_latency;
+ }
+
+ if (tlength_usec < s->sink_latency + 2*minreq_usec)
+ tlength_usec = s->sink_latency + 2*minreq_usec;
+
+ *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec);
+ *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+ if (*minreq <= 0) {
+ *minreq += frame_size;
+ *tlength += frame_size*2;
+ }
+
+ if (*tlength <= *minreq)
+ *tlength = *minreq*2 + frame_size;
+
+ if (*prebuf <= 0 || *prebuf > *tlength)
+ *prebuf = *tlength;
+}
+
+static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ s->minreq = *minreq;
+}
+
static playback_stream* playback_stream_new(
connection *c,
pa_sink *sink,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
uint32_t *maxlength,
uint32_t *tlength,
uint32_t *prebuf,
uint32_t *minreq,
pa_cvolume *volume,
+ pa_bool_t muted,
uint32_t syncid,
- int corked,
- uint32_t *missing) {
+ uint32_t *missing,
+ pa_sink_input_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency) {
playback_stream *s, *ssync;
pa_sink_input *sink_input;
- pa_memblock *silence;
+ pa_memchunk silence;
uint32_t idx;
int64_t start_index;
pa_sink_input_new_data data;
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+ pa_assert(volume);
+ pa_assert(missing);
+ pa_assert(p);
/* Find syncid group */
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -646,17 +883,24 @@ static playback_stream* playback_stream_new(
}
pa_sink_input_new_data_init(&data);
- data.sink = sink;
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
- data.name = name;
+ data.module = c->protocol->module;
+ data.client = c->client;
+ data.sink = sink;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
- data.module = c->protocol->module;
- data.client = c->client;
+ pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
- if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))
+ sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+
+ pa_sink_input_new_data_done(&data);
+
+ if (!sink_input)
return NULL;
s = pa_msgobject_new(playback_stream);
@@ -665,43 +909,51 @@ static playback_stream* playback_stream_new(
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->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill_cb;
+ s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
+ fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
*maxlength,
*tlength,
- pa_frame_size(ss),
+ pa_frame_size(&sink_input->sample_spec),
*prebuf,
*minreq,
- silence);
+ 0,
+ &silence);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
+ fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
- s->minreq = pa_memblockq_get_minreq(s->memblockq);
+ *ss = s->sink_input->sample_spec;
+ *map = s->sink_input->channel_map;
+
pa_atomic_store(&s->missing, 0);
- s->drain_request = 0;
+ s->drain_request = FALSE;
pa_idxset_put(c->output_streams, s, &s->index);
- pa_sink_input_put(s->sink_input);
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->sink_latency / PA_USEC_PER_MSEC);
+ pa_sink_input_put(s->sink_input);
return s;
}
@@ -788,13 +1040,13 @@ static void request_bytes(playback_stream *s) {
if (m <= 0)
return;
-/* pa_log("request_bytes(%u)", m); */
+/* pa_log("request_bytes(%lu)", (unsigned long) 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());
+
+ if (pa_memblockq_prebuf_active(s->memblockq) ||
+ (previous_missing < s->minreq && previous_missing+m >= s->minreq))
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- }
}
static void send_memblock(connection *c) {
@@ -853,6 +1105,43 @@ static void send_record_stream_killed(record_stream *r) {
/*** sink input callbacks ***/
+static void handle_seek(playback_stream *s, int64_t indexw) {
+ playback_stream_assert_ref(s);
+
+/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+ if (s->sink_input->thread_info.underrun_for > 0) {
+
+/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
+
+ if (pa_memblockq_is_readable(s->memblockq)) {
+
+ /* We just ended an underrun, let's ask the sink
+ * for a complete rewind rewrite */
+
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(s->sink_input,
+ s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for,
+ FALSE, TRUE);
+ }
+
+ } else {
+ int64_t indexr;
+
+ indexr = pa_memblockq_get_read_index(s->memblockq);
+
+ if (indexw < indexr) {
+ /* OK, the sink already asked for this data, so
+ * let's have it usk us again */
+
+ pa_log_debug("Requesting rewind due to rewrite.");
+ pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE);
+ }
+ }
+
+ request_bytes(s);
+}
+
/* 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);
@@ -864,48 +1153,44 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
switch (code) {
- case SINK_INPUT_MESSAGE_SEEK:
+ case SINK_INPUT_MESSAGE_SEEK: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
- request_bytes(s);
+
+ handle_seek(s, windex);
return 0;
+ }
case SINK_INPUT_MESSAGE_POST_DATA: {
+ int64_t windex;
+
pa_assert(chunk);
-/* pa_log("sink input post: %u", chunk->length); */
+ windex = pa_memblockq_get_write_index(s->memblockq);
- if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
+ 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);
}
- request_bytes(s);
-
- s->underrun = 0;
- return 0;
- }
-
- case SINK_INPUT_MESSAGE_DRAIN: {
+ handle_seek(s, windex);
- 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);
+/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
return 0;
}
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_FLUSH:
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
case SINK_INPUT_MESSAGE_TRIGGER: {
+ int64_t windex;
pa_sink_input *isync;
void (*func)(pa_memblockq *bq);
@@ -918,6 +1203,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
func = pa_memblockq_prebuf_force;
break;
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_TRIGGER:
func = pa_memblockq_prebuf_disable;
break;
@@ -926,23 +1212,32 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
pa_assert_not_reached();
}
+ windex = pa_memblockq_get_write_index(s->memblockq);
func(s->memblockq);
- s->underrun = 0;
- request_bytes(s);
+ handle_seek(s, windex);
/* 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);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = 0;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
}
for (isync = i->sync_next; isync; isync = isync->sync_next) {
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = 0;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
+ }
+
+ if (code == SINK_INPUT_MESSAGE_DRAIN) {
+ 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 = TRUE;
+ }
}
return 0;
@@ -952,14 +1247,21 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
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;
+ s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
return 0;
- case PA_SINK_INPUT_MESSAGE_SET_STATE:
+ case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_prebuf_force(s->memblockq);
- request_bytes(s);
+
+ handle_seek(s, windex);
+
+ /* Fall through to the default handler */
break;
+ }
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
@@ -976,7 +1278,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
playback_stream *s;
pa_sink_input_assert_ref(i);
@@ -984,44 +1286,61 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun
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("UNDERRUN: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
+ s->drain_request = FALSE;
+ 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);
+ } else if (i->thread_info.playing_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
+
+/* pa_log("adding %llu bytes", (unsigned long long) nbytes); */
+
+ request_bytes(s);
+
return -1;
}
-/* pa_log("peek: %u", chunk->length); */
+/* pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */
+
+ chunk->length = PA_MIN(nbytes, chunk->length);
+ if (i->thread_info.underrun_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
+ pa_memblockq_drop(s->memblockq, chunk->length);
request_bytes(s);
return 0;
}
-/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- pa_assert(length > 0);
- pa_memblockq_drop(s->memblockq, length);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
- 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_memblockq_rewind(s->memblockq, nbytes);
+}
- request_bytes(s);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
-/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream *s;
@@ -1033,6 +1352,70 @@ static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream_unlink(s);
}
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moved_cb(pa_sink_input *i) {
+ playback_stream *s;
+ pa_tagstruct *t;
+ uint32_t maxlength, tlength, prebuf, minreq;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, i->sink->index);
+ pa_tagstruct_puts(t, i->sink->name);
+ pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, tlength);
+ pa_tagstruct_putu32(t, prebuf);
+ pa_tagstruct_putu32(t, minreq);
+ pa_tagstruct_put_usec(t, s->sink_latency);
+ }
+
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** source_output callbacks ***/
/* Called from thread context */
@@ -1070,6 +1453,63 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
}
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moved_cb(pa_source_output *o) {
+ record_stream *s;
+ pa_tagstruct *t;
+ uint32_t maxlength, fragsize;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ fragsize = (uint32_t) s->fragment_size;
+ maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
+
+ fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, o->source->index);
+ pa_tagstruct_puts(t, o->source->name);
+ pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, fragsize);
+ pa_tagstruct_put_usec(t, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** pdispatch callbacks ***/
static void protocol_error(connection *c) {
@@ -1097,57 +1537,126 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
connection *c = CONNECTION(userdata);
playback_stream *s;
uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
- const char *name, *sink_name;
+ const char *name = NULL, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_sink *sink = NULL;
pa_cvolume volume;
- int corked;
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ muted = FALSE,
+ adjust_latency = FALSE;
+
+ pa_sink_input_flags_t flags = 0;
+ pa_proplist *p;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_get(
- t,
- PA_TAG_STRING, &name,
- PA_TAG_SAMPLE_SPEC, &ss,
- PA_TAG_CHANNEL_MAP, &map,
- PA_TAG_U32, &sink_index,
- PA_TAG_STRING, &sink_name,
- PA_TAG_U32, &maxlength,
- PA_TAG_BOOLEAN, &corked,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
- PA_TAG_U32, &syncid,
- PA_TAG_CVOLUME, &volume,
- PA_TAG_INVALID) < 0 ||
- !pa_tagstruct_eof(t) ||
- !name) {
+ if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+ pa_tagstruct_get(
+ t,
+ PA_TAG_SAMPLE_SPEC, &ss,
+ PA_TAG_CHANNEL_MAP, &map,
+ PA_TAG_U32, &sink_index,
+ PA_TAG_STRING, &sink_name,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_BOOLEAN, &corked,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_U32, &syncid,
+ PA_TAG_CVOLUME, &volume,
+ PA_TAG_INVALID) < 0) {
+
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength > 0 && maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength >= pa_frame_size(&ss), tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (sink_index != PA_INVALID_INDEX) {
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (sink_name) {
- sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
}
- s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);
+ flags =
+ (corked ? PA_SINK_INPUT_START_CORKED : 0) |
+ (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
+
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1159,7 +1668,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
/* pa_log("initial request is %u", missing); */
if (c->version >= 9) {
- /* Since 0.9 we support sending the buffer metrics back to the client */
+ /* Since 0.9.0 we support sending the buffer metrics back to the client */
pa_tagstruct_putu32(reply, (uint32_t) maxlength);
pa_tagstruct_putu32(reply, (uint32_t) tlength);
@@ -1167,6 +1676,23 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
pa_tagstruct_putu32(reply, (uint32_t) minreq);
}
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+ pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+ pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
+ }
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1233,46 +1759,127 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
record_stream *s;
uint32_t maxlength, fragment_size;
uint32_t source_index;
- const char *name, *source_name;
+ const char *name = NULL, *source_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_source *source = NULL;
- int corked;
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ adjust_latency = FALSE,
+ peak_detect = FALSE;
+ pa_source_output_flags_t flags = 0;
+ pa_proplist *p;
+ uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+ pa_sink_input *direct_on_input = NULL;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
pa_tagstruct_getu32(t, &source_index) < 0 ||
pa_tagstruct_gets(t, &source_name) < 0 ||
pa_tagstruct_getu32(t, &maxlength) < 0 ||
pa_tagstruct_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &fragment_size) < 0) {
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (source_index != PA_INVALID_INDEX) {
- source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (source_name) {
- source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
}
- s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);
+ if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+ if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ flags =
+ (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
+ (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1284,9 +1891,26 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
/* Since 0.9 we support sending the buffer metrics back to the client */
pa_tagstruct_putu32(reply, (uint32_t) maxlength);
- pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);
+ pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+ }
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->source_output->source->index);
+ pa_tagstruct_puts(reply, s->source_output->source->name);
+
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
}
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1311,6 +1935,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
connection *c = CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
+ pa_bool_t shm_on_remote, do_shm;
connection_assert_ref(c);
pa_assert(t);
@@ -1328,50 +1953,53 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
return;
}
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+ pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
+
if (!c->authorized) {
- int success = 0;
+ pa_bool_t success = FALSE;
#ifdef HAVE_CREDS
const pa_creds *creds;
if ((creds = pa_pdispatch_creds(pd))) {
if (creds->uid == getuid())
- success = 1;
+ success = TRUE;
else if (c->protocol->auth_group) {
int r;
gid_t gid;
if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1)
- pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group);
+ pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group);
else if (gid == creds->gid)
- success = 1;
+ success = TRUE;
if (!success) {
if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0)
- pa_log_warn("failed to check group membership.");
+ pa_log_warn("Failed to check group membership.");
else if (r > 0)
- success = 1;
+ success = TRUE;
}
}
pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
(unsigned long) creds->uid,
(unsigned long) creds->gid,
- success);
-
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->protocol->core->mempool) &&
- creds->uid == getuid()) {
-
- pa_pstream_use_shm(c->pstream, 1);
- pa_log_info("Enabled SHM for new connection");
- }
-
+ (int) success);
}
#endif
if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
- success = 1;
+ success = TRUE;
if (!success) {
pa_log_warn("Denied access to client with invalid authorization data.");
@@ -1379,15 +2007,39 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
return;
}
- 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;
}
}
+ /* Enable shared memory support if possible */
+ do_shm =
+ pa_mempool_is_shared(c->protocol->core->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+ if (do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ do_shm = FALSE;
+
+ if (do_shm) {
+ /* Only enable SHM if both sides are owned by the same
+ * user. This is a security measure because otherwise data
+ * private to the user might leak. */
+
+ const pa_creds *creds;
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ do_shm = FALSE;
+ }
+
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+ pa_pstream_enable_shm(c->pstream, do_shm);
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION);
+ pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
#ifdef HAVE_CREDS
{
@@ -1407,21 +2059,42 @@ 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) {
connection *c = CONNECTION(userdata);
- const char *name;
+ const char *name = NULL;
+ pa_proplist *p;
+ pa_tagstruct *reply;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ p = pa_proplist_new();
+
+ if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+ (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
!pa_tagstruct_eof(t)) {
+
protocol_error(c);
+ pa_proplist_free(p);
return;
}
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+ if (name)
+ if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ pa_proplist_free(p);
+ return;
+ }
- pa_client_set_name(c->client, name);
- pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, c->client->index);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1537,16 +2210,22 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
reply = reply_new(tag);
latency = pa_sink_get_latency(s->sink_input->sink);
- latency += pa_bytes_to_usec(s->resampled_chunk_length, &s->sink_input->sample_spec);
+ latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
pa_tagstruct_put_usec(reply, latency);
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_index);
+
+ if (c->version >= 13) {
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1574,7 +2253,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
reply = reply_new(tag);
pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, 0);
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_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));
@@ -1586,10 +2265,11 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
connection *c = CONNECTION(userdata);
upload_stream *s;
uint32_t length;
- const char *name;
+ const char *name = NULL;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
+ pa_proplist *p;
connection_assert_ref(c);
pa_assert(t);
@@ -1597,8 +2277,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
- pa_tagstruct_getu32(t, &length) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &length) < 0) {
protocol_error(c);
return;
}
@@ -1609,9 +2288,26 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
+
+ p = pa_proplist_new();
+
+ if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (c->version < 13)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+ else if (!name)
+ if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- s = upload_stream_new(c, &ss, &map, name, length);
+ s = upload_stream_new(c, &ss, &map, name, length, p);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1641,7 +2337,7 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, s, 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)
+ if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
else
pa_pstream_send_simple_ack(c->pstream, tag);
@@ -1655,20 +2351,23 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
pa_volume_t volume;
pa_sink *sink;
const char *name, *sink_name;
+ uint32_t idx;
+ pa_proplist *p;
+ pa_tagstruct *reply;
connection_assert_ref(c);
pa_assert(t);
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
pa_tagstruct_gets(t, &sink_name) < 0 ||
pa_tagstruct_getu32(t, &volume) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_gets(t, &name) < 0) {
protocol_error(c);
return;
}
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
@@ -1679,12 +2378,29 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) {
+ p = pa_proplist_new();
+
+ if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
return;
}
- pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_free(p);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, idx);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1711,16 +2427,38 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
+static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+ pa_assert(c);
+ pa_assert(fixed);
+ pa_assert(original);
+
+ *fixed = *original;
+
+ if (c->version < 12) {
+ /* Before protocol version 12 we didn't support S32 samples,
+ * so we need to lie about this to the client */
+
+ if (fixed->format == PA_SAMPLE_S32LE)
+ fixed->format = PA_SAMPLE_FLOAT32LE;
+ if (fixed->format == PA_SAMPLE_S32BE)
+ fixed->format = PA_SAMPLE_FLOAT32BE;
+ }
+}
+
+static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_sink_assert_ref(sink);
+ fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, sink->index,
PA_TAG_STRING, sink->name,
- PA_TAG_STRING, sink->description,
- PA_TAG_SAMPLE_SPEC, &sink->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
PA_TAG_CVOLUME, pa_sink_get_volume(sink),
@@ -1731,18 +2469,27 @@ static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
PA_TAG_STRING, sink->driver,
PA_TAG_U32, sink->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, sink->proplist);
+ pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
+ }
}
-static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
+static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_source_assert_ref(source);
+ fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, source->index,
PA_TAG_STRING, source->name,
- PA_TAG_STRING, source->description,
- PA_TAG_SAMPLE_SPEC, &source->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &source->channel_map,
PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
PA_TAG_CVOLUME, pa_source_get_volume(source),
@@ -1753,16 +2500,26 @@ static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
PA_TAG_STRING, source->driver,
PA_TAG_U32, source->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, source->proplist);
+ pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
+ }
}
-static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
+
+static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *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);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
+ pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
pa_tagstruct_puts(t, client->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, client->proplist);
+
}
static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
@@ -1777,55 +2534,80 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
}
static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t sink_latency;
+
pa_assert(t);
pa_sink_input_assert_ref(s);
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->sink->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_put_cvolume(t, &s->volume);
- pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
- pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
+ pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
+ pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
if (c->version >= 11)
pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
+static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t source_latency;
+
pa_assert(t);
pa_source_output_assert_ref(s);
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->source->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
- pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
+ pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+ pa_tagstruct_put_usec(t, source_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
+static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_assert(e);
+ if (e->memchunk.memblock)
+ fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ else
+ memset(&fixed_ss, 0, sizeof(fixed_ss));
+
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
pa_tagstruct_put_cvolume(t, &e->volume);
- pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
- pa_tagstruct_put_sample_spec(t, &e->sample_spec);
+ pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &e->channel_map);
pa_tagstruct_putu32(t, e->memchunk.length);
pa_tagstruct_put_boolean(t, e->lazy);
pa_tagstruct_puts(t, e->filename);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, e->proplist);
}
static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1891,19 +2673,19 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
reply = reply_new(tag);
if (sink)
- sink_fill_tagstruct(reply, sink);
+ sink_fill_tagstruct(c, reply, sink);
else if (source)
- source_fill_tagstruct(reply, source);
+ source_fill_tagstruct(c, reply, source);
else if (client)
- client_fill_tagstruct(reply, client);
+ client_fill_tagstruct(c, reply, client);
else if (module)
module_fill_tagstruct(reply, module);
else if (si)
sink_input_fill_tagstruct(c, reply, si);
else if (so)
- source_output_fill_tagstruct(reply, so);
+ source_output_fill_tagstruct(c, reply, so);
else
- scache_fill_tagstruct(reply, sce);
+ scache_fill_tagstruct(c, reply, sce);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1946,20 +2728,20 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
if (i) {
for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) {
if (command == PA_COMMAND_GET_SINK_INFO_LIST)
- sink_fill_tagstruct(reply, p);
+ sink_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
- source_fill_tagstruct(reply, p);
+ source_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
- client_fill_tagstruct(reply, p);
+ client_fill_tagstruct(c, reply, p);
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(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
- source_output_fill_tagstruct(reply, p);
+ source_output_fill_tagstruct(c, reply, p);
else {
pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
- scache_fill_tagstruct(reply, p);
+ scache_fill_tagstruct(c, reply, p);
}
}
}
@@ -1972,6 +2754,7 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
pa_tagstruct *reply;
char txt[256];
const char *n;
+ pa_sample_spec fixed_ss;
connection_assert_ref(c);
pa_assert(t);
@@ -1987,8 +2770,10 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
pa_tagstruct_puts(reply, PACKAGE_NAME);
pa_tagstruct_puts(reply, PACKAGE_VERSION);
pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
- pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
- pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_put_sample_spec(reply, &fixed_ss);
n = pa_namereg_get_default_sink_name(c->protocol->core);
pa_tagstruct_puts(reply, n);
@@ -2118,7 +2903,7 @@ static void command_set_mute(
connection *c = CONNECTION(userdata);
uint32_t idx;
- int mute;
+ pa_bool_t mute;
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
@@ -2181,7 +2966,7 @@ static void command_set_mute(
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) {
connection *c = CONNECTION(userdata);
uint32_t idx;
- int b;
+ pa_bool_t b;
playback_stream *s;
connection_assert_ref(c);
@@ -2248,7 +3033,7 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
connection *c = CONNECTION(userdata);
uint32_t idx;
record_stream *s;
- int b;
+ pa_bool_t b;
connection_assert_ref(c);
pa_assert(t);
@@ -2291,6 +3076,295 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_tagstruct *reply;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+ playback_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, tlength);
+ pa_tagstruct_putu32(reply, prebuf);
+ pa_tagstruct_putu32(reply, minreq);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
+ } else {
+ record_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &fragsize,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, fragsize);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t rate;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &rate) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ pa_sink_input_set_rate(s->sink_input, rate);
+
+ } else {
+ record_stream *s;
+ pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_source_output_set_rate(s->source_output, rate);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t mode;
+ pa_proplist *p;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ p = pa_proplist_new();
+
+ if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ } else {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ pa_proplist_update(s->sink_input->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_proplist_update(s->source_output->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+ } else {
+ pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
+
+ pa_proplist_update(c->client->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ unsigned changed = 0;
+ pa_proplist *p;
+ pa_strlist *l = NULL;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+ }
+
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ p = s->sink_input->proplist;
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ p = s->source_output->proplist;
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+
+ p = c->client->proplist;
+ }
+
+ for (;;) {
+ const char *k;
+
+ if (pa_tagstruct_gets(t, &k) < 0) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ if (!k)
+ break;
+
+ l = pa_strlist_prepend(l, k);
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ for (;;) {
+ char *z;
+
+ l = pa_strlist_pop(l, &z);
+
+ if (!z)
+ break;
+
+ changed += pa_proplist_unset(p, z) >= 0;
+ pa_xfree(z);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+
+ if (changed) {
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+ }
+}
+
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) {
connection *c = CONNECTION(userdata);
const char *s;
@@ -2340,6 +3414,7 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
} else {
record_stream *s;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
@@ -2620,7 +3695,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -2652,7 +3727,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
connection *c = CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL;
- int b;
+ pa_bool_t b;
connection_assert_ref(c);
pa_assert(t);
@@ -2862,7 +3937,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
connection *c;
char cname[256], pname[128];
- pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -2876,11 +3950,11 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
c->parent.parent.free = connection_free;
c->parent.process_msg = connection_process_msg;
- c->authorized = !!p->public;
+ c->authorized = p->public;
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) {
@@ -2891,14 +3965,16 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
} else
c->auth_timeout_event = NULL;
+ c->is_local = pa_iochannel_socket_is_local(io);
c->version = 8;
c->protocol = p;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
+ pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
c->client->kill = client_kill_cb;
c->client->userdata = c;
- c->client->owner = p->module;
+ c->client->module = p->module;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
@@ -2922,7 +3998,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
#ifdef HAVE_CREDS
if (pa_iochannel_creds_supported(io))
pa_iochannel_creds_enable(io);
-
#endif
}
@@ -2931,12 +4006,12 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
static int load_key(pa_protocol_native*p, const char*fn) {
pa_assert(p);
- p->auth_cookie_in_property = 0;
+ p->auth_cookie_in_property = FALSE;
if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
pa_log_info("using already loaded auth cookie.");
pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
- p->auth_cookie_in_property = 1;
+ p->auth_cookie_in_property = TRUE;
return 0;
}
@@ -2949,14 +4024,14 @@ static int load_key(pa_protocol_native*p, const char*fn) {
pa_log_info("loading cookie from disk.");
if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
- p->auth_cookie_in_property = 1;
+ p->auth_cookie_in_property = TRUE;
return 0;
}
static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
pa_protocol_native *p;
- int public = 0;
+ pa_bool_t public = FALSE;
const char *acl;
pa_assert(c);
@@ -2976,12 +4051,12 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
#ifdef HAVE_CREDS
{
- int a = 1;
+ pa_bool_t a = TRUE;
if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument.");
return NULL;
}
- p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", c->is_system_instance ? PA_ACCESS_GROUP : NULL)) : NULL;
+ p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
if (p->auth_group)
pa_log_info("Allowing access to group '%s'.", p->auth_group);
@@ -3021,7 +4096,7 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index bf05f937..a52fa8cf 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 64e2a81c..020a281d 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
@@ -42,6 +41,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/atomic.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
#include "protocol-simple.h"
@@ -57,12 +57,13 @@ typedef struct connection {
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
- int dead;
+ pa_bool_t dead;
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
} connection;
@@ -101,7 +102,8 @@ enum {
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
+#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
static void connection_unlink(connection *c) {
pa_assert(c);
@@ -140,8 +142,6 @@ 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);
@@ -158,27 +158,33 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
+ size_t space;
connection_assert_ref(c);
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)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
+ }
if (!c->playback.current_memblock) {
- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l));
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
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);
@@ -248,16 +254,16 @@ static void do_work(connection *c) {
if (c->dead)
return;
- if (pa_iochannel_is_readable(c->io)) {
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- } else if (pa_iochannel_is_hungup(c->io))
+
+ if (!c->sink_input && pa_iochannel_is_hungup(c->io))
goto fail;
- if (pa_iochannel_is_writable(c->io)) {
+ if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
- }
return;
@@ -266,7 +272,7 @@ 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;
+ c->dead = TRUE;
pa_iochannel_free(c->io);
c->io = NULL;
@@ -318,15 +324,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* New data from the main loop */
pa_memblockq_push_align(c->input_memblockq, chunk);
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
return 0;
}
- case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+ 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;
@@ -343,43 +353,64 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
connection *c;
- int r;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
pa_assert(chunk);
- r = pa_memblockq_peek(c->input_memblockq, chunk);
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
-/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */
+ c->playback.underrun = TRUE;
- if (c->dead && r < 0)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
- return r;
+ return -1;
+ } else {
+ size_t m;
+
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+ return 0;
+ }
}
/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
connection *c;
- size_t old, new;
- pa_assert(i);
+ pa_sink_input_assert_ref(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);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- 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);
- }
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
/* Called from main context */
@@ -395,7 +426,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
connection *c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
pa_assert(chunk);
@@ -414,7 +445,7 @@ static void source_output_kill_cb(pa_source_output *o) {
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
connection*c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
@@ -449,7 +480,7 @@ static void io_callback(pa_iochannel*io, void *userdata) {
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
connection *c = NULL;
- char cname[256];
+ char cname[256], pname[128];
pa_assert(s);
pa_assert(io);
@@ -471,49 +502,64 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
- c->playback.fragment_size = 0;
- c->dead = 0;
+ c->dead = FALSE;
+ c->playback.underrun = TRUE;
pa_atomic_store(&c->playback.missing, 0);
- pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
- c->client->owner = p->module;
+ pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
pa_sink_input_new_data data;
size_t l;
+ pa_sink *sink;
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ pa_log("Failed to get sink.");
+ goto fail;
+ }
pa_sink_input_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->module;
data.client = c->client;
+ data.sink = sink;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
- if (!(c->sink_input = pa_sink_input_new(p->core, &data, 0))) {
+ c->sink_input = pa_sink_input_new(p->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!c->sink_input) {
pa_log("Failed to create sink input.");
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->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&p->sample_spec),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
- c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+ pa_iochannel_socket_set_rcvbuf(io, l);
pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
@@ -523,15 +569,25 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
if (p->mode & RECORD) {
pa_source_output_new_data data;
size_t l;
+ pa_source *source;
+
+ if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) {
+ pa_log("Failed to get source.");
+ goto fail;
+ }
pa_source_output_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->module;
data.client = c->client;
+ data.source = source;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
- if (!(c->source_output = pa_source_output_new(p->core, &data, 0))) {
+ c->source_output = pa_source_output_new(p->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!c->source_output) {
pa_log("Failed to create source output.");
goto fail;
}
@@ -540,6 +596,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
@@ -548,8 +606,9 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_frame_size(&p->sample_spec),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
+ pa_iochannel_socket_set_sndbuf(io, l);
pa_source_output_put(c->source_output);
}
@@ -566,16 +625,17 @@ fail:
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;
+ pa_bool_t enable;
pa_assert(core);
pa_assert(server);
+ pa_assert(m);
pa_assert(ma);
p = pa_xnew0(pa_protocol_simple, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
@@ -587,14 +647,14 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
- enable = 0;
+ enable = FALSE;
if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
pa_log("record= expects a numeric argument.");
goto fail;
}
p->mode = enable ? RECORD : 0;
- enable = 1;
+ enable = TRUE;
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
pa_log("playback= expects a numeric argument.");
goto fail;
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
index 3b02c88e..e1b3143b 100644
--- a/src/pulsecore/protocol-simple.h
+++ b/src/pulsecore/protocol-simple.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index 1b513782..f84f486a 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,7 +54,7 @@ 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_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
index 67759f2a..ae0d79c3 100644
--- a/src/pulsecore/pstream-util.h
+++ b/src/pulsecore/pstream-util.h
@@ -1,8 +1,6 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 9d32a363..e26ca473 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -98,7 +96,7 @@ struct item_info {
/* packet info */
pa_packet *packet;
#ifdef HAVE_CREDS
- int with_creds;
+ pa_bool_t with_creds;
pa_creds creds;
#endif
@@ -121,7 +119,7 @@ struct pa_pstream {
pa_queue *send_queue;
- int dead;
+ pa_bool_t dead;
struct {
pa_pstream_descriptor descriptor;
@@ -141,7 +139,7 @@ struct pa_pstream {
size_t index;
} read;
- int use_shm;
+ pa_bool_t use_shm;
pa_memimport *import;
pa_memexport *export;
@@ -167,7 +165,7 @@ struct pa_pstream {
#ifdef HAVE_CREDS
pa_creds read_creds, write_creds;
- int read_creds_valid, send_creds_now;
+ pa_bool_t read_creds_valid, send_creds_now;
#endif
};
@@ -239,7 +237,7 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
PA_REFCNT_INIT(p);
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
- p->dead = 0;
+ p->dead = FALSE;
p->mainloop = m;
p->defer_event = m->defer_new(m, defer_callback, p);
@@ -269,18 +267,18 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
p->mempool = pool;
- p->use_shm = 0;
+ p->use_shm = FALSE;
p->export = NULL;
/* We do importing unconditionally */
p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
- pa_iochannel_socket_set_rcvbuf(io, 1024*8);
- pa_iochannel_socket_set_sndbuf(io, 1024*8);
+ pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
+ pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
#ifdef HAVE_CREDS
- p->send_creds_now = 0;
- p->read_creds_valid = 0;
+ p->send_creds_now = FALSE;
+ p->read_creds_valid = FALSE;
#endif
return p;
}
@@ -374,7 +372,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
- n = MIN(length, bsm);
+ n = PA_MIN(length, bsm);
i->chunk.index = chunk->index + idx;
i->chunk.length = n;
i->chunk.memblock = pa_memblock_ref(chunk->memblock);
@@ -383,7 +381,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
i->offset = offset;
i->seek_mode = seek_mode;
#ifdef HAVE_CREDS
- i->with_creds = 0;
+ i->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, i);
@@ -410,7 +408,7 @@ void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
item->type = PA_PSTREAM_ITEM_SHMRELEASE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, item);
@@ -447,7 +445,7 @@ void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
item->type = PA_PSTREAM_ITEM_SHMREVOKE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, item);
@@ -504,7 +502,7 @@ static void prepare_next_write_item(pa_pstream *p) {
} else {
uint32_t flags;
- int send_payload = 1;
+ pa_bool_t send_payload = TRUE;
pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
pa_assert(p->write.current->chunk.memblock);
@@ -529,7 +527,7 @@ static void prepare_next_write_item(pa_pstream *p) {
&length) >= 0) {
flags |= PA_FLAG_SHMDATA;
- send_payload = 0;
+ send_payload = FALSE;
p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
@@ -599,7 +597,7 @@ static int do_write(pa_pstream *p) {
if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
goto fail;
- p->send_creds_now = 0;
+ p->send_creds_now = FALSE;
} else
#endif
@@ -875,7 +873,7 @@ frame_done:
p->read.data = NULL;
#ifdef HAVE_CREDS
- p->read_creds_valid = 0;
+ p->read_creds_valid = FALSE;
#endif
return 0;
@@ -935,14 +933,14 @@ void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb,
p->release_callback_userdata = userdata;
}
-int pa_pstream_is_pending(pa_pstream *p) {
- int b;
+pa_bool_t pa_pstream_is_pending(pa_pstream *p) {
+ pa_bool_t b;
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
- b = 0;
+ b = FALSE;
else
b = p->write.current || !pa_queue_is_empty(p->send_queue);
@@ -971,7 +969,7 @@ void pa_pstream_unlink(pa_pstream *p) {
if (p->dead)
return;
- p->dead = 1;
+ p->dead = TRUE;
if (p->import) {
pa_memimport_free(p->import);
@@ -999,7 +997,7 @@ void pa_pstream_unlink(pa_pstream *p) {
p->recieve_memblock_callback = NULL;
}
-void pa_pstream_use_shm(pa_pstream *p, int enable) {
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) {
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
@@ -1018,3 +1016,10 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
}
}
}
+
+pa_bool_t pa_pstream_get_shm(pa_pstream *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ return p->use_shm;
+}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
index 72babea9..a528b25c 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -1,8 +1,6 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulsecore/iochannel.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
typedef struct pa_pstream pa_pstream;
@@ -44,8 +43,11 @@ 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);
+
pa_pstream* pa_pstream_ref(pa_pstream*p);
+void pa_pstream_unref(pa_pstream*p);
+
+void pa_pstream_unlink(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);
@@ -59,10 +61,9 @@ void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void
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);
+pa_bool_t pa_pstream_is_pending(pa_pstream *p);
-void pa_pstream_use_shm(pa_pstream *p, int enable);
-
-void pa_pstream_unlink(pa_pstream *p);
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable);
+pa_bool_t pa_pstream_get_shm(pa_pstream *p);
#endif
diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c
index a88876f6..08fd1426 100644
--- a/src/pulsecore/queue.c
+++ b/src/pulsecore/queue.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,10 +45,10 @@ 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;
}
@@ -65,7 +63,7 @@ void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *
pa_assert(!q->front);
pa_assert(!q->back);
pa_assert(q->length == 0);
-
+
pa_xfree(q);
}
@@ -74,10 +72,10 @@ void pa_queue_push(pa_queue *q, void *p) {
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;
@@ -112,7 +110,7 @@ void* pa_queue_pop(pa_queue *q) {
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
-
+
q->length--;
return p;
@@ -120,6 +118,6 @@ void* pa_queue_pop(pa_queue *q) {
int pa_queue_is_empty(pa_queue *q) {
pa_assert(q);
-
+
return q->length == 0;
}
diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h
index cd767364..b09216cf 100644
--- a/src/pulsecore/queue.h
+++ b/src/pulsecore/queue.h
@@ -1,8 +1,6 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index 87afebfa..5deac37b 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -64,7 +62,11 @@ static int random_proper(void *ret_data, size_t length) {
while (*device) {
ret = 0;
- if ((fd = open(*device, O_RDONLY)) >= 0) {
+ if ((fd = open(*device, O_RDONLY
+#ifdef O_NOCTTY
+ | O_NOCTTY
+#endif
+ )) >= 0) {
if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
ret = -1;
diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h
index 01b7d746..36d7f9df 100644
--- a/src/pulsecore/random.h
+++ b/src/pulsecore/random.h
@@ -1,8 +1,6 @@
#ifndef foorandomhfoo
#define foorandomhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 64271ab2..291f4504 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -1,8 +1,6 @@
#ifndef foopulserefcnthfoo
#define foopulserefcnthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,9 @@
#define PA_REFCNT_INIT(p) \
pa_atomic_store(&(p)->_ref, 1)
+#define PA_REFCNT_INIT_ZERO(p) \
+ pa_atomic_store(&(p)->_ref, 0)
+
#define PA_REFCNT_INC(p) \
pa_atomic_inc(&(p)->_ref)
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 5bbc6bf4..00dc794c 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,6 +36,7 @@
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
#include "speexwrap.h"
@@ -46,10 +45,12 @@
#include "resampler.h"
/* Number of samples of extra space we allow the resamplers to return */
-#define EXTRA_SAMPLES 128
+#define EXTRA_FRAMES 128
struct pa_resampler {
- pa_resample_method_t resample_method;
+ pa_resample_method_t method;
+ pa_resample_flags_t flags;
+
pa_sample_spec i_ss, o_ss;
pa_channel_map i_cm, o_cm;
size_t i_fz, o_fz, w_sz;
@@ -63,18 +64,28 @@ struct pa_resampler {
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;
+ float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_bool_t map_required;
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);
+ void (*impl_reset)(pa_resampler *r);
struct { /* data specific to the trivial resampler */
unsigned o_counter;
unsigned i_counter;
} trivial;
+ struct { /* data specific to the peak finder pseudo resampler */
+ unsigned o_counter;
+ unsigned i_counter;
+
+ float max_f[PA_CHANNELS_MAX];
+ int16_t max_i[PA_CHANNELS_MAX];
+
+ } peaks;
+
#ifdef HAVE_LIBSAMPLERATE
struct { /* data specific to libsamplerate */
SRC_STATE *state;
@@ -95,6 +106,7 @@ 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);
+static int peaks_init(pa_resampler*r);
#ifdef HAVE_LIBSAMPLERATE
static int libsamplerate_init(pa_resampler*r);
#endif
@@ -140,7 +152,8 @@ static int (* const init_table[])(pa_resampler*r) = {
[PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init,
[PA_RESAMPLER_FFMPEG] = ffmpeg_init,
[PA_RESAMPLER_AUTO] = NULL,
- [PA_RESAMPLER_COPY] = copy_init
+ [PA_RESAMPLER_COPY] = copy_init,
+ [PA_RESAMPLER_PEAKS] = peaks_init,
};
static inline size_t sample_size(pa_sample_format_t f) {
@@ -159,8 +172,8 @@ 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,
- int variable_rate) {
+ pa_resample_method_t method,
+ pa_resample_flags_t flags) {
pa_resampler *r = NULL;
@@ -169,41 +182,43 @@ pa_resampler* pa_resampler_new(
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);
+ pa_assert(method >= 0);
+ pa_assert(method < PA_RESAMPLER_MAX);
/* Fix method */
- if (!variable_rate && a->rate == b->rate) {
+ if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) {
pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
- resample_method = PA_RESAMPLER_COPY;
+ 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 (!pa_resample_method_supported(method)) {
+ pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_FFMPEG && variable_rate) {
+ if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) {
pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
- resample_method = PA_RESAMPLER_AUTO;
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_COPY && (variable_rate || a->rate != b->rate)) {
+ if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) {
pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
- resample_method = PA_RESAMPLER_AUTO;
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_AUTO)
- resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
+ if (method == PA_RESAMPLER_AUTO)
+ method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
r = pa_xnew(pa_resampler, 1);
r->mempool = pool;
- r->resample_method = resample_method;
+ r->method = method;
+ r->flags = flags;
r->impl_free = NULL;
r->impl_update_rates = NULL;
r->impl_resample = NULL;
+ r->impl_reset = NULL;
/* Fill sample specs */
r->i_ss = *a;
@@ -211,13 +226,13 @@ pa_resampler* pa_resampler_new(
if (am)
r->i_cm = *am;
- else
- pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
if (bm)
r->o_cm = *bm;
- else
- pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (!pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
@@ -231,16 +246,18 @@ pa_resampler* pa_resampler_new(
calc_map_table(r);
- pa_log_info("Using resampler '%s'", pa_resample_method_to_string(resample_method));
+ pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
- if ((resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
- (resample_method == PA_RESAMPLER_FFMPEG))
+ if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
+ (method == PA_RESAMPLER_FFMPEG))
r->work_format = PA_SAMPLE_S16NE;
- else if (resample_method == PA_RESAMPLER_TRIVIAL || resample_method == PA_RESAMPLER_COPY) {
+ else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
- if (r->map_required || a->format != b->format) {
+ if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
- if (a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
+ a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
r->work_format = PA_SAMPLE_FLOAT32NE;
else
@@ -279,7 +296,7 @@ pa_resampler* pa_resampler_new(
}
/* initialize implementation */
- if (init_table[resample_method](r) < 0)
+ if (init_table[method](r) < 0)
goto fail;
return r;
@@ -339,6 +356,12 @@ size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
}
+size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+ pa_assert(r);
+
+ return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+}
+
size_t pa_resampler_max_block_size(pa_resampler *r) {
size_t block_size_max;
pa_sample_spec ss;
@@ -350,28 +373,30 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {
/* 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;
+ ss.channels = PA_MAX(r->i_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;
+ ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+ ss.format = PA_MAX(ss.format, r->work_format);
- if (r->o_ss.rate > ss.rate)
- ss.rate = r->o_ss.rate;
+ ss.rate = PA_MAX(r->i_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;
+ return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+}
+
+void pa_resampler_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->impl_reset)
+ r->impl_reset(r);
}
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
pa_assert(r);
- return r->resample_method;
+ return r->method;
}
static const char * const resample_methods[] = {
@@ -405,7 +430,8 @@ static const char * const resample_methods[] = {
"speex-fixed-10",
"ffmpeg",
"auto",
- "copy"
+ "copy",
+ "peaks"
};
const char *pa_resample_method_to_string(pa_resample_method_t m) {
@@ -439,44 +465,431 @@ pa_resample_method_t pa_parse_resample_method(const char *string) {
return m;
if (!strcmp(string, "speex-fixed"))
- return PA_RESAMPLER_SPEEX_FIXED_BASE + 0;
+ return PA_RESAMPLER_SPEEX_FIXED_BASE + 3;
if (!strcmp(string, "speex-float"))
- return PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
+ return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
return PA_RESAMPLER_INVALID;
}
+static pa_bool_t on_left(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t on_right(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_bool_t on_center(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_REAR_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_lfe(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_LFE;
+}
+
static void calc_map_table(pa_resampler *r) {
- unsigned oc;
+ unsigned oc, ic;
+ pa_bool_t ic_connected[PA_CHANNELS_MAX];
+ pa_bool_t remix;
+ pa_strbuf *s;
+ char *t;
pa_assert(r);
- if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || !pa_channel_map_equal(&r->i_cm, &r->o_cm))))
+ if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
+ memset(r->map_table, 0, sizeof(r->map_table));
+ memset(ic_connected, 0, sizeof(ic_connected));
+ remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
+
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic, i = 0;
+ pa_bool_t oc_connected = FALSE;
+ pa_channel_position_t b = r->o_cm.map[oc];
for (ic = 0; ic < r->i_ss.channels; ic++) {
- pa_channel_position_t a, b;
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (r->flags & PA_RESAMPLER_NO_REMAP) {
+ /* We shall not do any remapping. Hence, just check by index */
+
+ if (ic == oc)
+ r->map_table[oc][ic] = 1.0;
+
+ continue;
+ }
- a = r->i_cm.map[ic];
- b = r->o_cm.map[oc];
+ if (r->flags & PA_RESAMPLER_NO_REMIX) {
+ /* We shall not do any remixing. Hence, just check by name */
- if (a == b ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
- (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
- (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
+ if (a == b)
+ r->map_table[oc][ic] = 1.0;
- r->map_table[oc][i++] = ic;
+ continue;
+ }
+
+ pa_assert(remix);
+
+ /* OK, we shall do the full monty: upmixing and
+ * downmixing. Our algorithm is relatively simple, does
+ * not do spacialization, delay elements or apply lowpass
+ * filters for LFE. Patches are always welcome,
+ * though. Oh, and it doesn't do any matrix
+ * decoding. (Which probably wouldn't make any sense
+ * anyway.)
+ *
+ * This code is not idempotent: downmixing an upmixed
+ * stereo stream is not identical to the original. The
+ * volume will not match, and the two channels will be a
+ * linear combination of both.
+ *
+ * This is losely based on random suggestions found on the
+ * Internet, such as this:
+ * http://www.halfgaar.net/surround-sound-in-linux and the
+ * alsa upmix plugin.
+ *
+ * The algorithm works basically like this:
+ *
+ * 1) Connect all channels with matching names.
+ *
+ * 2) Mono Handling:
+ * S:Mono: Copy into all D:channels
+ * D:Mono: Copy in all S:channels
+ *
+ * 3) Mix D:Left, D:Right:
+ * D:Left: If not connected, avg all S:Left
+ * D:Right: If not connected, avg all S:Right
+ *
+ * 4) Mix D:Center
+ * If not connected, avg all S:Center
+ * If still not connected, avg all S:Left, S:Right
+ *
+ * 5) Mix D:LFE
+ * If not connected, avg all S:*
+ *
+ * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If
+ * not connected, mix into all D:left and all D:right
+ * channels. Gain is 0.1, the current left and right
+ * should be multiplied by 0.9.
+ *
+ * 7) Make sure S:Center, S:LFE is used:
+ *
+ * S:Center, S:LFE: If not connected, mix into all
+ * D:left, all D:right, all D:center channels, gain is
+ * 0.375. The current (as result of 1..6) factors
+ * should be multiplied by 0.75. (Alt. suggestion: 0.25
+ * vs. 0.5)
+ *
+ * S: and D: shall relate to the source resp. destination channels.
+ *
+ * Rationale: 1, 2 are probably obvious. For 3: this
+ * copies front to rear if needed. For 4: we try to find
+ * some suitable C source for C, if we don't find any, we
+ * avg L and R. For 5: LFE is mixed from all channels. For
+ * 6: the rear channels should not be dropped entirely,
+ * however have only minimal impact. For 7: movies usually
+ * encode speech on the center channel. Thus we have to
+ * make sure this channel is distributed to L and R if not
+ * available in the output. Also, LFE is used to achieve a
+ * greater dynamic range, and thus we should try to do our
+ * best to pass it to L+R.
+ */
+
+ if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
+ r->map_table[oc][ic] = 1.0;
+
+ oc_connected = TRUE;
+ ic_connected[ic] = TRUE;
+ }
}
- /* Add an end marker */
- if (i < PA_CHANNELS_MAX)
- r->map_table[oc][i] = -1;
+ if (!oc_connected && remix) {
+ /* OK, we shall remix */
+
+ if (on_left(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the left side, let's
+ * average all left side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no left input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_right(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the right side, let's
+ * average all right side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no right input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_center(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and at the center. Let's
+ * average all center input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0) {
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+ } else {
+
+ /* Hmm, no center channel around, let's synthesize
+ * it by mixing L and R.*/
+
+ n = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is not even a
+ * left or right input channel. Something is
+ * really wrong in this case anyway. */
+ }
+
+ } else if (on_lfe(b)) {
+
+ /* We are not connected and an LFE. Let's average all
+ * channels for LFE. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ r->map_table[oc][ic] = 1.0 / r->i_ss.channels;
+
+ /* Please note that a channel connected to LFE
+ * doesn't really count as connected. */
+ }
+ }
+ }
}
+
+ if (remix) {
+ unsigned
+ ic_unconnected_left = 0,
+ ic_unconnected_right = 0,
+ ic_unconnected_center = 0,
+ ic_unconnected_lfe = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (ic_connected[ic])
+ continue;
+
+ if (on_left(a))
+ ic_unconnected_left++;
+ else if (on_right(a))
+ ic_unconnected_right++;
+ else if (on_center(a))
+ ic_unconnected_center++;
+ else if (on_lfe(a))
+ ic_unconnected_lfe++;
+ }
+
+ if (ic_unconnected_left > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * left. Let's multiply all already connected channels on
+ * the left side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_left(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_left;
+ }
+ }
+ }
+
+ if (ic_unconnected_right > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * right. Let's multiply all already connected channels on
+ * the right side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_right(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_right;
+ }
+ }
+ }
+
+ if (ic_unconnected_center > 0) {
+ pa_bool_t mixed_in = FALSE;
+
+ /* OK, so there are unconnected input channels on the
+ * center. Let's multiply all already connected channels on
+ * the center side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_center(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = .1 / ic_unconnected_center;
+ mixed_in = TRUE;
+ }
+ }
+ }
+
+ if (!mixed_in) {
+
+ /* Hmm, as it appears there was no center channel we
+ could mix our center channel in. In this case, mix
+ it into left and right. Using .375 and 0.75 as
+ factors. */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .75;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .375 / ic_unconnected_center;
+ }
+ }
+ }
+ }
+
+ if (ic_unconnected_lfe > 0) {
+
+ /* OK, so there is an unconnected LFE channel. Let's mix
+ * it into all channels, with factor 0.375 */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (!on_lfe(r->i_cm.map[ic]))
+ continue;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++)
+ r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe;
+ }
+ }
+ }
+
+
+ s = pa_strbuf_new();
+
+ pa_strbuf_printf(s, " ");
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " I%02u ", ic);
+ pa_strbuf_puts(s, "\n +");
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, "------");
+ pa_strbuf_puts(s, "\n");
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ pa_strbuf_printf(s, "O%02u |", oc);
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+
+ pa_strbuf_puts(s, "\n");
+ }
+
+ pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
+ pa_xfree(t);
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -516,6 +929,36 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return &r->buf1;
}
+static void vectoradd_s16_with_fraction(
+ int16_t *d, int dstr,
+ const int16_t *s1, int sstr1,
+ const int16_t *s2, int sstr2,
+ int n,
+ float s3, float s4) {
+
+ int32_t i3, i4;
+
+ i3 = (int32_t) (s3 * 0x10000);
+ i4 = (int32_t) (s4 * 0x10000);
+
+ for (; n > 0; n--) {
+ int32_t a, b;
+
+ a = *s1;
+ b = *s2;
+
+ a = (a * i3) / 0x10000;
+ b = (b * i4) / 0x10000;
+
+ *d = (int16_t) (a + b);
+
+ s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
+ s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
+ d = (int16_t*) ((uint8_t*) d + dstr);
+
+ }
+}
+
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;
@@ -558,16 +1001,21 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
case PA_SAMPLE_FLOAT32NE:
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned i;
+ unsigned ic;
static const float one = 1.0;
- for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++)
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
oil_vectoradd_f32(
(float*) dst + oc, o_skip,
(float*) dst + oc, o_skip,
- (float*) src + r->map_table[oc][i], i_skip,
+ (float*) src + ic, i_skip,
n_frames,
- &one, &one);
+ &one, &r->map_table[oc][ic]);
+ }
}
break;
@@ -575,16 +1023,32 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
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);
+ unsigned ic;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
+ if (r->map_table[oc][ic] >= 1.0) {
+ static const int16_t one = 1;
+
+ oil_vectoradd_s16(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ &one, &one);
+
+ } else
+
+ vectoradd_s16_with_fraction(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ 1.0, r->map_table[oc][ic]);
+ }
}
break;
@@ -616,7 +1080,7 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
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_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
out_n_samples = out_n_frames * r->o_ss.channels;
r->buf3.index = 0;
@@ -737,6 +1201,12 @@ static void libsamplerate_update_rates(pa_resampler *r) {
pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
}
+static void libsamplerate_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ pa_assert_se(src_reset(r->src.state) == 0);
+}
+
static void libsamplerate_free(pa_resampler *r) {
pa_assert(r);
@@ -749,12 +1219,13 @@ static int libsamplerate_init(pa_resampler *r) {
pa_assert(r);
- if (!(r->src.state = src_new(r->resample_method, r->o_ss.channels, &err)))
+ if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err)))
return -1;
r->impl_free = libsamplerate_free;
r->impl_update_rates = libsamplerate_update_rates;
r->impl_resample = libsamplerate_resample;
+ r->impl_reset = libsamplerate_reset;
return 0;
}
@@ -807,24 +1278,35 @@ static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsign
static void speex_update_rates(pa_resampler *r) {
pa_assert(r);
- if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->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(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->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);
}
}
+static void speex_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ pa_assert_se(paspfx_resampler_reset_mem(r->speex.state) == 0);
+ else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert_se(paspfl_resampler_reset_mem(r->speex.state) == 0);
+ }
+}
+
static void speex_free(pa_resampler *r) {
pa_assert(r);
if (!r->speex.state)
return;
- if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->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);
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
paspfl_resampler_destroy(r->speex.state);
}
}
@@ -836,9 +1318,10 @@ static int speex_init(pa_resampler *r) {
r->impl_free = speex_free;
r->impl_update_rates = speex_update_rates;
+ r->impl_reset = speex_reset;
- 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;
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+ q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
pa_log_info("Choosing speex quality setting %i.", q);
@@ -847,8 +1330,8 @@ static int speex_init(pa_resampler *r) {
r->impl_resample = speex_resample_int;
} else {
- 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_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
pa_log_info("Choosing speex quality setting %i.", q);
@@ -909,7 +1392,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
}
}
-static void trivial_update_rates(pa_resampler *r) {
+static void trivial_update_rates_or_reset(pa_resampler *r) {
pa_assert(r);
r->trivial.i_counter = 0;
@@ -922,8 +1405,117 @@ static int trivial_init(pa_resampler*r) {
r->trivial.o_counter = r->trivial.i_counter = 0;
r->impl_resample = trivial_resample;
- r->impl_update_rates = trivial_update_rates;
- r->impl_free = NULL;
+ r->impl_update_rates = trivial_update_rates_or_reset;
+ r->impl_reset = trivial_update_rates_or_reset;
+
+ return 0;
+}
+
+/* Peak finder implementation */
+
+static void peaks_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;
+ unsigned start = 0;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ 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->peaks.o_counter++) {
+ unsigned j;
+
+ j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
+ j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0;
+
+ if (j >= in_n_frames)
+ break;
+
+ pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+ if (r->work_format == PA_SAMPLE_S16NE) {
+ unsigned i, c;
+ int16_t *s = (int16_t*) ((uint8_t*) src + fz * j);
+ int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ int16_t n;
+
+ n = *s < 0 ? -*s : *s;
+
+ if (n > r->peaks.max_i[c])
+ r->peaks.max_i[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_i[c];
+ r->peaks.max_i[c] = 0;
+ }
+ } else {
+ unsigned i, c;
+ float *s = (float*) ((uint8_t*) src + fz * j);
+ float *d = (float*) ((uint8_t*) dst + fz * o_index);
+
+ pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ float n = fabsf(*s);
+
+ if (n > r->peaks.max_f[c])
+ r->peaks.max_f[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_f[c];
+ r->peaks.max_f[c] = 0;
+ }
+ }
+
+ start = j+1;
+ }
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = o_index;
+
+ r->peaks.i_counter += in_n_frames;
+
+ /* Normalize counters */
+ while (r->peaks.i_counter >= r->i_ss.rate) {
+ pa_assert(r->peaks.o_counter >= r->o_ss.rate);
+
+ r->peaks.i_counter -= r->i_ss.rate;
+ r->peaks.o_counter -= r->o_ss.rate;
+ }
+}
+
+static void peaks_update_rates_or_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ r->peaks.i_counter = 0;
+ r->peaks.o_counter = 0;
+}
+
+static int peaks_init(pa_resampler*r) {
+ pa_assert(r);
+
+ r->peaks.o_counter = r->peaks.i_counter = 0;
+ memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
+ memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
+
+ r->impl_resample = peaks_resample;
+ r->impl_update_rates = peaks_update_rates_or_reset;
+ r->impl_reset = peaks_update_rates_or_reset;
return 0;
}
@@ -1051,9 +1643,5 @@ static int copy_init(pa_resampler *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 23e1acb7..5e302a9b 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -1,8 +1,6 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,9 +44,16 @@ typedef enum pa_resample_method {
PA_RESAMPLER_FFMPEG,
PA_RESAMPLER_AUTO, /* automatic select based on sample format */
PA_RESAMPLER_COPY,
+ PA_RESAMPLER_PEAKS,
PA_RESAMPLER_MAX
} pa_resample_method_t;
+typedef enum pa_resample_flags {
+ PA_RESAMPLER_VARIABLE_RATE = 1,
+ PA_RESAMPLER_NO_REMAP = 2, /* implies NO_REMIX */
+ PA_RESAMPLER_NO_REMIX = 4
+} pa_resample_flags_t;
+
pa_resampler* pa_resampler_new(
pa_mempool *pool,
const pa_sample_spec *a,
@@ -56,13 +61,16 @@ pa_resampler* pa_resampler_new(
const pa_sample_spec *b,
const pa_channel_map *bm,
pa_resample_method_t resample_method,
- int variable_rate);
+ pa_resample_flags_t flags);
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);
+/* Inverse of pa_resampler_request() */
+size_t pa_resampler_result(pa_resampler *r, size_t in_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);
@@ -75,6 +83,9 @@ 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);
+/* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */
+void pa_resampler_reset(pa_resampler *r);
+
/* Return the resampling method of the resampler object */
pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
@@ -87,4 +98,5 @@ 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
index 07d776e4..f33de830 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -96,3 +94,24 @@ pa_usec_t pa_rtclock_usec(void) {
return pa_timeval_load(pa_rtclock_get(&tv));
}
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
+
+#ifdef HAVE_CLOCK_GETTIME
+ struct timeval wc_now, rt_now;
+
+ pa_gettimeofday(&wc_now);
+ pa_rtclock_get(&rt_now);
+
+ pa_assert(tv);
+
+ if (pa_timeval_cmp(&wc_now, tv) < 0)
+ pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
+ else
+ pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
+
+ *tv = rt_now;
+#endif
+
+ return tv;
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
index f0360af3..705de48f 100644
--- a/src/pulsecore/rtclock.h
+++ b/src/pulsecore/rtclock.h
@@ -1,8 +1,6 @@
#ifndef foopulsertclockhfoo
#define foopulsertclockhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,4 +38,6 @@ pa_bool_t pa_rtclock_hrtimer(void);
/* timer with a resolution better than this are considered high-resolution */
#define PA_HRTIMER_THRESHOLD_USEC 10
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 354c4c0e..a67a5516 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -63,7 +61,6 @@ struct pa_rtpoll {
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;
@@ -72,6 +69,7 @@ struct pa_rtpoll {
int rtsig;
sigset_t sigset_unblocked;
timer_t timer;
+ pa_bool_t timer_armed;
#ifdef __linux__
pa_bool_t dont_use_ppoll;
#endif
@@ -99,7 +97,7 @@ struct pa_rtpoll_item {
PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
-static void signal_handler_noop(int s) { }
+static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
pa_rtpoll *pa_rtpoll_new(void) {
pa_rtpoll *p;
@@ -131,6 +129,7 @@ pa_rtpoll *pa_rtpoll_new(void) {
p->rtsig = -1;
sigemptyset(&p->sigset_unblocked);
p->timer = (timer_t) -1;
+ p->timer_armed = FALSE;
#endif
@@ -139,7 +138,6 @@ pa_rtpoll *pa_rtpoll_new(void) {
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;
@@ -161,8 +159,10 @@ void pa_rtpoll_install(pa_rtpoll *p) {
p->installed = 1;
#ifdef HAVE_PPOLL
+# ifdef __linux__
if (p->dont_use_ppoll)
return;
+# endif
if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
pa_log_warn("Failed to reserve POSIX realtime signal.");
@@ -366,15 +366,13 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
if (p->rebuild_needed)
rtpoll_rebuild(p);
+ memset(&timeout, 0, sizeof(timeout));
+
/* Calculate timeout */
- if (!wait || p->quit) {
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- } else if (p->timer_enabled) {
+ if (wait && !p->quit && 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));
}
@@ -389,14 +387,14 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
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);
+ r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || 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);
+ r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
if (r < 0) {
if (errno == EAGAIN || errno == EINTR)
@@ -407,21 +405,6 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
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) {
@@ -479,26 +462,35 @@ static void update_timer(pa_rtpoll *p) {
if (p->timer != (timer_t) -1) {
struct itimerspec its;
- memset(&its, 0, sizeof(its));
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
+ sigset_t ss;
+
+ if (p->timer_armed) {
+ /* First disarm timer */
+ memset(&its, 0, sizeof(its));
+ pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+
+ /* Remove a signal that might be waiting in the signal q */
+ pa_assert_se(sigemptyset(&ss) == 0);
+ pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+ sigtimedwait(&ss, NULL, &ts);
+ }
+ /* And install the new timer */
if (p->timer_enabled) {
+ memset(&its, 0, sizeof(its));
+
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)
+ if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 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);
}
- pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+ p->timer_armed = p->timer_enabled;
}
#ifdef __linux__
@@ -508,23 +500,10 @@ static void update_timer(pa_rtpoll *p) {
#endif
}
-void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) {
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
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);
+ pa_timeval_store(&p->next_elapse, usec);
p->timer_enabled = TRUE;
update_timer(p);
@@ -533,7 +512,9 @@ 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) {
pa_assert(p);
- p->period = 0;
+ /* Scheduling a timeout for more than an hour is very very suspicious */
+ pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
+
pa_rtclock_get(&p->next_elapse);
pa_timeval_add(&p->next_elapse, usec);
p->timer_enabled = TRUE;
@@ -544,7 +525,6 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
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;
@@ -681,23 +661,23 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio
return i;
}
-static int asyncmsgq_before(pa_rtpoll_item *i) {
+static int asyncmsgq_read_before(pa_rtpoll_item *i) {
pa_assert(i);
- if (pa_asyncmsgq_before_poll(i->userdata) < 0)
+ if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
return 1; /* 1 means immediate restart of the loop */
return 0;
}
-static void asyncmsgq_after(pa_rtpoll_item *i) {
+static void asyncmsgq_read_after(pa_rtpoll_item *i) {
pa_assert(i);
pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
- pa_asyncmsgq_after_poll(i->userdata);
+ pa_asyncmsgq_read_after_poll(i->userdata);
}
-static int asyncmsgq_work(pa_rtpoll_item *i) {
+static int asyncmsgq_read_work(pa_rtpoll_item *i) {
pa_msgobject *object;
int code;
void *data;
@@ -723,7 +703,7 @@ static int asyncmsgq_work(pa_rtpoll_item *i) {
return 0;
}
-pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
pa_rtpoll_item *i;
struct pollfd *pollfd;
@@ -733,12 +713,47 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t
i = pa_rtpoll_item_new(p, prio, 1);
pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
- pollfd->fd = pa_asyncmsgq_get_fd(q);
+ pollfd->fd = pa_asyncmsgq_read_fd(q);
pollfd->events = POLLIN;
- i->before_cb = asyncmsgq_before;
- i->after_cb = asyncmsgq_after;
- i->work_cb = asyncmsgq_work;
+ i->before_cb = asyncmsgq_read_before;
+ i->after_cb = asyncmsgq_read_after;
+ i->work_cb = asyncmsgq_read_work;
+ i->userdata = q;
+
+ return i;
+}
+
+static int asyncmsgq_write_before(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_asyncmsgq_write_before_poll(i->userdata);
+ return 0;
+}
+
+static void asyncmsgq_write_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_asyncmsgq_write_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(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_write_fd(q);
+ pollfd->events = POLLIN;
+
+ i->before_cb = asyncmsgq_write_before;
+ i->after_cb = asyncmsgq_write_after;
+ i->work_cb = NULL;
i->userdata = q;
return i;
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
index d38d0864..08776ef0 100644
--- a/src/pulsecore/rtpoll.h
+++ b/src/pulsecore/rtpoll.h
@@ -1,8 +1,6 @@
#ifndef foopulsertpollhfoo
#define foopulsertpollhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -58,7 +56,7 @@ 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_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);
@@ -74,8 +72,7 @@ void pa_rtpoll_install(pa_rtpoll *p);
* 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_absolute(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);
@@ -107,7 +104,8 @@ 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);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(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 */
diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c
index bfc49c88..4df217c3 100644
--- a/src/pulsecore/rtsig.c
+++ b/src/pulsecore/rtsig.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
index 7830d272..e414122d 100644
--- a/src/pulsecore/rtsig.h
+++ b/src/pulsecore/rtsig.h
@@ -1,8 +1,6 @@
#ifndef foopulsertsighfoo
#define foopulsertsighfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 21771302..b42b79d1 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,35 +33,13 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
#include "sample-util.h"
#include "endianmacros.h"
#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;
- pa_assert(pool);
- pa_assert(spec);
-
- 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 = (length+fs-1)/fs;
-
- if (length <= 0)
- 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) {
void *data;
@@ -73,10 +49,11 @@ pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *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) {
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
void *data;
pa_assert(c);
@@ -86,35 +63,90 @@ void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *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;
- pa_assert(p);
- pa_assert(length > 0);
- pa_assert(spec);
+ return c;
+}
- switch (spec->format) {
+static uint8_t silence_byte(pa_sample_format_t format) {
+ switch (format) {
case PA_SAMPLE_U8:
- c = 0x80;
- break;
+ return 0x80;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
- case PA_SAMPLE_FLOAT32:
- case PA_SAMPLE_FLOAT32RE:
- c = 0;
- break;
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ return 0;
case PA_SAMPLE_ALAW:
- c = 0xd5;
- break;
+ return 0xd5;
case PA_SAMPLE_ULAW:
- c = 0xff;
- break;
+ return 0xff;
default:
pa_assert_not_reached();
}
+ return 0;
+}
+
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+ pa_assert(p);
+ pa_assert(length > 0);
+ pa_assert(spec);
- memset(p, c, length);
+ memset(p, silence_byte(spec->format), length);
+ return p;
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].i = (int32_t) (pa_sw_volume_to_linear(m->volume.values[channel]) * 0x10000);
+ }
+ }
+}
+
+static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+}
+
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].f = pa_sw_volume_to_linear(m->volume.values[channel]);
+ }
+ }
+}
+
+static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
}
size_t pa_mix(
@@ -124,11 +156,11 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute) {
+ pa_bool_t mute) {
pa_cvolume full_volume;
- size_t d = 0;
unsigned k;
+ size_t d = 0;
pa_assert(streams);
pa_assert(data);
@@ -139,50 +171,49 @@ size_t pa_mix(
volume = pa_cvolume_reset(&full_volume, spec->channels);
for (k = 0; k < nstreams; k++)
- streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock);
+ streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index;
switch (spec->format) {
+
case PA_SAMPLE_S16NE:{
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
-
- sum += v;
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int16_t*) m->ptr);
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
-
- sum = CLAMP(sum, -0x8000, 0x7FFF);
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
}
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
*((int16_t*) data) = (int16_t) sum;
+
data = (uint8_t*) data + sizeof(int16_t);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -191,45 +222,135 @@ size_t pa_mix(
case PA_SAMPLE_S16RE:{
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT16_SWAP(*((int16_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+ }
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
- if (d >= streams[i].chunk.length)
- goto finish;
+ data = (uint8_t*) data + sizeof(int16_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = PA_INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)));
+ break;
+ }
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
+ case PA_SAMPLE_S32NE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
- sum += v;
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int32_t*) m->ptr);
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = (int32_t) sum;
+
+ data = (uint8_t*) data + sizeof(int32_t);
- sum = CLAMP(sum, -0x8000, 0x7FFF);
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32RE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
}
- *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
- data = (uint8_t*) data + sizeof(int16_t);
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = PA_INT32_SWAP((int32_t) sum);
- if (++channel >= spec->channels)
+ data = (uint8_t*) data + sizeof(int32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -238,45 +359,133 @@ size_t pa_mix(
case PA_SAMPLE_U8: {
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d ++) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+ v = (v * cv) / 0x10000;
+ }
- if (d >= streams[i].chunk.length)
- goto finish;
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80;
+ sum = (sum * linear[channel]) / 0x10000;
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
+ *((uint8_t*) data) = (uint8_t) (sum + 0x80);
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
+ data = (uint8_t*) data + 1;
- sum += v;
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_14linear2ulaw(sum >> 2);
+
+ data = (uint8_t*) data + 1;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- sum = CLAMP(sum, -0x80, 0x7F);
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
}
- *((uint8_t*) data) = (uint8_t) (sum + 0x80);
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_13linear2alaw(sum >> 3);
+
data = (uint8_t*) data + 1;
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -285,43 +494,88 @@ size_t pa_mix(
case PA_SAMPLE_FLOAT32NE: {
unsigned channel = 0;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
for (d = 0;; d += sizeof(float)) {
float sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((float*) m->ptr);
+ v *= cv;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
+ }
+
+ sum *= linear[channel];
+ *((float*) data) = sum;
- for (i = 0; i < nstreams; i++) {
- float v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ data = (uint8_t*) data + sizeof(float);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
- if (d >= streams[i].chunk.length)
- goto finish;
+ break;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
+ case PA_SAMPLE_FLOAT32RE: {
+ unsigned channel = 0;
+ float linear[PA_CHANNELS_MAX];
- if (cvolume != PA_VOLUME_NORM)
- v *= pa_sw_volume_to_linear(cvolume);
- }
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
- sum += v;
+ for (d = 0;; d += sizeof(float)) {
+ float sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ uint32_t z = *(uint32_t*) m->ptr;
+ z = PA_UINT32_SWAP(z);
+ v = *((float*) &z);
+ v *= cv;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum *= pa_sw_volume_to_linear(volume->values[channel]);
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
}
- *((float*) data) = sum;
+ sum *= linear[channel];
+ *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum);
+
data = (uint8_t*) data + sizeof(float);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -330,7 +584,7 @@ size_t pa_mix(
default:
pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
- abort();
+ pa_assert_not_reached();
}
finish:
@@ -354,6 +608,9 @@ void pa_volume_memchunk(
pa_assert(c->length % pa_frame_size(spec) == 0);
pa_assert(volume);
+ if (pa_memblock_is_silence(c->memblock))
+ return;
+
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
return;
@@ -362,7 +619,7 @@ void pa_volume_memchunk(
return;
}
- ptr = pa_memblock_acquire(c->memblock);
+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
switch (spec->format) {
@@ -372,18 +629,17 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ for (channel = 0, d = ptr, 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);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (int16_t) t;
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
@@ -395,18 +651,62 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
int32_t t;
t = (int32_t)(PA_INT16_SWAP(*d));
t = (t * linear[channel]) / 0x10000;
- t = CLAMP(t, -0x8000, 0x7FFF);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = PA_INT16_SWAP((int16_t) t);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = (int32_t) t;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(PA_INT32_SWAP(*d));
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = PA_INT32_SWAP((int32_t) t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -419,18 +719,61 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) {
+ for (channel = 0, d = ptr, 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);
+ t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_ulaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_14linear2ulaw(t >> 2);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_alaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_13linear2alaw(t >> 3);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
@@ -442,14 +785,14 @@ void pa_volume_memchunk(
unsigned n;
unsigned channel;
- d = (float*) ((uint8_t*) ptr + c->index);
+ d = ptr;
skip = spec->channels * sizeof(float);
n = c->length/sizeof(float)/spec->channels;
- for (channel = 0; channel < spec->channels ; channel ++) {
+ for (channel = 0; channel < spec->channels; channel ++) {
float v, *t;
- if (volume->values[channel] == PA_VOLUME_NORM)
+ if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
continue;
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
@@ -459,6 +802,32 @@ void pa_volume_memchunk(
break;
}
+ case PA_SAMPLE_FLOAT32RE: {
+ uint32_t *d;
+ size_t n;
+ unsigned channel;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(float); n > 0; d++, n--) {
+ float t;
+ uint32_t z;
+
+ z = PA_UINT32_SWAP(*d);
+ t = *(float*) &z;
+ t *= linear[channel];
+ z = *(uint32_t*) &t;
+ *d = PA_UINT32_SWAP(z);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+
default:
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 */
@@ -542,3 +911,117 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
}
}
}
+
+static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
+ pa_memblock *b;
+ size_t length;
+ void *data;
+
+ pa_assert(pool);
+
+ length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
+
+ b = pa_memblock_new(pool, length);
+
+ data = pa_memblock_acquire(b);
+ memset(data, c, length);
+ pa_memblock_release(b);
+
+ pa_memblock_set_is_silence(b, TRUE);
+
+ return b;
+}
+
+void pa_silence_cache_init(pa_silence_cache *cache) {
+ pa_assert(cache);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+void pa_silence_cache_done(pa_silence_cache *cache) {
+ pa_sample_format_t f;
+ pa_assert(cache);
+
+ for (f = 0; f < PA_SAMPLE_MAX; f++)
+ if (cache->blocks[f])
+ pa_memblock_unref(cache->blocks[f]);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
+ pa_memblock *b;
+ size_t l;
+
+ pa_assert(cache);
+ pa_assert(pa_sample_spec_valid(spec));
+
+ if (!(b = cache->blocks[spec->format]))
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
+ break;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
+ cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
+ break;
+ case PA_SAMPLE_ALAW:
+ cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
+ break;
+ case PA_SAMPLE_ULAW:
+ cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_assert(b);
+
+ ret->memblock = pa_memblock_ref(b);
+
+ l = pa_memblock_get_length(b);
+ if (length > l || length == 0)
+ length = l;
+
+ ret->length = pa_frame_align(length, spec);
+ ret->index = 0;
+
+ return ret;
+}
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
+ const float *s;
+ float *d;
+
+ s = src; d = dst;
+
+ if (format == PA_SAMPLE_FLOAT32NE) {
+
+ float minus_one = -1.0, plus_one = 1.0;
+ oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one);
+
+ } else {
+ pa_assert(format == PA_SAMPLE_FLOAT32RE);
+
+ for (; n > 0; n--) {
+ float f;
+
+ f = PA_FLOAT32_SWAP(*s);
+ f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0);
+ *d = PA_FLOAT32_SWAP(f);
+
+ s = (const float*) ((const uint8_t*) s + sstr);
+ d = (float*) ((uint8_t*) d + dstr);
+ }
+ }
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 0a39d5ca..cef70750 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -1,8 +1,6 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,16 +28,31 @@
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
-pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
-pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length);
-void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
-void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+typedef struct pa_silence_cache {
+ pa_memblock* blocks[PA_SAMPLE_MAX];
+} pa_silence_cache;
+
+void pa_silence_cache_init(pa_silence_cache *cache);
+void pa_silence_cache_done(pa_silence_cache *cache);
+
+void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
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() */
+
+ /* The following fields are used internally by pa_mix(), should
+ * not be initialised by the caller of pa_mix(). */
+ void *ptr;
+ union {
+ int32_t i;
+ float f;
+ } linear[PA_CHANNELS_MAX];
} pa_mix_info;
size_t pa_mix(
@@ -49,7 +62,7 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute);
+ pa_bool_t mute);
void pa_volume_memchunk(
pa_memchunk*c,
@@ -63,4 +76,6 @@ 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);
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
+
#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index f74d0282..e7e1449a 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,12 +28,27 @@
#define INT16_FROM PA_INT16_FROM_BE
#define INT16_TO PA_INT16_TO_BE
+#define INT32_FROM PA_INT32_FROM_BE
+#define INT32_TO PA_INT32_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
+#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
+
+#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
+#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+
+#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
+
+#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
+#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 0
#else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index ad034489..60580815 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16befoo
#define foosconv_s16befoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,11 +29,31 @@ 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);
+void pa_sconv_s32be_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_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
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
#endif
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 6925052c..41670f27 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,7 +23,10 @@
#include <config.h>
#endif
+/* Despite the name of this file we implement S32 handling here, too. */
+
#include <inttypes.h>
+#include <stdio.h>
#include <liboil/liboilfuncs.h>
@@ -45,6 +46,14 @@
#define INT16_TO PA_INT16_TO_LE
#endif
+#ifndef INT32_FROM
+#define INT32_FROM PA_INT32_FROM_LE
+#endif
+
+#ifndef INT32_TO
+#define INT32_TO PA_INT32_TO_LE
+#endif
+
#ifndef SWAP_WORDS
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 1
@@ -72,6 +81,25 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
#endif
}
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ *(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ }
+
+#else
+{
+ static const double add = 0, factor = 1.0/0x7FFFFFFF;
+ oil_scaleconv_f32_s32(b, a, n, &add, &factor);
+}
+#endif
+}
+
void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
@@ -82,7 +110,7 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
int16_t s;
float v = *(a++);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
*(b++) = INT16_TO(s);
}
@@ -95,6 +123,29 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
#endif
}
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * (double) 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+
+#else
+{
+ static const double add = 0, factor = 0x7FFFFFFF;
+ oil_scaleconv_s32_f32(b, a, n, &add, &factor);
+}
+#endif
+}
+
void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
pa_assert(a);
pa_assert(b);
@@ -108,6 +159,19 @@ void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
}
}
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ float k = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ uint32_t *j = (uint32_t*) &k;
+ *j = PA_UINT32_SWAP(*j);
+ *(b++) = k;
+ }
+}
+
void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
@@ -117,8 +181,69 @@ void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
float v = *(a++);
uint32_t *j = (uint32_t*) &v;
*j = PA_UINT32_SWAP(*j);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
*(b++) = INT16_TO(s);
}
}
+
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+ uint32_t *j = (uint32_t*) &v;
+ *j = PA_UINT32_SWAP(*j);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+}
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = (int16_t) (INT32_FROM(*a) >> 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
+ *b = PA_UINT32_SWAP(s);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = INT32_TO(((int32_t) *a) << 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16;
+ *b = INT32_TO(s);
+ a++;
+ b++;
+ }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index 4203315a..8d4c87c3 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16lefoo
#define foosconv_s16lefoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,11 +29,31 @@ 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);
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_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
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
#endif
#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 7f5da63f..581e4681 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -130,7 +128,7 @@ static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--) {
float v = *(a++);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
v *= 0x1FFF;
*(b++) = st_14linear2ulaw((int16_t) v);
}
@@ -168,7 +166,7 @@ static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--, a++, b++) {
float v = *a;
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
v *= 0xFFF;
*b = st_13linear2alaw((int16_t) v);
}
@@ -198,6 +196,8 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
[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_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
};
@@ -214,6 +214,8 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
[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_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_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,
@@ -234,6 +236,8 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
[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_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
};
@@ -252,6 +256,8 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
[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_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
};
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index 901f50a3..59710369 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -1,8 +1,6 @@
#ifndef foosconvhfoo
#define foosconvhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
index 750c2afc..7c9f859d 100644
--- a/src/pulsecore/semaphore-posix.c
+++ b/src/pulsecore/semaphore-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
index f6576348..41e0b8d4 100644
--- a/src/pulsecore/semaphore-win32.c
+++ b/src/pulsecore/semaphore-win32.c
@@ -1,5 +1,3 @@
-/* $Id: mutex-win32.c 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h
index c394e0f2..850ae817 100644
--- a/src/pulsecore/semaphore.h
+++ b/src/pulsecore/semaphore.h
@@ -1,8 +1,6 @@
#ifndef foopulsesemaphorehfoo
#define foopulsesemaphorehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 02f6a7bd..298bf716 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,6 +40,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
@@ -56,7 +55,7 @@
#define MADV_REMOVE 9
#endif
-#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20))
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
#ifdef __linux__
/* On Linux we know that the shared memory blocks are files in
@@ -69,37 +68,38 @@
#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 {
+/* We now put this SHM marker at the end of each segment. It's
+ * optional, to not require a reboot when upgrading, though */
+struct shm_marker PA_GCC_PACKED {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
- void *_reserverd1;
- void *_reserverd2;
- void *_reserverd3;
- void *_reserverd4;
+ uint64_t *_reserverd1;
+ uint64_t *_reserverd2;
+ uint64_t *_reserverd3;
+ uint64_t *_reserverd4;
};
-
+
static char *segment_name(char *fn, size_t l, unsigned id) {
pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
}
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
char fn[32];
int fd = -1;
pa_assert(m);
pa_assert(size > 0);
- pa_assert(size < MAX_SHM_SIZE);
+ 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;
m->size = size;
@@ -122,12 +122,12 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
m->ptr = pa_xmalloc(m->size);
#endif
- m->do_unlink = 0;
+ m->do_unlink = FALSE;
} else {
#ifdef HAVE_SHM_OPEN
struct shm_marker *marker;
-
+
pa_random(&m->id, sizeof(m->id));
segment_name(fn, sizeof(fn), m->id);
@@ -153,9 +153,9 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
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;
+ m->do_unlink = TRUE;
#else
return -1;
#endif
@@ -185,7 +185,7 @@ void pa_shm_free(pa_shm *m) {
#ifdef MAP_FAILED
pa_assert(m->ptr != MAP_FAILED);
#endif
-
+
if (!m->shared) {
#ifdef MAP_ANONYMOUS
if (munmap(m->ptr, m->size) < 0)
@@ -199,12 +199,12 @@ void pa_shm_free(pa_shm *m) {
#ifdef HAVE_SHM_OPEN
if (munmap(m->ptr, m->size) < 0)
pa_log("munmap() failed: %s", pa_cstrerror(errno));
-
+
if (m->do_unlink) {
char fn[32];
-
+
segment_name(fn, sizeof(fn), m->id);
-
+
if (shm_unlink(fn) < 0)
pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
}
@@ -236,7 +236,7 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
/* Align this to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
-
+
if (o > 0) {
ps = PA_PAGE_SIZE;
ptr = (uint8_t*) ptr + (ps - o);
@@ -282,7 +282,9 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
goto fail;
}
- 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) {
+ if (st.st_size <= 0 ||
+ st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+ PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
}
@@ -318,6 +320,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
int pa_shm_cleanup(void) {
+#ifdef HAVE_SHM_OPEN
#ifdef SHM_PATH
DIR *d;
struct dirent *de;
@@ -333,13 +336,13 @@ int pa_shm_cleanup(void) {
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;
@@ -347,14 +350,14 @@ int pa_shm_cleanup(void) {
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;
@@ -366,16 +369,17 @@ int pa_shm_cleanup(void) {
}
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)
+ if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
}
closedir(d);
-#endif
+#endif /* SHM_PATH */
+#endif /* HAVE_SHM_OPEN */
return 0;
}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index 270591de..c2adbd07 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -1,8 +1,6 @@
#ifndef foopulseshmhfoo
#define foopulseshmhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,15 +24,17 @@
#include <sys/types.h>
+#include <pulsecore/macro.h>
+
typedef struct pa_shm {
unsigned id;
void *ptr;
size_t size;
- int do_unlink;
- int shared;
+ pa_bool_t do_unlink:1;
+ pa_bool_t shared:1;
} pa_shm;
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode);
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode);
int pa_shm_attach_ro(pa_shm *m, unsigned id);
void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c
new file mode 100644
index 00000000..eac56adb
--- /dev/null
+++ b/src/pulsecore/shmasyncq.c
@@ -0,0 +1,220 @@
+/***
+ 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 "fdsem.h"
+
+/* 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_shmasyncq {
+ pa_fdsem *read_fdsem, *write_fdsem;
+ pa_shmasyncq_data *data;
+};
+
+static int is_power_of_two(unsigned size) {
+ return !(size & (size - 1));
+}
+
+static int reduce(pa_shmasyncq *l, int value) {
+ return value & (unsigned) (l->n_elements - 1);
+}
+
+static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
+ pa_assert(i < l->data->n_elements);
+
+ return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
+}
+
+static void *get_cell_data(pa_atomic_t *a) {
+ return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
+}
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
+ pa_shmasyncq *l;
+
+ pa_assert(n_elements > 0);
+ pa_assert(is_power_of_two(n_elements));
+ pa_assert(element_size > 0);
+ pa_assert(data);
+ pa_assert(fd);
+
+ l = pa_xnew(pa_shmasyncq, 1);
+
+ l->data = data;
+ memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
+
+ l->data->n_elements = n_elements;
+ l->data->element_size = element_size;
+
+ if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) {
+ pa_xfree(l);
+ return NULL;
+ }
+
+ if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
+ pa_fdsem_free(l->read_fdsem);
+ pa_xfree(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
+ pa_assert(l);
+
+ if (free_cb) {
+ void *p;
+
+ while ((p = pa_shmasyncq_pop(l, 0)))
+ free_cb(p);
+ }
+
+ pa_fdsem_free(l->read_fdsem);
+ pa_fdsem_free(l->write_fdsem);
+ pa_xfree(l);
+}
+
+int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_SHMASYNCQ_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_shmasyncq_pop(pa_shmasyncq*l, int wait) {
+ int idx;
+ void *ret;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_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_shmasyncq_get_fd(pa_shmasyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_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_shmasyncq_after_poll(pa_shmasyncq *l) {
+ pa_assert(l);
+
+ pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
new file mode 100644
index 00000000..9845c391
--- /dev/null
+++ b/src/pulsecore/shmasyncq.h
@@ -0,0 +1,60 @@
+#ifndef foopulseshmasyncqhfoo
+#define foopulseshmasyncqhfoo
+
+/***
+ 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>
+#include <pulsecore/macro.h>
+
+/* Similar to pa_asyncq, but stores data in a shared memory segment */
+
+
+struct pa_shmasyncq_data {
+ unsigned n_elements;
+ size_t element_size;
+ unsigned read_idx;
+ unsigned write_idx;
+ pa_fdsem_data read_fdsem_data, write_fdsem_data;
+};
+
+#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
+#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
+#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
+
+typedef struct pa_shmasyncq pa_shmasyncq;
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
+void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
+
+void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
+
+int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_push_commit(pa_shmasyncq *q);
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q);
+int pa_shmasyncq_before_poll(pa_shmasyncq *a);
+void pa_shmasyncq_after_poll(pa_shmasyncq *a);
+
+#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 6f654b61..43e7bb21 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,12 +36,12 @@
#include <pulsecore/log.h>
#include <pulsecore/play-memblockq.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "sink-input.h"
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
-#define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256)
static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
@@ -54,10 +52,18 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
return data;
}
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
pa_assert(data);
@@ -72,20 +78,39 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
data->volume = *volume;
}
-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_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
}
-void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
pa_assert(data);
- data->muted_is_set = TRUE;
- data->muted = !!mute;
+ pa_proplist_free(data->proplist);
}
+/* Called from main context */
+static void reset_callbacks(pa_sink_input *i) {
+ pa_assert(i);
+
+ i->pop = NULL;
+ i->process_rewind = NULL;
+ i->update_max_rewind = NULL;
+ i->update_max_request = NULL;
+ i->update_sink_requested_latency = NULL;
+ i->update_sink_latency_range = NULL;
+ i->attach = NULL;
+ i->detach = NULL;
+ i->suspend = NULL;
+ i->moved = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->state_change = NULL;
+}
+
+/* Called from main context */
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
@@ -93,7 +118,7 @@ pa_sink_input* pa_sink_input_new(
pa_sink_input *i;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -102,10 +127,9 @@ pa_sink_input* pa_sink_input_new(
return NULL;
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);
+ data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
pa_return_null_if_fail(data->sink);
pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
@@ -120,7 +144,7 @@ pa_sink_input* pa_sink_input_new(
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);
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
@@ -133,13 +157,34 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
if (!data->muted_is_set)
- data->muted = 0;
+ data->muted = FALSE;
+
+ if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ data->sample_spec.format = data->sink->sample_spec.format;
+
+ if (flags & PA_SINK_INPUT_FIX_RATE)
+ data->sample_spec.rate = data->sink->sample_spec.rate;
+
+ if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->sink->sample_spec.channels;
+ data->channel_map = data->sink->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
+ /* Due to the fixing of the sample spec the volume might not match anymore */
+ if (data->volume.channels != data->sample_spec.channels)
+ pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
+ return NULL;
+
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.");
return NULL;
@@ -154,7 +199,9 @@ pa_sink_input* pa_sink_input_new(
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
data->resample_method,
- !!(flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
+ ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -169,7 +216,7 @@ pa_sink_input* pa_sink_input_new(
i->core = core;
i->state = PA_SINK_INPUT_INIT;
i->flags = flags;
- i->name = pa_xstrdup(data->name);
+ i->proplist = pa_proplist_copy(data->proplist);
i->driver = pa_xstrdup(data->driver);
i->module = data->module;
i->sink = data->sink;
@@ -192,40 +239,51 @@ pa_sink_input* pa_sink_input_new(
} else
i->sync_next = i->sync_prev = NULL;
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->attach = NULL;
- i->detach = NULL;
- i->suspend = NULL;
+ i->direct_outputs = pa_idxset_new(NULL, NULL);
+
+ reset_callbacks(i);
i->userdata = NULL;
i->thread_info.state = i->state;
+ i->thread_info.attached = FALSE;
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;
+ i->thread_info.requested_sink_latency = (pa_usec_t) -1;
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+ i->thread_info.underrun_for = (uint64_t) -1;
+ i->thread_info.playing_for = 0;
+ i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 0,
+ &i->sink->silence);
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",
+ pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
i->index,
- i->name,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
i->sink->name,
- pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
/* Don't forget to call pa_sink_input_put! */
return i;
}
+/* Called from main context */
static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert(i);
@@ -237,6 +295,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_update_status(i->sink);
}
+/* Called from main context */
static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input *ssync;
pa_assert(i);
@@ -247,8 +306,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
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;
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
update_n_corked(i, state);
i->state = state;
@@ -262,14 +320,23 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
ssync->state = state;
}
- if (state != PA_SINK_INPUT_UNLINKED)
+ if (state != PA_SINK_INPUT_UNLINKED) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+ for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+ for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+ }
+
return 0;
}
+/* Called from main context */
void pa_sink_input_unlink(pa_sink_input *i) {
pa_bool_t linked;
+ pa_source_output *o, *p = NULL;
pa_assert(i);
/* See pa_sink_unlink() for a couple of comments how this function
@@ -277,7 +344,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_ref(i);
- linked = PA_SINK_INPUT_LINKED(i->state);
+ linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked)
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
@@ -293,20 +360,20 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
pa_sink_input_unref(i);
- 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;
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->attach = NULL;
- i->detach = NULL;
- i->suspend = NULL;
+ update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+ i->state = PA_SINK_INPUT_UNLINKED;
+
+ if (linked)
+ if (i->sink->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+
+ reset_callbacks(i);
if (linked) {
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
@@ -317,329 +384,440 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_unref(i);
}
+/* Called from main context */
static void sink_input_free(pa_object *o) {
pa_sink_input* i = PA_SINK_INPUT(o);
pa_assert(i);
pa_assert(pa_sink_input_refcnt(i) == 0);
- if (PA_SINK_INPUT_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_sink_input_unlink(i);
- pa_log_info("Freeing output %u \"%s\"", i->index, i->name);
+ pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!i->thread_info.attached);
- if (i->thread_info.resampled_chunk.memblock)
- pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
+ if (i->thread_info.render_memblockq)
+ pa_memblockq_free(i->thread_info.render_memblockq);
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);
+ if (i->proplist)
+ pa_proplist_free(i->proplist);
+
+ if (i->direct_outputs)
+ pa_idxset_free(i->direct_outputs, NULL, NULL);
+
+ if (i->thread_info.direct_outputs)
+ pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
- pa_xfree(i->name);
pa_xfree(i->driver);
pa_xfree(i);
}
+/* Called from main context */
void pa_sink_input_put(pa_sink_input *i) {
+ pa_sink_input_state_t state;
pa_sink_input_assert_ref(i);
pa_assert(i->state == PA_SINK_INPUT_INIT);
- pa_assert(i->peek);
- pa_assert(i->drop);
- i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+ /* The following fields must be initialized properly */
+ pa_assert(i->pop);
+ pa_assert(i->process_rewind);
+ pa_assert(i->kill);
+
i->thread_info.volume = i->volume;
i->thread_info.muted = i->muted;
- if (i->state == PA_SINK_INPUT_CORKED)
- i->sink->n_corked++;
+ state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
- pa_sink_update_status(i->sink);
+ update_n_corked(i, state);
+ i->state = state;
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
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. */
}
+/* Called from main context */
void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (i->kill)
- i->kill(i);
+ i->kill(i);
}
-pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
- pa_usec_t r = 0;
+/* Called from main context */
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
+ pa_usec_t r[2] = { 0, 0 };
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_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;
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
if (i->get_latency)
- r += i->get_latency(i);
+ r[0] += i->get_latency(i);
+
+ if (sink_latency)
+ *sink_latency = r[1];
- return r;
+ return r[0];
}
/* 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;
+int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+ pa_bool_t do_volume_adj_here;
+ pa_bool_t volume_is_norm;
+ size_t block_size_max_sink, block_size_max_sink_input;
+ size_t ilength;
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(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk);
pa_assert(volume);
- if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
- goto finish;
+/* pa_log_debug("peek"); */
+
+ if (!i->pop)
+ return -1;
+
+ pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
+ i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->thread_info.state == PA_SINK_INPUT_DRAINED);
+
+ /* If there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 0);
- pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED);
+ block_size_max_sink_input = i->thread_info.resampler ?
+ pa_resampler_max_block_size(i->thread_info.resampler) :
+ pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
+
+ block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
/* 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->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->thread_info.silence_memblock);
- chunk->index = 0;
- 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 (slength <= 0)
+ slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
- if (!i->thread_info.resampler) {
- do_volume_adj_here = 0; /* FIXME??? */
- ret = i->peek(i, length, chunk);
- goto finish;
- }
+ if (slength > block_size_max_sink)
+ slength = block_size_max_sink;
+
+ if (i->thread_info.resampler) {
+ ilength = pa_resampler_request(i->thread_info.resampler, slength);
+
+ if (ilength <= 0)
+ ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+ } else
+ ilength = slength;
+
+ if (ilength > block_size_max_sink_input)
+ ilength = block_size_max_sink_input;
+
+ /* If the channel maps of the sink and this stream differ, we need
+ * to adjust the volume *before* we resample. Otherwise we can do
+ * it after and leave it for the sink code */
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
- while (!i->thread_info.resampled_chunk.memblock) {
+ while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
pa_memchunk tchunk;
- size_t l, rmbs;
- l = pa_resampler_request(i->thread_info.resampler, length);
+ /* There's nothing in our render queue. We need to fill it up
+ * with data from the implementor. */
- if (l <= 0)
- l = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->pop(i, ilength, &tchunk) < 0) {
- rmbs = pa_resampler_max_block_size(i->thread_info.resampler);
- if (l > rmbs)
- l = rmbs;
+ /* OK, we're corked or the implementor didn't give us any
+ * data, so let's just hand out silence */
+ pa_atomic_store(&i->thread_info.drained, 1);
+
+ pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE);
+ i->thread_info.playing_for = 0;
+ if (i->thread_info.underrun_for != (uint64_t) -1)
+ i->thread_info.underrun_for += ilength;
+ break;
+ }
- if ((ret = i->peek(i, l, &tchunk)) < 0)
- goto finish;
+ pa_atomic_store(&i->thread_info.drained, 0);
pa_assert(tchunk.length > 0);
+ pa_assert(tchunk.memblock);
+
+ i->thread_info.underrun_for = 0;
+ i->thread_info.playing_for += tchunk.length;
+
+ while (tchunk.length > 0) {
+ pa_memchunk wchunk;
+
+ wchunk = tchunk;
+ pa_memblock_ref(wchunk.memblock);
- if (tchunk.length > l)
- tchunk.length = l;
+ if (wchunk.length > block_size_max_sink_input)
+ wchunk.length = block_size_max_sink_input;
- 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(&wchunk, 0);
+
+ if (i->thread_info.muted)
+ pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
+ else
+ pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+ }
+
+ if (!i->thread_info.resampler)
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
+ else {
+ pa_memchunk rchunk;
+ pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+
+/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
+
+ if (rchunk.memblock) {
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
+ pa_memblock_unref(rchunk.memblock);
+ }
+ }
- /* It might be necessary to adjust the volume here */
- if (do_volume_adj_here && !volume_is_norm) {
- pa_memchunk_make_writable(&tchunk, 0);
+ pa_memblock_unref(wchunk.memblock);
- 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);
+ tchunk.index += wchunk.length;
+ tchunk.length -= wchunk.length;
}
- pa_resampler_run(i->thread_info.resampler, &tchunk, &i->thread_info.resampled_chunk);
pa_memblock_unref(tchunk.memblock);
}
- pa_assert(i->thread_info.resampled_chunk.memblock);
- pa_assert(i->thread_info.resampled_chunk.length > 0);
+ pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0);
- *chunk = i->thread_info.resampled_chunk;
- pa_memblock_ref(i->thread_info.resampled_chunk.memblock);
+ pa_assert(chunk->length > 0);
+ pa_assert(chunk->memblock);
- ret = 0;
+/* pa_log_debug("peeking %lu", (unsigned long) chunk->length); */
-finish:
+ if (chunk->length > block_size_max_sink)
+ chunk->length = block_size_max_sink;
- if (ret >= 0)
- pa_atomic_store(&i->thread_info.drained, 0);
- else if (ret < 0)
- pa_atomic_store(&i->thread_info.drained, 1);
+ /* Let's see if we had to apply the volume adjustment ourselves,
+ * or if this can be done by the sink for us */
- if (ret >= 0) {
- /* Let's see if we had to apply the volume adjustment
- * ourselves, or if this can be done by the sink for us */
-
- 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 if (i->thread_info.muted)
- /* We've both the same channel map, so let's have the sink do the adjustment for us*/
- pa_cvolume_mute(volume, i->sink->sample_spec.channels);
- else
- *volume = i->thread_info.volume;
- }
+ 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 if (i->thread_info.muted)
+ /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+ pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+ else
+ *volume = i->thread_info.volume;
- return ret;
+ return 0;
}
/* Called from thread context */
-void pa_sink_input_drop(pa_sink_input *i, size_t length) {
+void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
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->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
- return;
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+ pa_assert(nbytes > 0);
- if (i->thread_info.move_silence > 0) {
+/* pa_log_debug("dropping %lu", (unsigned long) nbytes); */
- 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 there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 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;
- }
- }
+ pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+ size_t lbq;
+ pa_sink_input_assert_ref(i);
+
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
+
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
- if (length <= 0)
- return;
+ if (nbytes > 0) {
+ pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
+ pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
}
- if (i->thread_info.resampled_chunk.memblock) {
- size_t l = length;
+ if (i->thread_info.rewrite_nbytes == (size_t) -1) {
- if (l > i->thread_info.resampled_chunk.length)
- l = i->thread_info.resampled_chunk.length;
+ /* We were asked to drop all buffered data, and rerequest new
+ * data from implementor the next time push() is called */
- i->thread_info.resampled_chunk.index += l;
- i->thread_info.resampled_chunk.length -= l;
+ pa_memblockq_flush(i->thread_info.render_memblockq);
- 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);
- }
+ } else if (i->thread_info.rewrite_nbytes > 0) {
+ size_t max_rewrite, amount;
+
+ /* Calculate how much make sense to rewrite at most */
+ max_rewrite = nbytes + lbq;
+
+ /* Transform into local domain */
+ if (i->thread_info.resampler)
+ max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
- length -= l;
+ /* Calculate how much of the rewinded data should actually be rewritten */
+ amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
+
+ if (amount > 0) {
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
+
+ /* Tell the implementor */
+ if (i->process_rewind)
+ i->process_rewind(i, amount);
+
+ /* Convert back to to sink domain */
+ if (i->thread_info.resampler)
+ amount = pa_resampler_result(i->thread_info.resampler, amount);
+
+ if (amount > 0)
+ /* Ok, now update the write pointer */
+ pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+
+ if (i->thread_info.rewrite_flush)
+ pa_memblockq_silence(i->thread_info.render_memblockq);
+
+ /* And reset the resampler */
+ if (i->thread_info.resampler)
+ pa_resampler_reset(i->thread_info.resampler);
+ }
}
- if (length > 0) {
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+}
- 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. */
+/* Called from thread context */
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
- while (length > 0) {
- pa_memchunk chunk;
- pa_cvolume volume;
+ pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
- if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) {
- size_t l;
+ if (i->update_max_rewind)
+ i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- pa_memblock_unref(chunk.memblock);
+/* Called from thread context */
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
- l = chunk.length;
- if (l > length)
- l = length;
+ if (i->update_max_request)
+ i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- pa_sink_input_drop(i, l);
- length -= l;
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+ pa_sink_assert_ref(s);
- } else {
- size_t l;
+ if (usec == (pa_usec_t) -1)
+ return usec;
- l = pa_resampler_request(i->thread_info.resampler, length);
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
- /* Hmmm, peeking failed, so let's at least drop
- * the right amount of data */
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
- if (l > 0)
- if (i->drop)
- i->drop(i, l);
+ return usec;
+}
- break;
- }
- }
+/* Called from thread context */
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+ pa_sink_input_assert_ref(i);
- } else {
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ pa_sink_invalidate_requested_latency(i->sink);
- /* We have no resampler, hence let's just drop the data */
+ return usec;
+}
- if (i->drop)
- i->drop(i, length);
- }
+/* Called from main context */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ i->sink->thread_info.requested_latency_valid = FALSE;
}
+
+ return usec;
}
+/* Called from main context */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
+ pa_usec_t usec = 0;
+
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = i->thread_info.requested_sink_latency;
+
+ return usec;
+}
+
+/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(volume);
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_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
+/* Called from main context */
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));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->volume;
}
+/* Called from main context */
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));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute)
return;
@@ -650,23 +828,26 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
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) {
+/* Called from main context */
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- return !!i->muted;
+ return i->muted;
}
+/* Called from main context */
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));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
}
+/* Called from main context */
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_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
@@ -680,38 +861,46 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
return 0;
}
+/* Called from main context */
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
+ const char *old;
pa_sink_input_assert_ref(i);
- if (!i->name && !name)
+ if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
return;
- if (i->name && name && !strcmp(i->name, name))
+ old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
+
+ if (old && name && !strcmp(old, name))
return;
- pa_xfree(i->name);
- i->name = pa_xstrdup(name);
+ if (name)
+ pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SINK_INPUT_LINKED(i->state)) {
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED], i);
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
+/* Called from main context */
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
return i->resample_method;
}
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
pa_resampler *new_resampler;
pa_sink *origin;
- pa_usec_t silence_usec = 0;
- pa_sink_input_move_info info;
+ pa_sink_input_move_hook_data hook_data;
+ pa_source_output *o, *p = NULL;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_sink_assert_ref(dest);
origin = i->sink;
@@ -732,6 +921,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return -1;
}
+ /* Kill directly connected outputs */
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
+
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))
@@ -750,80 +946,20 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
i->resample_method,
- !!(i->flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
+ ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
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);
+ hook_data.sink_input = i;
+ hook_data.destination = dest;
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
- memset(&info, 0, sizeof(info));
- info.sink_input = i;
-
- if (!immediately) {
- pa_usec_t old_latency, new_latency;
-
- /* Let's do a little bit of Voodoo for compensating latency
- * 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);
-
- /* The already resampled data should go to the old sink */
-
- if (old_latency >= new_latency) {
-
- /* The latency of the old sink is larger than the latency
- * of the new sink. Therefore to compensate for the
- * difference we to play silence on the new one for a
- * while */
-
- silence_usec = old_latency - new_latency;
-
- } else {
-
- /* The latency of new sink is larger than the latency of
- * the old sink. Therefore we have to precompute a little
- * and make sure that this is still played on the old
- * sink, until we can play the first sample on the new
- * sink.*/
-
- info.buffer_bytes = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
- }
-
- /* Okey, let's move it */
-
- if (info.buffer_bytes > 0) {
-
- info.ghost_sink_input = pa_memblockq_sink_input_new(
- origin,
- "Ghost Stream",
- &origin->sample_spec,
- &origin->channel_map,
- NULL,
- NULL);
-
- 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;
-
- info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);
- }
- }
-
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, &info, 0, NULL);
-
- if (info.ghost_sink_input) {
- /* Basically, do what pa_sink_input_put() does ...*/
-
- 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);
- }
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
pa_idxset_remove_by_data(origin->inputs, i, NULL);
pa_idxset_put(dest->inputs, i, NULL);
@@ -834,42 +970,34 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
dest->n_corked++;
}
- /* Replace resampler */
+ /* Replace resampler and render queue */
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->thread_info.silence_memblock) {
- pa_memblock_unref(i->thread_info.silence_memblock);
- i->thread_info.silence_memblock = NULL;
- }
- }
-
- /* Dump already resampled data */
- 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);
+ pa_memblockq_free(i->thread_info.render_memblockq);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 0,
+ &i->sink->silence);
}
- /* 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_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+
+ if (i->moved)
+ i->moved(i);
+
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);
@@ -880,30 +1008,61 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return 0;
}
-/* Called from thread context */
+/* Called from IO thread context */
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_sink_input_assert_ref(i);
+
+ if (state == i->thread_info.state)
+ return;
+
+ if ((state == PA_SINK_INPUT_DRAINED || state == 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);
+
+ if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) {
+
+ /* This will tell the implementing sink input driver to rewind
+ * so that the unplayed already mixed data is not lost */
+ pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+
+ } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
+
+ /* OK, we're being uncorked. Make sure we're not rewound when
+ * the hw buffer is remixed and request a remix. */
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+
+ if (i->state_change)
+ i->state_change(i, state);
+
+ i->thread_info.state = state;
+}
+
+/* Called from thread context, except when it is not. */
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);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
+ pa_usec_t sink_usec = 0;
- if (i->thread_info.resampled_chunk.memblock)
- *r += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &i->sink->sample_spec);
+ r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
- if (i->thread_info.move_silence)
- *r += pa_bytes_to_usec(i->thread_info.move_silence, &i->sink->sample_spec);
+ if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0)
+ r[1] += sink_usec;
return 0;
}
@@ -918,26 +1077,28 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
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);
+ pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
- i->thread_info.state = PA_PTR_TO_UINT(userdata);
+ for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+ pa_sink_input_set_state_within_thread(ssync, 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)
+ pa_sink_input_set_state_within_thread(ssync, 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;
+ }
+ case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
+
+ *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec);
+ return 0;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = i->thread_info.requested_sink_latency;
return 0;
}
}
@@ -945,6 +1106,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
return -1;
}
+/* Called from main thread */
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
@@ -953,3 +1115,88 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
return i->state;
}
+
+/* Called from IO context */
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
+ return pa_memblockq_is_empty(i->thread_info.render_memblockq);
+
+ return TRUE;
+}
+
+/* Called from IO context */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) {
+ size_t lbq;
+
+ /* If 'rewrite' is TRUE the sink is rewound as far as requested
+ * and possible and the exact value of this is passed back the
+ * implementor via process_rewind(). If 'flush' is also TRUE all
+ * already rendered data is also dropped.
+ *
+ * If 'rewrite' is FALSE the sink is rewound as far as requested
+ * and possible and the already rendered data is dropped so that
+ * in the next iteration we read new data from the
+ * implementor. This implies 'flush' is TRUE. */
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(i->thread_info.rewrite_nbytes == 0);
+
+ /* We don't take rewind requests while we are corked */
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED)
+ return;
+
+ pa_assert(rewrite || flush);
+
+ /* Calculate how much we can rewind locally without having to
+ * touch the sink */
+ if (rewrite)
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+ else
+ lbq = 0;
+
+ /* Check if rewinding for the maximum is requested, and if so, fix up */
+ if (nbytes <= 0) {
+
+ /* Calculate maximum number of bytes that could be rewound in theory */
+ nbytes = i->sink->thread_info.max_rewind + lbq;
+
+ /* Transform from sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
+ }
+
+ if (rewrite) {
+ /* Make sure to not overwrite over underruns */
+ if (nbytes > i->thread_info.playing_for)
+ nbytes = (size_t) i->thread_info.playing_for;
+
+ i->thread_info.rewrite_nbytes = nbytes;
+ } else
+ i->thread_info.rewrite_nbytes = (size_t) -1;
+
+ i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
+
+ /* Transform to sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+ if (nbytes > lbq)
+ pa_sink_request_rewind(i->sink, nbytes - lbq);
+}
+
+/* Called from main context */
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(ret);
+
+ pa_silence_memchunk_get(
+ &i->sink->core->silence_cache,
+ i->sink->core->mempool,
+ ret,
+ &i->sample_spec,
+ i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
+
+ return ret;
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index fec431f0..c07a7404 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkinputhfoo
#define foopulsesinkinputhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,14 +44,19 @@ typedef enum pa_sink_input_state {
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) {
+static inline pa_bool_t PA_SINK_INPUT_IS_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_DONT_MOVE = 2,
- PA_SINK_INPUT_START_CORKED = 4
+ PA_SINK_INPUT_START_CORKED = 4,
+ PA_SINK_INPUT_NO_REMAP = 8,
+ PA_SINK_INPUT_NO_REMIX = 16,
+ PA_SINK_INPUT_FIX_FORMAT = 32,
+ PA_SINK_INPUT_FIX_RATE = 64,
+ PA_SINK_INPUT_FIX_CHANNELS = 128
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -65,41 +68,69 @@ struct pa_sink_input {
/* 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_state_t state;
pa_sink_input_flags_t flags;
- char *name, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
pa_sink *sink;
+ /* A sink input may be connected to multiple source outputs
+ * directly, so that they don't get mixed data of the entire
+ * source. */
+ pa_idxset *direct_outputs;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_sink_input *sync_prev, *sync_next;
-
+
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);
+ pa_resample_method_t resample_method;
- /* 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);
+ /* Returns the chunk of audio data and drops it from the
+ * queue. Returns -1 on failure. Called from IO thread context. If
+ * data needs to be generated from scratch then please in the
+ * specified length request_nbytes. 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 (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Rewind the queue by the specified number of bytes. Called just
+ * before peek() if it is called at all. Only called if the sink
+ * input driver ever plans to call
+ * pa_sink_input_request_rewind(). Called from IO context. */
+ void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
+
+ /* Called whenever the maximum rewindable size of the sink
+ * changes. Called from IO context. */
+ void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the maxiumum request size of the sink
+ * changes. Called from IO context. */
+ void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the sink
+ * changes. Called from IO context. */
+ void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */
+
+ /* Called whenver the latency range of the sink changes. Called
+ * from IO context. */
+ void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
/* 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 */
+ 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 */
@@ -107,41 +138,52 @@ struct pa_sink_input {
/* 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 */
-
+ void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the the sink this input is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_sink_input *i); /* may be NULL */
+
/* Supposed to unlink and destroy this stream. Called from main
* context. */
- void (*kill) (pa_sink_input *i); /* may be NULL */
+ void (*kill) (pa_sink_input *i); /* may NOT 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. */
+ this stream. Called from main context. This is added to what the
+ PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
- pa_resample_method_t resample_method;
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
struct {
pa_sink_input_state_t state;
pa_atomic_t drained;
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 */
+ /* We maintain a history of resampled audio data here. */
+ pa_memblockq *render_memblockq;
+
+ size_t rewrite_nbytes;
+ pa_bool_t rewrite_flush;
+ uint64_t underrun_for, playing_for;
pa_sink_input *sync_prev, *sync_next;
-
+
pa_cvolume volume;
pa_bool_t muted;
+
+ /* The requested latency for the sink */
+ pa_usec_t requested_sink_latency;
+
+ pa_hashmap *direct_outputs;
} thread_info;
void *userdata;
@@ -156,11 +198,15 @@ enum {
PA_SINK_INPUT_MESSAGE_GET_LATENCY,
PA_SINK_INPUT_MESSAGE_SET_RATE,
PA_SINK_INPUT_MESSAGE_SET_STATE,
+ PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
PA_SINK_INPUT_MESSAGE_MAX
};
typedef struct pa_sink_input_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+
+ const char *driver;
pa_module *module;
pa_client *client;
@@ -170,7 +216,7 @@ typedef struct pa_sink_input_new_data {
pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
pa_bool_t channel_map_is_set;
-
+
pa_cvolume volume;
pa_bool_t volume_is_set;
pa_bool_t muted;
@@ -186,6 +232,12 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
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);
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
+
+typedef struct pa_sink_input_move_hook_data {
+ pa_sink_input *sink_input;
+ pa_sink *destination;
+} pa_sink_input_move_hook_data;
/* To be called by the implementing module only */
@@ -199,39 +251,57 @@ void pa_sink_input_unlink(pa_sink_input* i);
void pa_sink_input_set_name(pa_sink_input *i, const char *name);
-/* Callable by everyone */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
+
+/* Request that the specified number of bytes already written out to
+the hw device is rewritten, if possible. Please note that this is
+only a kind request. The sink driver may not be able to fulfill it
+fully -- or at all. If the request for a rewrite was successful, the
+sink driver will call ->rewind() and pass the number of bytes that
+could be rewound in the HW device. This functionality is required for
+implementing the "zero latency" write-through functionality. */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
+
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
+
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+
+/* Callable by everyone from main thread*/
/* 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);
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
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);
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, pa_bool_t b);
-
-int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
-/* To be used exclusively by the sink driver thread */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver IO 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);
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
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;
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
+
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index dccb34cc..edb023b2 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,7 @@
#include <pulse/introspect.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h>
@@ -47,43 +46,130 @@
#define MAX_MIX_CHANNELS 32
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
static void sink_free(pa_object *s);
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_sink_new_data_done(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->request_rewind = NULL;
+ s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
pa_sink* pa_sink_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags) {
pa_sink *s;
- char *n = NULL;
- char st[256];
- pa_channel_map tmap;
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_source_new_data source_data;
+ const char *dn;
pa_assert(core);
- pa_assert(name);
- pa_assert(spec);
+ pa_assert(data);
+ pa_assert(data->name);
- pa_return_null_if_fail(pa_sample_spec_valid(spec));
+ s = pa_msgobject_new(pa_sink);
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
- 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);
+ pa_sink_new_data_set_name(data, name);
- s = pa_msgobject_new(pa_sink);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
+
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+ if (!data->volume_is_set)
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
@@ -92,71 +178,108 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
- s->flags = 0;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->module = NULL;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
- s->sample_spec = *spec;
- s->channel_map = *map;
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- pa_cvolume_reset(&s->volume, spec->channels);
- s->muted = FALSE;
- s->refresh_volume = s->refresh_mute = FALSE;
+ s->volume = data->volume;
+ s->muted = data->muted;
+ s->refresh_volume = s->refresh_muted = FALSE;
- s->get_latency = NULL;
- s->set_volume = NULL;
- s->get_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
s->rtpoll = NULL;
- s->silence = NULL;
+
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
+
+ s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
+ s->thread_info.state = s->state;
+ s->thread_info.rewind_nbytes = 0;
+ s->thread_info.max_rewind = 0;
+ s->thread_info.max_request = 0;
+ s->thread_info.requested_latency_valid = FALSE;
+ s->thread_info.requested_latency = 0;
+ s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+ s->thread_info.max_latency = 0;
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
- pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+
+ pa_source_new_data_init(&source_data);
+ pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
+ pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
+ source_data.name = pa_sprintf_malloc("%s.monitor", name);
+ source_data.driver = data->driver;
+ source_data.module = data->module;
+
+ dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
+ pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
- n = pa_sprintf_malloc("%s.monitor", name);
+ s->monitor_source = pa_source_new(core, &source_data, 0);
- if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map)))
- pa_log_warn("Failed to create monitor source.");
- else {
- char *d;
- s->monitor_source->monitor_of = s;
- d = pa_sprintf_malloc("Monitor Source of %s", s->name);
- pa_source_set_description(s->monitor_source, d);
- pa_xfree(d);
+ pa_source_new_data_done(&source_data);
+
+ if (!s->monitor_source) {
+ pa_sink_unlink(s);
+ pa_sink_unref(s);
+ return NULL;
}
- pa_xfree(n);
+ s->monitor_source->monitor_of = s;
- 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;
+ pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
return s;
}
+/* Called from main context */
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
int ret;
+ pa_bool_t suspend_change;
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)) {
+ suspend_change =
+ (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+ (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
pa_sink_input *i;
uint32_t idx;
@@ -167,35 +290,40 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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;
}
+/* Called from main context */
void pa_sink_put(pa_sink* s) {
pa_sink_assert_ref(s);
pa_assert(s->state == PA_SINK_INIT);
+
+ /* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SINK_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
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);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
}
+/* Called from main context */
void pa_sink_unlink(pa_sink* s) {
pa_bool_t linked;
pa_sink_input *i, *j = NULL;
@@ -210,7 +338,7 @@ void pa_sink_unlink(pa_sink* s) {
* may be called multiple times on the same sink without bad
* effects. */
- linked = PA_SINK_LINKED(s->state);
+ linked = PA_SINK_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
@@ -230,12 +358,7 @@ void pa_sink_unlink(pa_sink* s) {
else
s->state = PA_SINK_UNLINKED;
- s->get_latency = NULL;
- s->get_volume = NULL;
- s->set_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
if (s->monitor_source)
pa_source_unlink(s->monitor_source);
@@ -246,6 +369,7 @@ void pa_sink_unlink(pa_sink* s) {
}
}
+/* Called from main context */
static void sink_free(pa_object *o) {
pa_sink *s = PA_SINK(o);
pa_sink_input *i;
@@ -253,7 +377,7 @@ static void sink_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_sink_refcnt(s) == 0);
- if (PA_SINK_LINKED(s->state))
+ if (PA_SINK_IS_LINKED(s->state))
pa_sink_unlink(s);
pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
@@ -270,18 +394,21 @@ static void sink_free(pa_object *o) {
pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
- if (s->silence)
- pa_memblock_unref(s->silence);
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
+/* Called from main context */
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_sink_assert_ref(s);
- pa_assert(q);
s->asyncmsgq = q;
@@ -289,18 +416,19 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_source_set_asyncmsgq(s->monitor_source, q);
}
+/* Called from main context */
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
- pa_assert(p);
s->rtpoll = p;
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
}
+/* Called from main context */
int pa_sink_update_status(pa_sink*s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->state == PA_SINK_SUSPENDED)
return 0;
@@ -308,9 +436,10 @@ int pa_sink_update_status(pa_sink*s) {
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
+/* Called from main context */
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (suspend)
return sink_set_state(s, PA_SINK_SUSPENDED);
@@ -318,17 +447,36 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
-void pa_sink_ping(pa_sink *s) {
+/* Called from IO thread context */
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
+ pa_sink_input *i;
+ void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL);
+ /* Make sure the sink code already reset the counter! */
+ pa_assert(s->thread_info.rewind_nbytes <= 0);
+
+ if (nbytes <= 0)
+ return;
+
+ pa_log_debug("Processing rewind...");
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_process_rewind(i, nbytes);
+ }
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+ pa_source_process_rewind(s->monitor_source, nbytes);
}
-static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) {
+/* Called from IO thread context */
+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;
+ size_t mixlength = *length;
pa_sink_assert_ref(s);
pa_assert(info);
@@ -336,8 +484,16 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi
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, length, &info->chunk, &info->volume) < 0)
+ if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
+ continue;
+
+ if (mixlength == 0 || info->chunk.length < mixlength)
+ mixlength = info->chunk.length;
+
+ if (pa_memblock_is_silence(info->chunk.memblock)) {
+ pa_memblock_unref(info->chunk.memblock);
continue;
+ }
info->userdata = pa_sink_input_ref(i);
@@ -349,27 +505,32 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi
maxinfo--;
}
+ if (mixlength > 0)
+ *length = mixlength;
+
return n;
}
-static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) {
+/* Called from IO thread context */
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
pa_sink_input *i;
void *state = NULL;
unsigned p = 0;
unsigned n_unreffed = 0;
pa_sink_assert_ref(s);
+ pa_assert(result);
+ pa_assert(result->memblock);
+ pa_assert(result->length > 0);
/* 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_mix_info* m = NULL;
pa_sink_input_assert_ref(i);
- m = NULL;
-
/* Let's try to find the matching entry info the pa_mix_info array */
for (j = 0; j < n; j ++) {
@@ -384,14 +545,47 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length
}
/* Drop read data */
- pa_sink_input_drop(i, length);
+ pa_sink_input_drop(i, result->length);
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+
+ if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
+ void *ostate = NULL;
+ pa_source_output *o;
+ pa_memchunk c;
+
+ if (m && m->chunk.memblock) {
+ c = m->chunk;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+
+ pa_memchunk_make_writable(&c, 0);
+ pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
+ } else {
+ c = s->silence;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+ }
+
+ while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_assert(o->direct_on_input == i);
+ pa_source_post_direct(s->monitor_source, o, &c);
+ }
+
+ pa_memblock_unref(c.memblock);
+ }
+ }
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);
+ pa_memchunk_reset(&m->chunk);
+
+ pa_sink_input_unref(m->userdata);
+ m->userdata = NULL;
n_unreffed += 1;
}
@@ -408,20 +602,26 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length
pa_memblock_unref(info->chunk.memblock);
}
}
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+ pa_source_post(s->monitor_source, result);
}
+/* Called from IO thread context */
void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
size_t block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
pa_sink_ref(s);
+ s->thread_info.rewind_nbytes = 0;
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -431,24 +631,15 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_assert(length > 0);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, length, info, MAX_MIX_CHANNELS) : 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);
-
- pa_assert(length > 0);
-
- 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 = s->silence;
+ pa_memblock_ref(result->memblock);
- result->memblock = pa_memblock_ref(s->silence);
- result->length = length;
- result->index = 0;
+ if (result->length > length)
+ result->length = length;
} else if (n == 1) {
pa_cvolume volume;
@@ -462,6 +653,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
+ pa_log("adjusting volume ");
pa_memchunk_make_writable(result, 0);
if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
pa_silence_memchunk(result, &s->sample_spec);
@@ -473,27 +665,30 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
result->memblock = pa_memblock_new(s->core->mempool, length);
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);
+ 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->index = 0;
}
if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, result->length);
-
- if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
- pa_source_post(s->monitor_source, result);
+ inputs_drop(s, info, n, result);
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
+ size_t length, block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -501,34 +696,48 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_sink_ref(s);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0;
+ s->thread_info.rewind_nbytes = 0;
+
+ length = target->length;
+ 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 (target->length > length)
+ target->length = length;
+
pa_silence_memchunk(target, &s->sample_spec);
} else if (n == 1) {
- if (target->length > info[0].chunk.length)
- target->length = info[0].chunk.length;
+ pa_cvolume volume;
+
+ if (target->length > length)
+ target->length = length;
+
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
- if (s->thread_info.soft_muted)
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
pa_silence_memchunk(target, &s->sample_spec);
else {
- void *src, *ptr;
- pa_cvolume volume;
-
- ptr = pa_memblock_acquire(target->memblock);
- src = pa_memblock_acquire(info[0].chunk.memblock);
+ pa_memchunk vchunk;
- memcpy((uint8_t*) ptr + target->index,
- (uint8_t*) src + info[0].chunk.index,
- target->length);
+ vchunk = info[0].chunk;
+ pa_memblock_ref(vchunk.memblock);
- pa_memblock_release(target->memblock);
- pa_memblock_release(info[0].chunk.memblock);
+ if (vchunk.length > length)
+ vchunk.length = length;
- pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+ if (!pa_cvolume_is_norm(&volume)) {
+ pa_memchunk_make_writable(&vchunk, 0);
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ }
- if (!pa_cvolume_is_norm(&volume))
- pa_volume_memchunk(target, &s->sample_spec, &volume);
+ pa_memchunk_memcpy(target, &vchunk);
+ pa_memblock_unref(vchunk.memblock);
}
} else {
@@ -537,8 +746,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
ptr = pa_memblock_acquire(target->memblock);
target->length = pa_mix(info, n,
- (uint8_t*) ptr + target->index,
- target->length,
+ (uint8_t*) ptr + target->index, length,
&s->sample_spec,
&s->thread_info.soft_volume,
s->thread_info.soft_muted);
@@ -547,20 +755,18 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
}
if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, target->length);
-
- if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
- pa_source_post(s->monitor_source, target);
+ inputs_drop(s, info, n, target);
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_memchunk chunk;
size_t l, d;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -568,6 +774,8 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_sink_ref(s);
+ s->thread_info.rewind_nbytes = 0;
+
l = target->length;
d = 0;
while (l > 0) {
@@ -584,13 +792,16 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
+ s->thread_info.rewind_nbytes = 0;
+
/*** This needs optimization ***/
result->index = 0;
@@ -600,62 +811,29 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
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);
- }
- }
-}
-
+/* Called from main thread */
pa_usec_t pa_sink_get_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
-
- if (!PA_SINK_OPENED(s->state))
- return 0;
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (s->get_latency)
- return s->get_latency(s);
+ /* The returned value is supposed to be in the time domain of the sound card! */
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ if (!PA_SINK_IS_OPENED(s->state))
return 0;
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
return usec;
}
+/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
- int changed;
+ pa_bool_t changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -665,37 +843,39 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
s->set_volume = NULL;
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);
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
- struct pa_cvolume old_volume;
-
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- old_volume = s->volume;
+ if (s->refresh_volume) {
+ struct pa_cvolume old_volume = s->volume;
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ 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_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+ if (!s->get_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
- if (!pa_cvolume_equal(&old_volume, &s->volume))
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return &s->volume;
}
+/* Called from main thread */
void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
- int changed;
+ pa_bool_t changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -710,71 +890,66 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
pa_bool_t pa_sink_get_mute(pa_sink *s) {
- pa_bool_t old_muted;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- old_muted = s->muted;
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- 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 (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
- if (old_muted != s->muted)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return s->muted;
}
-void pa_sink_set_module(pa_sink *s, pa_module *m) {
- pa_sink_assert_ref(s);
-
- if (s->module == m)
- return;
-
- s->module = m;
-
- if (s->monitor_source)
- pa_source_set_module(s->monitor_source, m);
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
-
+/* Called from main thread */
void pa_sink_set_description(pa_sink *s, const char *description) {
+ const char *old;
pa_sink_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
if (s->monitor_source) {
char *n;
- n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name);
+ n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
pa_source_set_description(s->monitor_source, n);
pa_xfree(n);
}
- if (PA_SINK_LINKED(s->state)) {
+ if (PA_SINK_IS_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);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
}
}
+/* Called from main thread */
unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
@@ -787,31 +962,36 @@ unsigned pa_sink_linked_by(pa_sink *s) {
return ret;
}
+/* Called from main thread */
unsigned pa_sink_used_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_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;
+ return ret - s->n_corked;
}
+/* Called from IO thread, except when it is not */
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink *s = PA_SINK(o);
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);
+
+ /* If you change anything here, make sure to change the
+ * sink input handling a few lines down at
+ * PA_SINK_MESSAGE_FINISH_MOVE, too. */
+
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
@@ -836,9 +1016,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
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. */
+ pa_sink_input_set_state_within_thread(i, i->state);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ /* We don't rewind here automatically. This is left to the
+ * sink input implementor because some sink inputs need a
+ * slow start, i.e. need some time to buffer client
+ * samples before beginning streaming. */
return 0;
}
@@ -848,11 +1036,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
/* 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. */
+ * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
if (i->detach)
i->detach(i);
+ pa_sink_input_set_state_within_thread(i, i->state);
+
pa_assert(i->thread_info.attached);
i->thread_info.attached = FALSE;
@@ -876,82 +1066,94 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
+ pa_sink_invalidate_requested_latency(s);
+ pa_sink_request_rewind(s, 0);
+
return 0;
}
- case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: {
- pa_sink_input_move_info *info = userdata;
- int volume_is_norm;
+ case PA_SINK_MESSAGE_START_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
/* 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);
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
+
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t sink_nbytes, total_nbytes;
+
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+ total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+ if (total_nbytes > 0) {
+ i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
+ i->thread_info.rewrite_flush = TRUE;
+ pa_sink_input_process_rewind(i, sink_nbytes);
+ }
+ }
- if (info->sink_input->detach)
- info->sink_input->detach(info->sink_input);
+ if (i->detach)
+ i->detach(i);
- pa_assert(info->sink_input->thread_info.attached);
- info->sink_input->thread_info.attached = FALSE;
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
- if (info->ghost_sink_input) {
- pa_assert(info->buffer_bytes > 0);
- pa_assert(info->buffer);
+ /* Let's remove the sink input ...*/
+ if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+ pa_sink_input_unref(i);
- volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume);
+ pa_sink_invalidate_requested_latency(s);
- pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes);
+ pa_log_debug("Requesting rewind due to started move");
+ pa_sink_request_rewind(s, 0);
- while (info->buffer_bytes > 0) {
- pa_memchunk memchunk;
- pa_cvolume volume;
- size_t n;
+ return 0;
+ }
- if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0)
- break;
+ case PA_SINK_MESSAGE_FINISH_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
- n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length;
- pa_sink_input_drop(info->sink_input, n);
- memchunk.length = n;
+ /* We don't support moving synchronized streams. */
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
- if (!volume_is_norm) {
- pa_memchunk_make_writable(&memchunk, 0);
- pa_volume_memchunk(&memchunk, &s->sample_spec, &volume);
- }
+ pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
- if (pa_memblockq_push(info->buffer, &memchunk) < 0) {
- pa_memblock_unref(memchunk.memblock);
- break;
- }
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
- pa_memblock_unref(memchunk.memblock);
- info->buffer_bytes -= n;
- }
+ if (i->attach)
+ i->attach(i);
- /* 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_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
- pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer);
+ pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
- pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer));
- }
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t nbytes;
- /* 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);
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
- /* .. 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;
+ nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
- pa_assert(!info->ghost_sink_input->thread_info.attached);
- info->ghost_sink_input->thread_info.attached = TRUE;
+ if (nbytes > 0)
+ pa_sink_input_drop(i, nbytes);
- if (info->ghost_sink_input->attach)
- info->ghost_sink_input->attach(info->ghost_sink_input);
+ pa_log_debug("Requesting rewind due to finished move");
+ pa_sink_request_rewind(s, nbytes);
}
return 0;
@@ -959,10 +1161,14 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_VOLUME:
s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+
+ pa_sink_request_rewind(s, 0);
return 0;
case PA_SINK_MESSAGE_SET_MUTE:
s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+
+ pa_sink_request_rewind(s, 0);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -973,9 +1179,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
*((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);
@@ -983,17 +1186,53 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_DETACH:
- /* We're detaching all our input streams so that the
- * asyncmsgq and rtpoll fields can be changed without
- * problems */
+ /* Detach all streams */
pa_sink_detach_within_thread(s);
- break;
+ return 0;
case PA_SINK_MESSAGE_ATTACH:
/* Reattach all streams */
pa_sink_attach_within_thread(s);
- break;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_sink_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_sink_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_MAX_REQUEST:
+
+ *((size_t*) userdata) = s->thread_info.max_request;
+ return 0;
case PA_SINK_MESSAGE_GET_LATENCY:
case PA_SINK_MESSAGE_MAX:
@@ -1003,6 +1242,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
return -1;
}
+/* Called from main thread */
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
pa_sink *sink;
uint32_t idx;
@@ -1016,26 +1256,29 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
return ret;
}
+/* Called from main thread */
void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
}
+/* Called from main thread */
void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
}
+/* Called from IO thread */
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));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->detach)
@@ -1045,12 +1288,13 @@ void pa_sink_detach_within_thread(pa_sink *s) {
pa_source_detach_within_thread(s->monitor_source);
}
+/* Called from IO thread */
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));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->attach)
@@ -1059,3 +1303,229 @@ void pa_sink_attach_within_thread(pa_sink *s) {
if (s->monitor_source)
pa_source_attach_within_thread(s->monitor_source);
}
+
+/* Called from IO thread */
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ nbytes = s->thread_info.max_rewind;
+
+ nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+ if (nbytes <= s->thread_info.rewind_nbytes)
+ return;
+
+ s->thread_info.rewind_nbytes = nbytes;
+
+ if (s->request_rewind)
+ s->request_rewind(s);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_sink_input *i;
+ void *state = NULL;
+ pa_usec_t monitor_latency;
+
+ pa_sink_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+
+ if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
+ result = i->thread_info.requested_sink_latency;
+
+ monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
+
+ if (monitor_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > monitor_latency))
+ result = monitor_latency;
+
+ if (result != (pa_usec_t) -1) {
+ if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+ result = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+
+ return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
+ pa_usec_t usec = 0;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (!PA_SINK_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ }
+
+ if (s->monitor_source)
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_request == s->thread_info.max_request)
+ return;
+
+ s->thread_info.max_request = max_request;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+ }
+}
+
+/* Called from IO thread */
+void pa_sink_invalidate_requested_latency(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_requested_latency)
+ i->update_sink_requested_latency(i);
+}
+
+/* Called from main thread */
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_sink_assert_ref(s);
+
+ /* min_latency == 0: no limit
+ * min_latency == (size_t) -1: default limit
+ * min_latency anything else: specified limit
+ *
+ * Similar for max_latency */
+
+ if (min_latency == (pa_usec_t) -1)
+ min_latency = DEFAULT_MIN_LATENCY;
+
+ if (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ r[1] = max_latency;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+ } else {
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ s->monitor_source->thread_info.min_latency = min_latency;
+ s->monitor_source->thread_info.max_latency = max_latency;
+
+ s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
+ }
+}
+
+/* Called from main thread */
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+ pa_sink_assert_ref(s);
+ pa_assert(min_latency);
+ pa_assert(max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* Called from IO thread */
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_latency_range)
+ i->update_sink_latency_range(i);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+}
+
+size_t pa_sink_get_max_rewind(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+ return r;
+}
+
+size_t pa_sink_get_max_request(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_request;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
+
+ return r;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ce1d12c3..b73944e8 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkhfoo
#define foopulsesinkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,6 @@ typedef struct pa_sink pa_sink;
#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>
@@ -52,11 +49,11 @@ typedef enum pa_sink_state {
PA_SINK_UNLINKED
} pa_sink_state_t;
-static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_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) {
+static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
}
@@ -69,7 +66,8 @@ struct pa_sink {
pa_sink_flags_t flags;
char *name;
- char *description, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
pa_module *module; /* may be NULL */
@@ -82,29 +80,75 @@ struct pa_sink {
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_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll;
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
+ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
+ int (*get_volume)(pa_sink *s); /* may be NULL */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
+ int (*get_mute)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_sink *s); /* dito */
+
+ /* Called when a rewind request is issued. Called from IO thread
+ * context. */
+ void (*request_rewind)(pa_sink *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_sink *s); /* dito */
+
/* 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_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* The number of bytes streams need to keep around as history to
+ * be able to satisfy every DMA buffer rewrite */
+ size_t max_rewind;
- pa_memblock *silence;
+ /* The number of bytes streams need to keep around to satisfy
+ * every DMA write request */
+ size_t max_request;
+
+ /* Maximum of what clients requested to rewind in this cycle */
+ size_t rewind_nbytes;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
+ } thread_info;
void *userdata;
};
@@ -120,48 +164,79 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
+ PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
PA_SINK_MESSAGE_SET_STATE,
- PA_SINK_MESSAGE_PING,
- PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER,
+ PA_SINK_MESSAGE_START_MOVE,
+ PA_SINK_MESSAGE_FINISH_MOVE,
PA_SINK_MESSAGE_ATTACH,
PA_SINK_MESSAGE_DETACH,
+ PA_SINK_MESSAGE_SET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_MAX_REWIND,
+ PA_SINK_MESSAGE_GET_MAX_REQUEST,
PA_SINK_MESSAGE_MAX
} pa_sink_message_t;
+typedef struct pa_sink_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_sink_new_data;
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
+void pa_sink_new_data_done(pa_sink_new_data *data);
+
/* 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);
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags);
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_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
/* May be called by everyone, from main context */
+/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_sink_get_latency(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+size_t pa_sink_get_max_rewind(pa_sink *s);
+size_t pa_sink_get_max_request(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);
-/* 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, 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);
@@ -178,11 +253,24 @@ 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);
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
void pa_sink_attach_within_thread(pa_sink *s);
void pa_sink_detach_within_thread(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
+
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by sink input drivers, from IO context */
+
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
+
+void pa_sink_invalidate_requested_latency(pa_sink *s);
+
#endif
diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c
index 8d4c6fa7..7e5b186c 100644
--- a/src/pulsecore/sioman.c
+++ b/src/pulsecore/sioman.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h
index 49fffb34..d0cacc9b 100644
--- a/src/pulsecore/sioman.h
+++ b/src/pulsecore/sioman.h
@@ -1,8 +1,6 @@
#ifndef foosiomanhfoo
#define foosiomanhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 5b5bc5ca..e69a63da 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -61,6 +59,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
#include <pulsecore/parseaddr.h>
#include <pulsecore/macro.h>
@@ -77,9 +76,9 @@ struct pa_socket_client {
pa_io_event *io_event;
pa_time_event *timeout_event;
pa_defer_event *defer_event;
- void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata);
+ pa_socket_client_cb_t callback;
void *userdata;
- int local;
+ pa_bool_t local;
#ifdef HAVE_LIBASYNCNS
asyncns_t *asyncns;
asyncns_query_t * asyncns_query;
@@ -87,7 +86,7 @@ struct pa_socket_client {
#endif
};
-static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
+static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
pa_assert(m);
@@ -96,11 +95,11 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
c->mainloop = m;
c->fd = -1;
c->io_event = NULL;
- c->defer_event = NULL;
c->timeout_event = NULL;
+ c->defer_event = NULL;
c->callback = NULL;
c->userdata = NULL;
- c->local = 0;
+ c->local = FALSE;
#ifdef HAVE_LIBASYNCNS
c->asyncns = NULL;
@@ -119,15 +118,15 @@ static void free_events(pa_socket_client *c) {
c->io_event = NULL;
}
- if (c->defer_event) {
- c->mainloop->defer_free(c->defer_event);
- c->defer_event = NULL;
- }
-
if (c->timeout_event) {
c->mainloop->time_free(c->timeout_event);
c->timeout_event = NULL;
}
+
+ if (c->defer_event) {
+ c->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
}
static void do_call(pa_socket_client *c) {
@@ -177,7 +176,7 @@ finish:
pa_socket_client_unref(c);
}
-static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
@@ -223,7 +222,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
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));
+ pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
return 0;
}
@@ -252,8 +251,7 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file
memset(&sa, 0, sizeof(sa));
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_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
@@ -271,22 +269,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
pa_assert(sa);
pa_assert(salen);
- switch (sa->sa_family) {
- case AF_UNIX:
- c->local = 1;
- break;
-
- case AF_INET:
- c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
- break;
-
- case AF_INET6:
- c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
- break;
-
- default:
- c->local = 0;
- }
+ c->local = pa_socket_address_is_local(sa);
if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
@@ -294,6 +277,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
}
pa_make_fd_cloexec(c->fd);
+
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
pa_make_tcp_socket_low_delay(c->fd);
else
@@ -312,7 +296,7 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct
pa_assert(sa);
pa_assert(salen > 0);
- pa_assert_se(c = pa_socket_client_new(m));
+ pa_assert_se(c = socket_client_new(m));
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
@@ -361,7 +345,7 @@ pa_socket_client* pa_socket_client_ref(pa_socket_client *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) {
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -489,23 +473,22 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
hints.ai_socktype = SOCK_STREAM;
-#ifdef HAVE_LIBASYNCNS
+#if defined(HAVE_LIBASYNCNS)
{
asyncns_t *asyncns;
if (!(asyncns = asyncns_new(1)))
goto finish;
- c = pa_socket_client_new(m);
+ pa_assert_se(c = socket_client_new(m));
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);
pa_assert(c->asyncns_query);
start_timeout(c);
}
-#else /* HAVE_LIBASYNCNS */
+#elif defined(HAVE_GETADDRINFO)
{
-#ifdef HAVE_GETADDRINFO
int ret;
struct addrinfo *res = NULL;
@@ -520,7 +503,9 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
}
freeaddrinfo(res);
-#else /* HAVE_GETADDRINFO */
+ }
+#else
+ {
struct hostent *host = NULL;
struct sockaddr_in s;
@@ -546,7 +531,6 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
start_timeout(c);
-#endif /* HAVE_GETADDRINFO */
}
#endif /* HAVE_LIBASYNCNS */
}
@@ -561,7 +545,7 @@ finish:
/* Return non-zero when the target sockaddr is considered
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) {
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index b1d58eff..9ceeaddc 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -1,8 +1,6 @@
#ifndef foosocketclienthfoo
#define foosocketclienthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -34,17 +32,19 @@ struct sockaddr;
typedef struct pa_socket_client pa_socket_client;
+typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
+
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
-void pa_socket_client_unref(pa_socket_client *c);
pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+void pa_socket_client_unref(pa_socket_client *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);
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
-int pa_socket_client_is_local(pa_socket_client *c);
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c);
#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index e0d2f164..9885a02b 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -83,7 +81,7 @@ struct pa_socket_server {
char *filename;
char *tcpwrap_service;
- void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+ pa_socket_server_on_connection_cb_t on_connection;
void *userdata;
pa_io_event *io_event;
@@ -91,11 +89,11 @@ struct pa_socket_server {
enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
};
-static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_server *s = userdata;
pa_iochannel *io;
int nfd;
-
+
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(s->mainloop == mainloop);
@@ -150,7 +148,7 @@ finish:
pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server *s;
-
+
pa_assert(m);
pa_assert(fd >= 0);
@@ -173,7 +171,7 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
+
PA_REFCNT_INC(s);
return s;
}
@@ -195,9 +193,9 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
pa_make_fd_cloexec(fd);
+ memset(&sa, 0, sizeof(sa));
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_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
pa_make_socket_low_delay(fd);
@@ -295,7 +293,7 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_socket_server *ss;
int fd = -1;
struct sockaddr_in6 sa;
- int on = 1;
+ int on;
pa_assert(m);
pa_assert(port > 0);
@@ -308,11 +306,13 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_make_fd_cloexec(fd);
#ifdef IPV6_V6ONLY
+ on = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
#endif
#ifdef SO_REUSEADDR
+ on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
#endif
@@ -426,7 +426,7 @@ void pa_socket_server_unref(pa_socket_server *s) {
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) {
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -507,7 +507,6 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
}
pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
-
}
return c;
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
index 777599e5..1edfb432 100644
--- a/src/pulsecore/socket-server.h
+++ b/src/pulsecore/socket-server.h
@@ -1,8 +1,6 @@
#ifndef foosocketserverhfoo
#define foosocketserverhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,9 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
void pa_socket_server_unref(pa_socket_server*s);
pa_socket_server* pa_socket_server_ref(pa_socket_server *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);
+typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata);
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 085edeb8..f721f699 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -129,8 +127,8 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
return;
#endif
}
-
}
+
#ifndef OS_IS_WIN32
pa_snprintf(c, l, "Unknown network client");
return;
@@ -144,7 +142,7 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
}
void pa_make_socket_low_delay(int fd) {
-
+
#ifdef SO_PRIORITY
int priority;
pa_assert(fd >= 0);
@@ -157,7 +155,7 @@ void pa_make_socket_low_delay(int fd) {
void pa_make_tcp_socket_low_delay(int fd) {
pa_assert(fd >= 0);
-
+
pa_make_socket_low_delay(fd);
#if defined(SOL_TCP) || defined(IPPROTO_TCP)
@@ -171,7 +169,7 @@ void pa_make_tcp_socket_low_delay(int fd) {
pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
}
#endif
-
+
#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
{
int tos = IPTOS_LOWDELAY;
@@ -187,9 +185,9 @@ void pa_make_tcp_socket_low_delay(int fd) {
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;
@@ -284,3 +282,40 @@ int pa_unix_socket_remove_stale(const char *fn) {
}
#endif /* HAVE_SYS_UN_H */
+
+
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
+ pa_assert(sa);
+
+ switch (sa->sa_family) {
+ case AF_UNIX:
+ return TRUE;
+
+ case AF_INET:
+ return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+
+ case AF_INET6:
+ return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+
+ default:
+ return FALSE;
+ }
+}
+
+pa_bool_t pa_socket_is_local(int fd) {
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un un;
+#endif
+ } sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getpeername(fd, &sa.sa, &sa_len) < 0)
+ return FALSE;
+
+ return pa_socket_address_is_local(&sa.sa);
+}
diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h
index a0344c68..7a40285a 100644
--- a/src/pulsecore/socket-util.h
+++ b/src/pulsecore/socket-util.h
@@ -1,8 +1,6 @@
#ifndef foosocketutilhfoo
#define foosocketutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,9 @@
***/
#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <pulsecore/macro.h>
void pa_socket_peer_to_string(int fd, char *c, size_t l);
@@ -39,4 +40,7 @@ int pa_socket_set_rcvbuf(int fd, size_t l);
int pa_unix_socket_is_stale(const char *fn);
int pa_unix_socket_remove_stale(const char *fn);
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa);
+pa_bool_t pa_socket_is_local(int fd);
+
#endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index d9f11a26..8eedf830 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -35,23 +33,30 @@
#include <sndfile.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.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 <pulsecore/sample-util.h>
#include "sound-file-stream.h"
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
typedef struct file_stream {
pa_msgobject parent;
pa_core *core;
- SNDFILE *sndfile;
pa_sink_input *sink_input;
- pa_memchunk memchunk;
+
+ SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
- size_t drop;
+
+ /* We need this memblockq here to easily fulfill rewind requests
+ * (even beyond the file start!) */
+ pa_memblockq *memblockq;
} file_stream;
enum {
@@ -62,29 +67,28 @@ PA_DECLARE_CLASS(file_stream);
#define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+/* Called from main context */
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);
}
+/* Called from main context */
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->memblockq)
+ pa_memblockq_free(u->memblockq);
if (u->sndfile)
sf_close(u->sndfile);
@@ -92,10 +96,11 @@ static void file_stream_free(pa_object *o) {
pa_xfree(u);
}
+/* Called from main context */
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);
@@ -105,117 +110,125 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int
return 0;
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
+ file_stream *u;
+
pa_sink_input_assert_ref(i);
-
- file_stream_unlink(FILE_STREAM(i->userdata));
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ file_stream_unlink(u);
}
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
file_stream *u;
-
- pa_assert(i);
+
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
pa_assert(chunk);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
- if (!u->sndfile)
+ if (!u->memblockq)
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 (n <= 0)
- n = 0;
-
- u->memchunk.length = n * fs;
- } else {
- sf_count_t n;
- void *p;
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- n = sf_read_raw(u->sndfile, p, length);
- pa_memblock_release(u->memchunk.memblock);
-
- if (n <= 0)
- n = 0;
-
- 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;
- }
+ pa_memchunk tchunk;
+ size_t fs;
+ void *p;
+ sf_count_t n;
+
+ if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
+ chunk->length = PA_MIN(chunk->length, length);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+ return 0;
+ }
+
+ if (!u->sndfile)
+ break;
+
+ tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+ tchunk.index = 0;
+
+ p = pa_memblock_acquire(tchunk.memblock);
+
+ if (u->readf_function) {
+ fs = pa_frame_size(&i->sample_spec);
+ n = u->readf_function(u->sndfile, p, length/fs);
+ } else {
+ fs = 1;
+ n = sf_read_raw(u->sndfile, p, length);
}
- pa_assert(u->memchunk.memblock);
- pa_assert(u->memchunk.length > 0);
+ pa_memblock_release(tchunk.memblock);
- if (u->drop < u->memchunk.length) {
- u->memchunk.index += u->drop;
- u->memchunk.length -= u->drop;
- u->drop = 0;
+ if (n <= 0) {
+ pa_memblock_unref(tchunk.memblock);
+
+ sf_close(u->sndfile);
+ u->sndfile = NULL;
break;
}
-
- u->drop -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
+
+ tchunk.length = n * fs;
+
+ pa_memblockq_push(u->memblockq, &tchunk);
+ pa_memblock_unref(tchunk.memblock);
}
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
-
- pa_assert(chunk->length > 0);
- pa_assert(u->drop <= 0);
-
- return 0;
+ if (pa_sink_input_safe_to_remove(i)) {
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
+
+ return -1;
+ }
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ pa_log("backwards %lu", (unsigned long) nbytes);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
}
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
file_stream *u;
- pa_assert(i);
- pa_assert(length > 0);
+ pa_sink_input_assert_ref(i);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
-
- if (u->memchunk.memblock) {
- if (length < u->memchunk.length) {
- u->memchunk.index += length;
- u->memchunk.length -= length;
- return;
- }
+ if (!u->memblockq)
+ return;
- length -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
-
- u->drop += length;
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
int pa_play_file(
@@ -228,7 +241,7 @@ int pa_play_file(
pa_sample_spec ss;
pa_sink_input_new_data data;
int fd;
-
+
pa_assert(sink);
pa_assert(fname);
@@ -237,10 +250,9 @@ int pa_play_file(
u->parent.process_msg = file_stream_process_msg;
u->core = sink->core;
u->sink_input = NULL;
- pa_memchunk_reset(&u->memchunk);
u->sndfile = NULL;
u->readf_function = NULL;
- u->drop = 0;
+ u->memblockq = NULL;
memset(&sfinfo, 0, sizeof(sfinfo));
@@ -264,14 +276,14 @@ int pa_play_file(
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);
@@ -312,23 +324,31 @@ int pa_play_file(
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = fname;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_volume(&data, volume);
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ if (!u->sink_input)
goto fail;
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
+ u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
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:
diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h
index 189e242d..4cc69146 100644
--- a/src/pulsecore/sound-file-stream.h
+++ b/src/pulsecore/sound-file-stream.h
@@ -1,8 +1,6 @@
#ifndef foosoundfilestreamhfoo
#define foosoundfilestreamhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index 50352930..3183ede6 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,7 @@ int pa_sound_file_load(
pa_sample_spec *ss,
pa_channel_map *map,
pa_memchunk *chunk) {
-
+
SNDFILE *sf = NULL;
SF_INFO sfinfo;
int ret = -1;
@@ -79,7 +77,7 @@ int pa_sound_file_load(
} 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);
@@ -119,7 +117,7 @@ int pa_sound_file_load(
}
if (map)
- pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
@@ -155,13 +153,13 @@ finish:
}
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 -1;
@@ -198,7 +196,7 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
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);
return 1;
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
index 46763bd8..e4d703d3 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -1,8 +1,6 @@
#ifndef soundfilehfoo
#define soundfilehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 2a902dc2..3d1abe30 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,12 +30,16 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "source-output.h"
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+
static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
static void source_output_free(pa_object* mo);
@@ -47,9 +49,18 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
+
return data;
}
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
pa_assert(data);
@@ -57,13 +68,31 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data,
data->channel_map = *map;
}
-void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+void pa_source_output_new_data_done(pa_source_output_new_data *data) {
pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ pa_proplist_free(data->proplist);
}
+/* Called from main context */
+static void reset_callbacks(pa_source_output *o) {
+ pa_assert(o);
+
+ o->push = NULL;
+ o->process_rewind = NULL;
+ o->update_max_rewind = NULL;
+ o->update_source_requested_latency = NULL;
+ o->update_source_latency_range = NULL;
+ o->attach = NULL;
+ o->detach = NULL;
+ o->suspend = NULL;
+ o->moved = NULL;
+ o->kill = NULL;
+ o->get_latency = NULL;
+ o->state_change = NULL;
+}
+
+/* Called from main context */
pa_source_output* pa_source_output_new(
pa_core *core,
pa_source_output_new_data *data,
@@ -71,7 +100,7 @@ pa_source_output* pa_source_output_new(
pa_source_output *o;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -80,14 +109,15 @@ pa_source_output* pa_source_output_new(
return NULL;
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);
+ data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
pa_return_null_if_fail(data->source);
pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
+ pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
+
if (!data->sample_spec_is_set)
data->sample_spec = data->source->sample_spec;
@@ -97,17 +127,34 @@ pa_source_output* pa_source_output_new(
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);
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+ if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ data->sample_spec.format = data->source->sample_spec.format;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ data->sample_spec.rate = data->source->sample_spec.rate;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->source->sample_spec.channels;
+ data->channel_map = data->source->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
+ return NULL;
+
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;
@@ -122,7 +169,9 @@ pa_source_output* pa_source_output_new(
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
data->resample_method,
- !!(flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
+ ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -137,7 +186,7 @@ pa_source_output* pa_source_output_new(
o->core = core;
o->state = PA_SOURCE_OUTPUT_INIT;
o->flags = flags;
- o->name = pa_xstrdup(data->name);
+ o->proplist = pa_proplist_copy(data->proplist);
o->driver = pa_xstrdup(data->driver);
o->module = data->module;
o->source = data->source;
@@ -147,49 +196,68 @@ pa_source_output* pa_source_output_new(
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->direct_on_input = data->direct_on_input;
+
+ reset_callbacks(o);
o->userdata = NULL;
o->thread_info.state = o->state;
o->thread_info.attached = FALSE;
o->thread_info.sample_spec = o->sample_spec;
o->thread_info.resampler = resampler;
+ o->thread_info.requested_source_latency = (pa_usec_t) -1;
+ o->thread_info.direct_on_input = o->direct_on_input;
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
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 output %u \"%s\" on %s with sample spec %s",
+ if (o->direct_on_input)
+ pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
+
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
o->index,
- o->name,
+ pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
o->source->name,
- pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
/* Don't forget to call pa_source_output_put! */
return o;
}
-static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+/* Called from main context */
+static void update_n_corked(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);
+}
+
+/* Called from main context */
+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;
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+ update_n_corked(o, state);
o->state = state;
if (state != PA_SOURCE_OUTPUT_UNLINKED)
@@ -198,6 +266,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
return 0;
}
+/* Called from main context */
void pa_source_output_unlink(pa_source_output*o) {
pa_bool_t linked;
pa_assert(o);
@@ -207,28 +276,25 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_ref(o);
- linked = PA_SOURCE_OUTPUT_LINKED(o->state);
+ linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+ if (o->direct_on_input)
+ pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
pa_source_output_unref(o);
- 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;
+ update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
+ 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)
+ if (o->source->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+ reset_callbacks(o);
if (linked) {
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
@@ -239,106 +305,252 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_unref(o);
}
+/* Called from main context */
static void source_output_free(pa_object* mo) {
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+ pa_assert(o);
pa_assert(pa_source_output_refcnt(o) == 0);
- if (PA_SOURCE_OUTPUT_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_source_output_unlink(o);
- pa_log_info("Freeing output %u \"%s\"", o->index, o->name);
+ pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!o->thread_info.attached);
+ if (o->thread_info.delay_memblockq)
+ pa_memblockq_free(o->thread_info.delay_memblockq);
+
if (o->thread_info.resampler)
pa_resampler_free(o->thread_info.resampler);
- pa_xfree(o->name);
+ if (o->proplist)
+ pa_proplist_free(o->proplist);
+
pa_xfree(o->driver);
pa_xfree(o);
}
+/* Called from main context */
void pa_source_output_put(pa_source_output *o) {
+ pa_source_output_state_t state;
pa_source_output_assert_ref(o);
pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
+
+ /* The following fields must be initialized properly */
pa_assert(o->push);
+ pa_assert(o->kill);
- o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+ state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
- if (o->state == PA_SOURCE_OUTPUT_CORKED)
- o->source->n_corked++;
+ update_n_corked(o, state);
+ o->state = state;
- 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_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
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);
}
+/* Called from main context */
void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- if (o->kill)
- o->kill(o);
+ o->kill(o);
}
-pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
- pa_usec_t r = 0;
+/* Called from main context */
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
+ pa_usec_t r[2] = { 0, 0 };
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_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;
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
if (o->get_latency)
- r += o->get_latency(o);
+ r[0] += o->get_latency(o);
- return r;
+ if (source_latency)
+ *source_latency = r[1];
+
+ return r[0];
}
/* Called from thread context */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
- pa_memchunk rchunk;
+ size_t length;
+ size_t limit, mbs = 0;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(chunk);
- pa_assert(chunk->length);
+ pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
- if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED)
+ if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
return;
- pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING);
+ pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
- if (!o->thread_info.resampler) {
- o->push(o, chunk);
- return;
+ if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
+ pa_log_debug("Delay queue overflow!");
+ pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE);
+ }
+
+ limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+
+ /* Implement the delay queue */
+ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
+ pa_memchunk qchunk;
+
+ length -= limit;
+
+ pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
+
+ if (qchunk.length > length)
+ qchunk.length = length;
+
+ pa_assert(qchunk.length > 0);
+
+ if (!o->thread_info.resampler)
+ o->push(o, &qchunk);
+ else {
+ pa_memchunk rchunk;
+
+ if (mbs == 0)
+ mbs = pa_resampler_max_block_size(o->thread_info.resampler);
+
+ if (qchunk.length > mbs)
+ qchunk.length = mbs;
+
+ pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
+
+ if (rchunk.length > 0)
+ o->push(o, &rchunk);
+
+ if (rchunk.memblock)
+ pa_memblock_unref(rchunk.memblock);
+ }
+
+ pa_memblock_unref(qchunk.memblock);
+ pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
}
+}
+
+/* Called from thread context */
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
+ pa_source_output_assert_ref(o);
- pa_resampler_run(o->thread_info.resampler, chunk, &rchunk);
- if (!rchunk.length)
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+ if (nbytes <= 0)
return;
- pa_assert(rchunk.memblock);
- o->push(o, &rchunk);
- pa_memblock_unref(rchunk.memblock);
+ if (o->process_rewind) {
+ pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+
+ if (o->thread_info.resampler)
+ nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+
+ if (nbytes > 0)
+ o->process_rewind(o, nbytes);
+
+ if (o->thread_info.resampler)
+ pa_resampler_reset(o->thread_info.resampler);
+
+ } else
+ pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
}
+/* Called from thread context */
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+ if (o->update_max_rewind)
+ o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
+ pa_source_assert_ref(s);
+
+ if (usec == (pa_usec_t) -1)
+ return usec;
+
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
+
+ return usec;
+}
+
+/* Called from thread context */
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
+ pa_source_output_assert_ref(o);
+
+ usec = fixup_latency(o->source, usec);
+ o->thread_info.requested_source_latency = usec;
+ pa_source_invalidate_requested_latency(o->source);
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+
+ usec = fixup_latency(o->source, usec);
+ o->thread_info.requested_source_latency = usec;
+ o->source->thread_info.requested_latency_valid = FALSE;
+ }
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
+ pa_usec_t usec = 0;
+
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = o->thread_info.requested_source_latency;
+
+ return usec;
+}
+
+/* Called from main context */
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));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
}
+/* Called from main context */
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_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_return_val_if_fail(o->thread_info.resampler, -1);
if (o->sample_spec.rate == rate)
@@ -352,36 +564,45 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
return 0;
}
+/* Called from main context */
void pa_source_output_set_name(pa_source_output *o, const char *name) {
+ const char *old;
pa_source_output_assert_ref(o);
- if (!o->name && !name)
+ if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
return;
- if (o->name && name && !strcmp(o->name, name))
+ old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
+
+ if (old && name && !strcmp(old, name))
return;
- pa_xfree(o->name);
- o->name = pa_xstrdup(name);
+ if (name)
+ pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o);
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
}
+/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
pa_source_output_assert_ref(o);
return o->resample_method;
}
+/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
- pa_resampler *new_resampler = NULL;
+ pa_resampler *new_resampler;
+ pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_source_assert_ref(dest);
origin = o->source;
@@ -392,6 +613,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
return -1;
+ if (o->direct_on_input)
+ return -1;
+
if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log_warn("Failed to move source output: too many outputs per source.");
return -1;
@@ -415,16 +639,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
o->resample_method,
- !!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
+ ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
- }
+ } else
+ new_resampler = NULL;
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o);
+ hook_data.source_output = o;
+ hook_data.destination = dest;
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
/* Okey, let's move it */
- pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
pa_idxset_remove_by_data(origin->outputs, o, NULL);
pa_idxset_put(dest->outputs, o, NULL);
@@ -440,13 +669,28 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
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_memblockq_free(o->thread_info.delay_memblockq);
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
+ }
pa_source_update_status(origin);
pa_source_update_status(dest);
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+ if (o->moved)
+ o->moved(o);
+
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);
@@ -457,26 +701,61 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
return 0;
}
-/* Called from thread context */
+/* Called from IO thread context */
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+ pa_source_output_assert_ref(o);
+
+ if (state == o->thread_info.state)
+ return;
+
+ if (o->state_change)
+ o->state_change(o, state);
+
+ o->thread_info.state = state;
+}
+
+/* Called from IO thread context, except when it is not */
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: {
+ case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+ pa_usec_t source_usec = 0;
+
+ r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
+
+ if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0)
+ r[1] += source_usec;
+
+ return 0;
+ }
+
+ 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:
+
+ pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+ return 0;
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
+
+ *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
return 0;
}
- case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
- o->thread_info.state = PA_PTR_TO_UINT(userdata);
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
+ *r = o->thread_info.requested_source_latency;
return 0;
}
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 60552d43..61825b22 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourceoutputhfoo
#define foopulsesourceoutputhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,14 +40,19 @@ typedef enum pa_source_output_state {
PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t;
-static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_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_VARIABLE_RATE = 1,
PA_SOURCE_OUTPUT_DONT_MOVE = 2,
- PA_SOURCE_OUTPUT_START_CORKED = 4
+ PA_SOURCE_OUTPUT_START_CORKED = 4,
+ PA_SOURCE_OUTPUT_NO_REMAP = 8,
+ PA_SOURCE_OUTPUT_NO_REMIX = 16,
+ PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+ PA_SOURCE_OUTPUT_FIX_RATE = 64,
+ PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
} pa_source_output_flags_t;
struct pa_source_output {
@@ -57,54 +60,93 @@ struct pa_source_output {
uint32_t index;
pa_core *core;
+
pa_source_output_state_t state;
pa_source_output_flags_t flags;
- char *name, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
pa_source *source;
+ /* A source output can monitor just a single input of a sink, in which case we find it here */
+ pa_sink_input *direct_on_input; /* may be NULL */
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+ pa_resample_method_t resample_method;
+
/* Pushes a new memchunk into the output. Called from IO thread
* context. */
- void (*push)(pa_source_output *o, const pa_memchunk *chunk);
+ void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Only relevant for monitor sources right now: called when the
+ * recorded stream is rewound. Called from IO context */
+ void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the maximum rewindable size of the source
+ * changes. Called from IO thread context. */
+ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the source
+ * changes. Called from IO context. */
+ void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */
+
+ /* Called whenver the latency range of the source changes. Called
+ * from IO context. */
+ void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
/* 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 */
+ 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 */
-
+ 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 */
+ void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the the source this output is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_source_output *o); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
- void (*kill)(pa_source_output* o); /* may be NULL */
+ void (*kill)(pa_source_output* o); /* may NOT 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. */
+ this stream. Called from main context. This is added to what the
+ PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
- pa_resample_method_t resample_method;
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
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 */
+
+ /* We maintain a delay memblockq here for source outputs that
+ * don't implement rewind() */
+ pa_memblockq *delay_memblockq;
+
+ /* The requested latency for the source */
+ pa_usec_t requested_source_latency;
+
+ pa_sink_input *direct_on_input; /* may be NULL */
} thread_info;
void *userdata;
@@ -117,11 +159,16 @@ enum {
PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
PA_SOURCE_OUTPUT_MESSAGE_MAX
};
typedef struct pa_source_output_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+ pa_sink_input *direct_on_input;
+
+ const char *driver;
pa_module *module;
pa_client *client;
@@ -138,7 +185,12 @@ typedef struct pa_source_output_new_data {
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
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);
+void pa_source_output_new_data_done(pa_source_output_new_data *data);
+
+typedef struct pa_source_output_move_hook_data {
+ pa_source_output *source_output;
+ pa_source *destination;
+} pa_source_output_move_hook_data;
/* To be called by the implementing module only */
@@ -152,16 +204,18 @@ void pa_source_output_unlink(pa_source_output*o);
void pa_source_output_set_name(pa_source_output *i, const char *name);
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
+
+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);
+
/* Callable by everyone */
/* External code may request disconnection with this funcion */
void pa_source_output_kill(pa_source_output*o);
-pa_usec_t pa_source_output_get_latency(pa_source_output *i);
-
-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_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
@@ -169,9 +223,18 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
#define pa_source_output_get_state(o) ((o)->state)
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
+
/* To be used exclusively by the source driver thread */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
+
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 9a6902ae..8256a988 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
@@ -41,40 +40,127 @@
#include "source.h"
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
static void source_free(pa_object *o);
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_source_new_data_done(pa_source_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
pa_source* pa_source_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
+ pa_source_new_data *data,
+ pa_source_flags_t flags) {
pa_source *s;
- char st[256];
- pa_channel_map tmap;
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
- pa_assert(name);
- pa_assert(spec);
+ pa_assert(data);
+ pa_assert(data->name);
+
+ s = pa_msgobject_new(pa_source);
- pa_return_null_if_fail(pa_sample_spec_valid(spec));
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_source_new_data_set_name(data, 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);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
- s = pa_msgobject_new(pa_source);
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
+ 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);
+
+ 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 = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
@@ -83,57 +169,81 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
- s->flags = 0;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->module = NULL;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
- s->sample_spec = *spec;
- s->channel_map = *map;
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
s->outputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
s->monitor_of = NULL;
- pa_cvolume_reset(&s->volume, spec->channels);
- s->muted = FALSE;
+ s->volume = data->volume;
+ s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
- s->get_latency = NULL;
- s->set_volume = NULL;
- s->get_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
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 source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
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;
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
s->thread_info.state = s->state;
+ s->thread_info.max_rewind = 0;
+ s->thread_info.requested_latency_valid = FALSE;
+ s->thread_info.requested_latency = 0;
+ s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+ s->thread_info.max_latency = 0;
+
+ pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+
+ pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
return s;
}
+/* Called from main context */
static int source_set_state(pa_source *s, pa_source_state_t state) {
int ret;
+ pa_bool_t suspend_change;
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)) {
+ suspend_change =
+ (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+ (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
pa_source_output *o;
uint32_t idx;
@@ -144,33 +254,38 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
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;
}
+/* Called from main context */
void pa_source_put(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(s->state == PA_SINK_INIT);
- pa_assert(s->rtpoll);
+
+ /* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
+ pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
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);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
}
+/* Called from main context */
void pa_source_unlink(pa_source *s) {
pa_bool_t linked;
pa_source_output *o, *j = NULL;
@@ -180,7 +295,7 @@ void pa_source_unlink(pa_source *s) {
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
- linked = PA_SOURCE_LINKED(s->state);
+ linked = PA_SOURCE_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
@@ -200,12 +315,7 @@ void pa_source_unlink(pa_source *s) {
else
s->state = PA_SOURCE_UNLINKED;
- s->get_latency = NULL;
- s->get_volume = NULL;
- s->set_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
if (linked) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
@@ -213,6 +323,7 @@ void pa_source_unlink(pa_source *s) {
}
}
+/* Called from main context */
static void source_free(pa_object *o) {
pa_source_output *so;
pa_source *s = PA_SOURCE(o);
@@ -220,7 +331,7 @@ static void source_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_source_refcnt(s) == 0);
- if (PA_SOURCE_LINKED(s->state))
+ if (PA_SOURCE_IS_LINKED(s->state))
pa_source_unlink(s);
pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
@@ -232,15 +343,36 @@ static void source_free(pa_object *o) {
pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
+
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
+/* Called from main context */
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
+ pa_source_assert_ref(s);
+
+ s->asyncmsgq = q;
+}
+
+/* Called from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+ pa_source_assert_ref(s);
+
+ s->rtpoll = p;
+}
+
+/* Called from main context */
int pa_source_update_status(pa_source*s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
return 0;
@@ -248,9 +380,10 @@ int pa_source_update_status(pa_source*s) {
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
+/* Called from main context */
int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED);
@@ -258,19 +391,32 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-void pa_source_ping(pa_source *s) {
+/* Called from IO thread context */
+void pa_source_process_rewind(pa_source *s, size_t nbytes) {
+ pa_source_output *o;
+ void *state = NULL;
+
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ return;
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);
+ pa_log_debug("Processing rewind...");
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_source_output_process_rewind(o, nbytes);
+ }
}
+/* Called from IO thread context */
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
pa_assert(chunk);
if (s->thread_info.state != PA_SOURCE_RUNNING)
@@ -287,40 +433,75 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
else
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);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, &vchunk);
+ }
pa_memblock_unref(vchunk.memblock);
} else {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- pa_source_output_push(o, chunk);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, chunk);
+ }
}
}
+/* Called from IO thread context */
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_source_output_assert_ref(o);
+ pa_assert(o->thread_info.direct_on_input);
+ pa_assert(chunk);
+
+ 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->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->thread_info.soft_volume);
+
+ pa_source_output_push(o, &vchunk);
+
+ pa_memblock_unref(vchunk.memblock);
+ } else
+ pa_source_output_push(o, chunk);
+}
+
+/* Called from main thread */
pa_usec_t pa_source_get_latency(pa_source *s) {
pa_usec_t usec;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_OPENED(s->state))
+ if (!PA_SOURCE_IS_OPENED(s->state))
return 0;
- if (s->get_latency)
- return s->get_latency(s);
-
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- return 0;
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
return usec;
}
+/* Called from main thread */
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
- int changed;
+ pa_bool_t changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -330,37 +511,39 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
s->set_volume = NULL;
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);
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
const pa_cvolume *pa_source_get_volume(pa_source *s) {
- pa_cvolume old_volume;
-
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_volume = s->volume;
+ if (s->refresh_volume) {
+ pa_cvolume old_volume = s->volume;
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ 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 (!s->get_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);
+ 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;
}
+/* Called from main thread */
void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
- int changed;
+ pa_bool_t changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -375,81 +558,66 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
pa_bool_t pa_source_get_mute(pa_source *s) {
- pa_bool_t old_muted;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_muted = s->muted;
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- 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 (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
- if (old_muted != s->muted)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return s->muted;
}
-void pa_source_set_module(pa_source *s, pa_module *m) {
- pa_source_assert_ref(s);
-
- if (m == s->module)
- return;
-
- s->module = m;
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
-
+/* Called from main thread */
void pa_source_set_description(pa_source *s, const char *description) {
+ const char *old;
pa_source_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
- if (PA_SOURCE_LINKED(s->state)) {
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s);
+ if (PA_SOURCE_IS_LINKED(s->state)) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
}
}
-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;
-}
-
+/* Called from main thread */
unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
return pa_idxset_size(s->outputs);
}
+/* Called from main thread */
unsigned pa_source_used_by(pa_source *s) {
unsigned ret;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked);
@@ -457,37 +625,62 @@ unsigned pa_source_used_by(pa_source *s) {
return ret - s->n_corked;
}
+/* Called from IO thread, except when it is not */
int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_source *s = PA_SOURCE(object);
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));
+ if (o->direct_on_input) {
+ o->thread_info.direct_on_input = o->direct_on_input;
+ pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
+ }
+
pa_assert(!o->thread_info.attached);
o->thread_info.attached = TRUE;
if (o->attach)
o->attach(o);
+ pa_source_output_set_state_within_thread(o, o->state);
+
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+
+ /* We don't just invalidate the requested latency here,
+ * because if we are in a move we might need to fix up the
+ * requested latency. */
+ pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
return 0;
}
case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+ pa_source_output_set_state_within_thread(o, o->state);
+
if (o->detach)
o->detach(o);
pa_assert(o->thread_info.attached);
o->thread_info.attached = FALSE;
+ if (o->thread_info.direct_on_input) {
+ pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
+ o->thread_info.direct_on_input = NULL;
+ }
+
if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
pa_source_output_unref(o);
+ pa_source_invalidate_requested_latency(s);
+
return 0;
}
@@ -507,28 +700,65 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
*((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 */
+ /* Detach all streams */
pa_source_detach_within_thread(s);
- break;
+ return 0;
case PA_SOURCE_MESSAGE_ATTACH:
/* Reattach all streams */
pa_source_attach_within_thread(s);
- break;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_source_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_source_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+ if (s->monitor_of) {
+ *((pa_usec_t*) userdata) = 0;
+ return 0;
+ }
+
+ /* Implementors need to overwrite this implementation! */
+ return -1;
+
case PA_SOURCE_MESSAGE_MAX:
;
}
@@ -536,6 +766,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
return -1;
}
+/* Called from main thread */
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
uint32_t idx;
pa_source *source;
@@ -549,41 +780,206 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
return ret;
}
+/* Called from main thread */
void pa_source_detach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
}
+/* Called from main thread */
void pa_source_attach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
}
+/* Called from IO thread */
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));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->detach)
o->detach(o);
}
+/* Called from IO thread */
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));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->attach)
o->attach(o);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+
+ if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
+ result = o->thread_info.requested_source_latency;
+
+ if (result != (pa_usec_t) -1) {
+ if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+ result = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+
+ return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_requested_latency(pa_source *s) {
+ pa_usec_t usec;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ if (!PA_SOURCE_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+ }
+}
+
+void pa_source_invalidate_requested_latency(pa_source *s) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ o->update_source_requested_latency(o);
+
+ if (s->monitor_of)
+ pa_sink_invalidate_requested_latency(s->monitor_of);
+}
+
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_source_assert_ref(s);
+
+ /* min_latency == 0: no limit
+ * min_latency == (size_t) -1: default limit
+ * min_latency anything else: specified limit
+ *
+ * Similar for max_latency */
+
+ if (min_latency == (pa_usec_t) -1)
+ min_latency = DEFAULT_MIN_LATENCY;
+
+ if (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ r[1] = max_latency;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+ } else {
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ s->thread_info.requested_latency_valid = FALSE;
+ }
+}
+
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+ pa_source_assert_ref(s);
+ pa_assert(min_latency);
+ pa_assert(max_latency);
+
+ if (PA_SOURCE_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* Called from IO thread */
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_latency_range)
+ o->update_source_latency_range(o);
+
+ pa_source_invalidate_requested_latency(s);
+}
+
+size_t pa_source_get_max_rewind(pa_source *s) {
+ size_t r;
+ pa_source_assert_ref(s);
+
+ if (!PA_SOURCE_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+ return r;
}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index bd0a9122..f4a17e8d 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourcehfoo
#define foopulsesourcehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,6 @@ typedef struct pa_source pa_source;
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/memblock.h>
@@ -43,6 +40,7 @@ typedef struct pa_source pa_source;
#include <pulsecore/asyncmsgq.h>
#include <pulsecore/msgobject.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/source-output.h>
#define PA_MAX_OUTPUTS_PER_SOURCE 32
@@ -54,11 +52,11 @@ typedef enum pa_source_state {
PA_SOURCE_UNLINKED
} pa_source_state_t;
-static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_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) {
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
}
@@ -71,7 +69,8 @@ struct pa_source {
pa_source_flags_t flags;
char *name;
- char *description, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
pa_module *module; /* may be NULL */
@@ -84,18 +83,45 @@ struct pa_source {
pa_cvolume volume;
pa_bool_t muted;
- pa_bool_t refresh_volume;
- pa_bool_t refresh_muted;
+ pa_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
+
+ pa_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
- int (*set_volume)(pa_source *s); /* dito */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
int (*get_volume)(pa_source *s); /* dito */
- int (*set_mute)(pa_source *s); /* dito */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_source *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
int (*get_mute)(pa_source *s); /* dito */
- pa_usec_t (*get_latency)(pa_source *s); /* dito */
- pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_source *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_source *s); /* dito */
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
@@ -103,7 +129,17 @@ struct pa_source {
pa_source_state_t state;
pa_hashmap *outputs;
pa_cvolume soft_volume;
- pa_bool_t soft_muted;
+ pa_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* Then number of bytes this source will be rewound for at
+ * max. (Only used on monitor sources) */
+ size_t max_rewind;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
} thread_info;
void *userdata;
@@ -120,44 +156,75 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_GET_MUTE,
PA_SOURCE_MESSAGE_SET_MUTE,
PA_SOURCE_MESSAGE_GET_LATENCY,
+ PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
PA_SOURCE_MESSAGE_SET_STATE,
- PA_SOURCE_MESSAGE_PING,
PA_SOURCE_MESSAGE_ATTACH,
PA_SOURCE_MESSAGE_DETACH,
+ PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_MAX_REWIND,
PA_SOURCE_MESSAGE_MAX
} pa_source_message_t;
+typedef struct pa_source_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_source_new_data;
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
+void pa_source_new_data_done(pa_source_new_data *data);
+
/* 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);
+ pa_source_new_data *data,
+ pa_source_flags_t flags);
void pa_source_put(pa_source *s);
void pa_source_unlink(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_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
/* May be called by everyone, from main context */
+/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_source_get_latency(pa_source *s);
+pa_usec_t pa_source_get_requested_latency(pa_source *s);
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+size_t pa_source_get_max_rewind(pa_source *s);
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_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);
@@ -169,11 +236,22 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar
/* To be called exclusively by the source driver, from IO context */
-void pa_source_post(pa_source*s, const pa_memchunk *b);
+void pa_source_post(pa_source*s, const pa_memchunk *chunk);
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_process_rewind(pa_source *s, size_t nbytes);
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);
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
+
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by source output drivers, from IO context */
+
+void pa_source_invalidate_requested_latency(pa_source *s);
+
#endif
diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h
index e2d731ac..9987c8fb 100644
--- a/src/pulsecore/speex/arch.h
+++ b/src/pulsecore/speex/arch.h
@@ -7,18 +7,18 @@
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
@@ -35,6 +35,45 @@
#ifndef ARCH_H
#define ARCH_H
+#ifndef SPEEX_VERSION
+#define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */
+#define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */
+#define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */
+#define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */
+#define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */
+#endif
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef _USE_SSE
+#error SSE is only for floating-point
+#endif
+#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_POINT_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
#ifndef OUTSIDE_SPEEX
#include "speex/speex_types.h"
#endif
@@ -68,6 +107,7 @@ typedef spx_word32_t spx_sig_t;
#define LPC_SHIFT 13
#define LSP_SHIFT 13
#define SIG_SHIFT 14
+#define GAIN_SHIFT 6
#define VERY_SMALL 0
#define VERY_LARGE32 ((spx_word32_t)2147483647)
@@ -111,9 +151,6 @@ typedef float spx_word32_t;
#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
@@ -182,11 +219,11 @@ typedef float spx_word32_t;
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
-#define BYTES_PER_CHAR 2
+#define BYTES_PER_CHAR 2
#define BITS_PER_CHAR 16
#define LOG2_BITS_PER_CHAR 4
-#else
+#else
#define BYTES_PER_CHAR 1
#define BITS_PER_CHAR 8
@@ -194,4 +231,11 @@ typedef float spx_word32_t;
#endif
+
+
+#ifdef FIXED_DEBUG
+long long spx_mips=0;
+#endif
+
+
#endif
diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h
index 2948177c..547e22c7 100644
--- a/src/pulsecore/speex/fixed_generic.h
+++ b/src/pulsecore/speex/fixed_generic.h
@@ -7,18 +7,18 @@
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
diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c
index 1cc4d490..1e592002 100644
--- a/src/pulsecore/speex/resample.c
+++ b/src/pulsecore/speex/resample.c
@@ -1,5 +1,5 @@
/* Copyright (C) 2007 Jean-Marc Valin
-
+
File: resample.c
Arbitrary resampling code
@@ -37,17 +37,23 @@
- 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
+ Warning: This resampler is relatively new. Although I think I got rid of
+ all the major bugs and I don't expect the API to change anymore, there
+ may be something I've missed. So use with caution.
+
+ This algorithm is based on this original resampling algorithm:
+ Smith, Julius O. Digital Audio Resampling Home Page
+ Center for Computer Research in Music and Acoustics (CCRMA),
+ Stanford University, 2007.
+ Web published at http://www-ccrma.stanford.edu/~jos/resample/.
+
+ There is one main difference, though. This resampler uses cubic
+ interpolation instead of linear interpolation in the above paper. This
+ makes the table much smaller and makes it possible to compute that table
+ on a per-stream basis. In turn, being able to tweak the table for each
+ stream makes it possible to both reduce complexity on simple ratios
+ (e.g. 2/3), and get rid of the rounding operations in the inner loop.
+ The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
*/
#ifdef HAVE_CONFIG_H
@@ -62,9 +68,10 @@ 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"
+#include "arch.h"
+#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include <math.h>
@@ -74,11 +81,11 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifdef FIXED_POINT
-#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
+#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))))
+#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
@@ -97,7 +104,7 @@ struct SpeexResamplerState_ {
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;
@@ -108,17 +115,17 @@ struct SpeexResamplerState_ {
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;
} ;
@@ -160,7 +167,7 @@ static double kaiser8_table[36] = {
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,
@@ -173,7 +180,7 @@ struct FuncDef {
double *table;
int oversample;
};
-
+
static struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
@@ -195,7 +202,7 @@ struct QualityMapping {
/* This table maps conversion quality to internal parameters. There are two
- reasons that explain why the up-sampling bandwidth is larger than the
+ 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)
@@ -221,7 +228,7 @@ static double compute_func(float x, struct FuncDef *func)
{
float y, frac;
double interp[4];
- int ind;
+ int ind;
y = x*func->oversample;
ind = (int)floor(y);
frac = (y-ind);
@@ -232,7 +239,7 @@ static double compute_func(float x, struct FuncDef *func)
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];
}
@@ -320,7 +327,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
{
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 */
@@ -328,7 +335,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
{
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++)
@@ -336,7 +343,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
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++;
@@ -368,7 +375,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
{
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 */
@@ -376,7 +383,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
{
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++)
@@ -384,7 +391,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
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++;
@@ -414,7 +421,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
{
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];
@@ -428,7 +435,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
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
+ 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++)
{
@@ -451,7 +458,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
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++;
@@ -483,7 +490,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
{
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];
@@ -492,7 +499,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
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
+ 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++)
{
@@ -515,7 +522,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
}
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++;
@@ -536,11 +543,11 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
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 */
@@ -616,7 +623,7 @@ static void update_filter(SpeexResamplerState *st)
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. */
@@ -654,7 +661,7 @@ static void update_filter(SpeexResamplerState *st)
/*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--)
@@ -729,12 +736,12 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uin
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));
@@ -749,9 +756,9 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uin
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;
@@ -780,14 +787,14 @@ static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t
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;
@@ -809,20 +816,20 @@ static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t
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;
}
@@ -879,7 +886,7 @@ int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_
olen -= ochunk;
}
*in_len -= ilen;
- *out_len -= olen;
+ *out_len -= olen;
#endif
return RESAMPLER_ERR_SUCCESS;
}
@@ -942,7 +949,7 @@ int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_in
olen -= ochunk;
}
*in_len -= ilen;
- *out_len -= olen;
+ *out_len -= olen;
#endif
return RESAMPLER_ERR_SUCCESS;
}
@@ -966,7 +973,7 @@ int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const flo
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;
@@ -1003,7 +1010,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
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;
@@ -1018,7 +1025,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
st->den_rate /= fact;
}
}
-
+
if (old_den > 0)
{
for (i=0;i<st->nb_channels;i++)
@@ -1029,7 +1036,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
st->samp_frac_num[i] = st->den_rate-1;
}
}
-
+
if (st->initialised)
update_filter(st);
return RESAMPLER_ERR_SUCCESS;
diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h
index c44fbcd0..8629eeb3 100644
--- a/src/pulsecore/speex/speex_resampler.h
+++ b/src/pulsecore/speex/speex_resampler.h
@@ -1,8 +1,8 @@
/* 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
@@ -43,7 +43,7 @@
/********* WARNING: MENTAL SANITY ENDS HERE *************/
-/* If the resampler is defined outside of Speex, we change the symbol names so that
+/* 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 */
@@ -53,7 +53,7 @@
#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)
@@ -79,7 +79,7 @@
#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"
@@ -102,7 +102,7 @@ enum {
RESAMPLER_ERR_BAD_STATE = 2,
RESAMPLER_ERR_INVALID_ARG = 3,
RESAMPLER_ERR_PTR_OVERLAP = 4,
-
+
RESAMPLER_ERR_MAX_ERROR
};
@@ -118,14 +118,14 @@ typedef struct SpeexResamplerState_ SpeexResamplerState;
* @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,
+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
+/** 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
@@ -137,11 +137,11 @@ SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
* @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,
+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);
@@ -152,24 +152,24 @@ 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
+ * @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
+ * @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,
+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
+ * @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
@@ -177,11 +177,11 @@ int speex_resampler_process_float(SpeexResamplerState *st,
* @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,
+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.
@@ -193,10 +193,10 @@ int speex_resampler_process_int(SpeexResamplerState *st,
* @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,
+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.
@@ -208,10 +208,10 @@ int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
* @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,
+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).
@@ -219,8 +219,8 @@ int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
* @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,
+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).
@@ -228,11 +228,11 @@ int speex_resampler_set_rate(SpeexResamplerState *st,
* @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,
+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
+/** 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
@@ -240,10 +240,10 @@ void speex_resampler_get_rate(SpeexResamplerState *st,
* @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,
+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
@@ -252,56 +252,56 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st,
* @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,
+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
+ * @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 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
+ * @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,
+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,
+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,
+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,
+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,
+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
+/** 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
diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h
index c0d5c0c0..617e4afb 100644
--- a/src/pulsecore/speexwrap.h
+++ b/src/pulsecore/speexwrap.h
@@ -1,8 +1,6 @@
#ifndef foopulsespeexwraphfoo
#define foopulsespeexwraphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,10 +37,12 @@ SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_
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);
+int paspfx_resampler_reset_mem(SpeexResamplerState *st);
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);
+int paspfl_resampler_reset_mem(SpeexResamplerState *st);
#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
new file mode 100644
index 00000000..1661383d
--- /dev/null
+++ b/src/pulsecore/start-child.c
@@ -0,0 +1,160 @@
+/***
+ 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 <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+
+#include "start-child.h"
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
+ pid_t child;
+ int pipe_fds[2] = { -1, -1 };
+
+ if (pipe(pipe_fds) < 0) {
+ pa_log("pipe() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ if ((child = fork()) == (pid_t) -1) {
+ pa_log("fork() failed: %s", pa_cstrerror(errno));
+ goto fail;
+
+ } else if (child != 0) {
+
+ /* Parent */
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ if (pid)
+ *pid = child;
+
+ return pipe_fds[0];
+ } else {
+#ifdef __linux__
+ DIR* d;
+#endif
+ int max_fd, i;
+
+ /* child */
+
+ pa_reset_priority();
+
+ pa_assert_se(pa_close(pipe_fds[0]) == 0);
+ pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+
+ if (pipe_fds[1] != 1)
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ pa_close(0);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+
+ pa_close(2);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+
+#ifdef __linux__
+
+ if ((d = opendir("/proc/self/fd/"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ char *e = NULL;
+ int fd;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ fd = strtol(de->d_name, &e, 10);
+ pa_assert(errno == 0 && e && *e == 0);
+
+ if (fd >= 3 && dirfd(d) != fd)
+ pa_close(fd);
+ }
+
+ closedir(d);
+ } else {
+
+#endif
+
+ max_fd = 1024;
+
+#ifdef HAVE_SYS_RESOURCE_H
+ {
+ struct rlimit r;
+ if (getrlimit(RLIMIT_NOFILE, &r) == 0)
+ max_fd = r.rlim_max;
+ }
+#endif
+
+ for (i = 3; i < max_fd; i++)
+ pa_close(i);
+
+#ifdef __linux__
+ }
+#endif
+
+#ifdef PR_SET_PDEATHSIG
+ /* On Linux we can use PR_SET_PDEATHSIG to have the helper
+ process killed when the daemon dies abnormally. On non-Linux
+ machines the client will die as soon as it writes data to
+ stdout again (SIGPIPE) */
+
+ prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+#ifdef SIGPIPE
+ /* Make sure that SIGPIPE kills the child process */
+ signal(SIGPIPE, SIG_DFL);
+#endif
+
+#ifdef SIGTERM
+ /* Make sure that SIGTERM kills the child process */
+ signal(SIGTERM, SIG_DFL);
+#endif
+
+ execl(name, name, argv1, NULL);
+ _exit(1);
+ }
+
+fail:
+ pa_close_pipe(pipe_fds);
+
+ return -1;
+}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
new file mode 100644
index 00000000..0b5ff660
--- /dev/null
+++ b/src/pulsecore/start-child.h
@@ -0,0 +1,30 @@
+#ifndef foopulsestartchildhfoo
+#define foopulsestartchildhfoo
+
+/***
+ 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 <sys/types.h>
+#include <unistd.h>
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid);
+
+#endif
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index ea8389f5..b59b6f49 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,13 +53,13 @@ pa_strbuf *pa_strbuf_new(void) {
sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
-
+
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
pa_assert(sb);
-
+
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
@@ -76,7 +74,7 @@ void pa_strbuf_free(pa_strbuf *sb) {
char *pa_strbuf_tostring(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
-
+
pa_assert(sb);
e = t = pa_xnew(char, sb->length+1);
@@ -98,20 +96,20 @@ 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;
-
+
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) {
-
+
pa_assert(sb);
pa_assert(t);
-
+
pa_strbuf_putsn(sb, t, strlen(t));
}
@@ -136,7 +134,7 @@ 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;
-
+
pa_assert(sb);
pa_assert(t);
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index 1c0850b1..24c876d5 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -1,8 +1,6 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,7 +22,7 @@
USA.
***/
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
typedef struct pa_strbuf pa_strbuf;
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index 1fcb0451..f587a2f8 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@ struct pa_strlist {
pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
pa_strlist *n;
size_t size;
-
+
pa_assert(s);
size = strlen(s);
n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
@@ -108,7 +106,7 @@ void pa_strlist_free(pa_strlist *l) {
pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
pa_strlist *r;
-
+
pa_assert(s);
if (!l) {
@@ -146,3 +144,18 @@ pa_strlist* pa_strlist_parse(const char *s) {
return head;
}
+
+pa_strlist *pa_strlist_reverse(pa_strlist *l) {
+ pa_strlist *r = NULL;
+
+ while (l) {
+ pa_strlist *n;
+
+ n = l->next;
+ l->next = r;
+ r = l;
+ l = n;
+ }
+
+ return r;
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 96ad47e2..1cb7537a 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -1,8 +1,6 @@
#ifndef foostrlisthfoo
#define foostrlisthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,4 +44,7 @@ pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
/* Parse a whitespace separated server list */
pa_strlist* pa_strlist_parse(const char *s);
+/* Reverse string list */
+pa_strlist *pa_strlist_reverse(pa_strlist *l);
+
#endif
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 8cf542b6..b0ed59ef 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,12 +40,14 @@
#include "tagstruct.h"
+#define MAX_TAG_SIZE (64*1024)
+
struct pa_tagstruct {
uint8_t *data;
size_t length, allocated;
size_t rindex;
- int dynamic;
+ pa_bool_t dynamic;
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
@@ -60,7 +60,7 @@ pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
t->allocated = t->length = data ? length : 0;
t->rindex = 0;
t->dynamic = !data;
-
+
return t;
}
@@ -74,11 +74,11 @@ void pa_tagstruct_free(pa_tagstruct*t) {
uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
uint8_t *p;
-
+
pa_assert(t);
pa_assert(t->dynamic);
pa_assert(l);
-
+
p = t->data;
*l = t->length;
pa_xfree(t);
@@ -98,7 +98,7 @@ static void extend(pa_tagstruct*t, size_t l) {
void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
size_t l;
pa_assert(t);
-
+
if (s) {
l = strlen(s)+2;
extend(t, l);
@@ -114,7 +114,7 @@ void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
pa_assert(t);
-
+
extend(t, 5);
t->data[t->length] = PA_TAG_U32;
i = htonl(i);
@@ -124,7 +124,7 @@ void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
pa_assert(t);
-
+
extend(t, 2);
t->data[t->length] = PA_TAG_U8;
*(t->data+t->length+1) = c;
@@ -133,10 +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;
-
+
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;
@@ -148,7 +148,7 @@ 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;
-
+
pa_assert(t);
pa_assert(p);
@@ -161,7 +161,7 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
t->length += 5+length;
}
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
pa_assert(t);
extend(t, 1);
@@ -184,7 +184,7 @@ 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;
-
+
pa_assert(t);
extend(t, 9);
@@ -198,7 +198,7 @@ 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;
-
+
pa_assert(t);
extend(t, 9);
@@ -212,7 +212,7 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
uint32_t tmp;
-
+
pa_assert(t);
extend(t, 9);
@@ -254,11 +254,37 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
}
}
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
+ void *state = NULL;
+ pa_assert(t);
+ pa_assert(p);
+
+ extend(t, 1);
+
+ t->data[t->length++] = PA_TAG_PROPLIST;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ size_t l;
+
+ if (!(k = pa_proplist_iterate(p, &state)))
+ break;
+
+ pa_tagstruct_puts(t, k);
+ pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
+ pa_tagstruct_putu32(t, (uint32_t) l);
+ pa_tagstruct_put_arbitrary(t, d, l);
+ }
+
+ pa_tagstruct_puts(t, NULL);
+}
+
int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
char *c;
-
+
pa_assert(t);
pa_assert(s);
@@ -345,7 +371,7 @@ 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;
-
+
pa_assert(t);
pa_assert(p);
@@ -366,7 +392,7 @@ int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
int pa_tagstruct_eof(pa_tagstruct*t) {
pa_assert(t);
-
+
return t->rindex >= t->length;
}
@@ -374,22 +400,22 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *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) {
+int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
pa_assert(t);
pa_assert(b);
-
+
if (t->rindex+1 > t->length)
return -1;
if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
- *b = 1;
+ *b = TRUE;
else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
- *b = 0;
+ *b = FALSE;
else
return -1;
@@ -401,7 +427,7 @@ 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;
@@ -458,7 +484,7 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
uint32_t tmp;
-
+
pa_assert(t);
pa_assert(u);
@@ -529,6 +555,52 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
return 0;
}
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
+ size_t saved_rindex;
+
+ pa_assert(t);
+ pa_assert(p);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_PROPLIST)
+ return -1;
+
+ saved_rindex = t->rindex;
+ t->rindex++;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ uint32_t length;
+
+ if (pa_tagstruct_gets(t, &k) < 0)
+ goto fail;
+
+ if (!k)
+ break;
+
+ if (pa_tagstruct_getu32(t, &length) < 0)
+ goto fail;
+
+ if (length > MAX_TAG_SIZE)
+ goto fail;
+
+ if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
+ goto fail;
+
+ if (pa_proplist_set(p, k, d, length) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ t->rindex = saved_rindex;
+ return -1;
+}
+
void pa_tagstruct_put(pa_tagstruct *t, ...) {
va_list va;
pa_assert(t);
@@ -591,6 +663,10 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {
pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
break;
+ case PA_TAG_PROPLIST:
+ pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
pa_assert_not_reached();
}
@@ -643,7 +719,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
case PA_TAG_BOOLEAN_TRUE:
case PA_TAG_BOOLEAN_FALSE:
- ret = pa_tagstruct_get_boolean(t, va_arg(va, int*));
+ ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
break;
case PA_TAG_TIMEVAL:
@@ -662,6 +738,10 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
break;
+ case PA_TAG_PROPLIST:
+ ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
pa_assert_not_reached();
}
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index e9bb9ac8..e7d07054 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -1,8 +1,6 @@
#ifndef footagstructhfoo
#define footagstructhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,10 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
typedef struct pa_tagstruct pa_tagstruct;
@@ -51,7 +53,8 @@ enum {
PA_TAG_TIMEVAL = 'T',
PA_TAG_USEC = 'U' /* 64bit unsigned */,
PA_TAG_CHANNEL_MAP = 'm',
- PA_TAG_CVOLUME = 'v'
+ PA_TAG_CVOLUME = 'v',
+ PA_TAG_PROPLIST = 'P'
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
@@ -70,11 +73,12 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
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);
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b);
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
int pa_tagstruct_get(pa_tagstruct *t, ...);
@@ -85,11 +89,12 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
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);
-int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b);
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
#endif
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
index d572f6e0..34f92a7e 100644
--- a/src/pulsecore/thread-mq.c
+++ b/src/pulsecore/thread-mq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,15 +41,15 @@
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) {
+static void asyncmsgq_read_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(pa_asyncmsgq_read_fd(q->outq) == fd);
pa_assert(events == PA_IO_EVENT_INPUT);
pa_asyncmsgq_ref(aq = q->outq);
- pa_asyncmsgq_after_poll(aq);
+ pa_asyncmsgq_write_after_poll(aq);
for (;;) {
pa_msgobject *object;
@@ -68,35 +66,52 @@ static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_even
pa_asyncmsgq_done(aq, ret);
}
- if (pa_asyncmsgq_before_poll(aq) == 0)
+ if (pa_asyncmsgq_read_before_poll(aq) == 0)
break;
}
pa_asyncmsgq_unref(aq);
}
-void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) {
+static void asyncmsgq_write_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_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
+ pa_assert(events == PA_IO_EVENT_INPUT);
+
+ pa_asyncmsgq_write_after_poll(q->inq);
+ pa_asyncmsgq_write_before_poll(q->inq);
+}
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
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));
+
+ pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+ pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+
+ pa_asyncmsgq_write_before_poll(q->inq);
+ pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
+
+ pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
+ pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
}
void pa_thread_mq_done(pa_thread_mq *q) {
pa_assert(q);
- q->mainloop->io_free(q->io_event);
- q->io_event = NULL;
+ q->mainloop->io_free(q->read_event);
+ q->mainloop->io_free(q->write_event);
+ q->read_event = q->write_event = NULL;
pa_asyncmsgq_unref(q->inq);
pa_asyncmsgq_unref(q->outq);
q->inq = q->outq = NULL;
-
+
q->mainloop = NULL;
}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
index 13b6e01f..3b5e0e78 100644
--- a/src/pulsecore/thread-mq.h
+++ b/src/pulsecore/thread-mq.h
@@ -1,8 +1,6 @@
#ifndef foopulsethreadmqhfoo
#define foopulsethreadmqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
#include <pulse/mainloop-api.h>
#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/rtpoll.h>
/* Two way communication between a thread and a mainloop. Before the
* thread is started a pa_pthread_mq should be initialized and than
@@ -34,10 +33,10 @@
typedef struct pa_thread_mq {
pa_mainloop_api *mainloop;
pa_asyncmsgq *inq, *outq;
- pa_io_event *io_event;
+ pa_io_event *read_event, *write_event;
} pa_thread_mq;
-void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop);
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
void pa_thread_mq_done(pa_thread_mq *q);
/* Install the specified pa_thread_mq object for the current thread */
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index 3c69adfb..20ed16d9 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -126,7 +124,7 @@ pa_thread* pa_thread_self(void) {
if ((t = PA_STATIC_TLS_GET(current_thread)))
return t;
-
+
/* This is a foreign thread, let's create a pthread structure to
* make sure that we can always return a sensible pointer */
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index cad1420a..c40d3342 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index 54ef320e..f3aca13e 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -1,8 +1,6 @@
#ifndef foopulsethreadhfoo
#define foopulsethreadhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 6bda3df0..fe5a4f18 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -34,7 +32,7 @@
#include "time-smoother.h"
-#define HISTORY_MAX 50
+#define HISTORY_MAX 64
/*
* Implementation of a time smoothing algorithm to synchronize remote
@@ -61,7 +59,6 @@
struct pa_smoother {
pa_usec_t adjust_time, history_time;
- pa_bool_t monotonic;
pa_usec_t time_offset;
@@ -70,27 +67,34 @@ struct pa_smoother {
pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */
double de; /* Gradient we estimated for point e */
+ pa_usec_t ry; /* The original y value for ex */
/* 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;
+ pa_usec_t last_y, last_x;
/* 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_bool_t monotonic:1;
+ pa_bool_t paused:1;
+
pa_usec_t pause_time;
+
+ unsigned min_history;
};
-pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic) {
+pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
pa_smoother *s;
pa_assert(adjust_time > 0);
pa_assert(history_time > 0);
+ pa_assert(min_history >= 2);
+ pa_assert(min_history <= HISTORY_MAX);
s = pa_xnew(pa_smoother, 1);
s->adjust_time = adjust_time;
@@ -101,18 +105,20 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
s->px = s->py = 0;
s->dp = 1;
- s->ex = s->ey = 0;
+ s->ex = s->ey = s->ry = 0;
s->de = 1;
s->history_idx = 0;
s->n_history = 0;
- s->last_y = 0;
+ s->last_y = s->last_x = 0;
s->abc_valid = FALSE;
s->paused = FALSE;
+ s->min_history = min_history;
+
return s;
}
@@ -122,39 +128,58 @@ void pa_smoother_free(pa_smoother* s) {
pa_xfree(s);
}
+#define REDUCE(x) \
+ do { \
+ x = (x) % HISTORY_MAX; \
+ } while(FALSE)
+
+#define REDUCE_INC(x) \
+ do { \
+ x = ((x)+1) % HISTORY_MAX; \
+ } while(FALSE)
+
+
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 */
+ /* Drop items from history which are too old, but make sure to
+ * always keep min_history in the history */
- for (j = s->n_history; j > 2; j--) {
+ while (s->n_history > s->min_history) {
- if (s->history_x[s->history_idx] + s->history_time >= x) {
+ 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;
+ REDUCE_INC(s->history_idx);
s->n_history --;
}
}
static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
- unsigned j;
+ unsigned j, i;
pa_assert(s);
+ /* First try to update an existing history entry */
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+
+ if (s->history_x[i] == x) {
+ s->history_y[i] = y;
+ return;
+ }
+
+ REDUCE_INC(i);
+ }
+
+ /* Drop old entries */
drop_old(s, x);
/* Calculate position for new entry */
j = s->history_idx + s->n_history;
- while (j >= HISTORY_MAX)
- j -= HISTORY_MAX;
+ REDUCE(j);
/* Fill in entry */
s->history_x[j] = x;
@@ -164,8 +189,9 @@ static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->n_history ++;
/* And make sure we don't store more entries than fit in */
- if (s->n_history >= HISTORY_MAX) {
+ if (s->n_history > HISTORY_MAX) {
s->history_idx += s->n_history - HISTORY_MAX;
+ REDUCE(s->history_idx);
s->n_history = HISTORY_MAX;
}
}
@@ -175,7 +201,9 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
int64_t ax = 0, ay = 0, k, t;
double r;
- drop_old(s, x);
+ /* Too few measurements, assume gradient of 1 */
+ if (s->n_history < s->min_history)
+ return 1;
/* First, calculate average of all measurements */
i = s->history_idx;
@@ -185,15 +213,10 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
ay += s->history_y[i];
c++;
- i++;
- while (i >= HISTORY_MAX)
- i -= HISTORY_MAX;
+ REDUCE_INC(i);
}
- /* Too few measurements, assume gradient of 1 */
- if (c < 2)
- return 1;
-
+ pa_assert(c >= s->min_history);
ax /= c;
ay /= c;
@@ -210,14 +233,45 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
k += dx*dy;
t += dx*dx;
- i++;
- while (i >= HISTORY_MAX)
- i -= HISTORY_MAX;
+ REDUCE_INC(i);
}
r = (double) k / t;
- return s->monotonic && r < 0 ? 0 : r;
+ return (s->monotonic && r < 0) ? 0 : r;
+}
+
+static void calc_abc(pa_smoother *s) {
+ pa_usec_t ex, ey, px, py;
+ int64_t kx, ky;
+ double de, dp;
+
+ pa_assert(s);
+
+ if (s->abc_valid)
+ return;
+
+ /* 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+bx^2+cx */
+ s->c = de;
+ s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
+ s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
+
+ s->abc_valid = TRUE;
}
static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
@@ -242,36 +296,10 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
} 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;
+ /* Ok, we're not yet on track, thus let's interpolate, and
+ * make sure that the first derivative is smooth */
- 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;
- }
+ calc_abc(s);
/* Move to origin */
x -= s->ex;
@@ -290,11 +318,6 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
/* 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;
}
@@ -303,22 +326,26 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
pa_usec_t ney;
double nde;
+ pa_bool_t is_new;
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);
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
- /* 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;
+ is_new = x >= s->ex;
+
+ if (is_new) {
+ /* 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;
+
+ s->ry = y;
+ }
/* Then, we add the new measurement to our history */
add_to_history(s, x, y);
@@ -327,27 +354,41 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
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->px = s->ex + s->adjust_time;
+ s->py = s->ry + s->dp *s->adjust_time;
s->abc_valid = FALSE;
+
+/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
}
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);
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
estimate(s, x, &y, NULL);
+
+ if (s->monotonic) {
+
+ /* Make sure the querier doesn't jump forth and back. */
+ pa_assert(x >= s->last_x);
+ s->last_x = x;
+
+ if (y < s->last_y)
+ y = s->last_y;
+ else
+ s->last_y = y;
+ }
+
+/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+
return y;
}
@@ -355,6 +396,8 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
pa_assert(s);
s->time_offset = offset;
+
+/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -363,6 +406,8 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
+/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+
s->paused = TRUE;
s->pause_time = x;
}
@@ -373,6 +418,42 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
if (!s->paused)
return;
+ pa_assert(x >= s->pause_time);
+
+/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+
s->paused = FALSE;
s->time_offset += x - s->pause_time;
}
+
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
+ pa_usec_t ney;
+ double nde;
+
+ pa_assert(s);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+ estimate(s, x, &ney, &nde);
+
+ /* Play safe and take the larger gradient, so that we wakeup
+ * earlier when this is used for sleeping */
+ if (s->dp > nde)
+ nde = s->dp;
+
+/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+
+ return (pa_usec_t) ((double) y_delay / nde);
+}
+
+void pa_smoother_reset(pa_smoother *s) {
+ pa_assert(s);
+
+ s->n_history = 0;
+ s->abc_valid = FALSE;
+
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
index 8b8512e2..2051e640 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -1,8 +1,6 @@
#ifndef foopulsetimesmootherhfoo
#define foopulsetimesmootherhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,15 +27,23 @@
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);
+pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
void pa_smoother_free(pa_smoother* s);
+/* Adds a new value to our dataset. x = local/system time, y = remote time */
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+
+/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
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);
+/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+void pa_smoother_reset(pa_smoother *s);
+
#endif
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
index ed54a86e..d1e0836b 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,9 @@
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/dynarray.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "tokenizer.h"
@@ -44,7 +42,7 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
int infty = 0;
const char delimiter[] = " \t\n\r";
const char *p;
-
+
pa_assert(a);
pa_assert(s);
@@ -77,7 +75,7 @@ pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
void pa_tokenizer_free(pa_tokenizer *t) {
pa_dynarray *a = (pa_dynarray*) t;
-
+
pa_assert(a);
pa_dynarray_free(a, token_free, NULL);
}
diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h
index 68a8db49..d51cd73e 100644
--- a/src/pulsecore/tokenizer.h
+++ b/src/pulsecore/tokenizer.h
@@ -1,8 +1,6 @@
#ifndef footokenizerhfoo
#define footokenizerhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index a740e39b..9e75f63a 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h
index 388c5a34..c5998d3e 100644
--- a/src/pulsecore/x11prop.h
+++ b/src/pulsecore/x11prop.h
@@ -1,8 +1,6 @@
#ifndef foox11prophfoo
#define foox11prophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index 1cb1ce84..00b6a157 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -63,7 +61,8 @@ struct pa_x11_wrapper {
struct pa_x11_client {
PA_LLIST_FIELDS(pa_x11_client);
pa_x11_wrapper *wrapper;
- int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+ pa_x11_event_cb_t event_cb;
+ pa_x11_kill_cb_t kill_cb;
void *userdata;
};
@@ -72,17 +71,23 @@ static void work(pa_x11_wrapper *w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
+ pa_x11_wrapper_ref(w);
+
while (XPending(w->display)) {
- pa_x11_client *c;
+ pa_x11_client *c, *n;
XEvent e;
XNextEvent(w->display, &e);
- for (c = w->clients; c; c = c->next) {
- pa_assert(c->callback);
- if (c->callback(w, &e, c->userdata) != 0)
- break;
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->event_cb)
+ if (c->event_cb(w, &e, c->userdata) != 0)
+ break;
}
}
+
+ pa_x11_wrapper_unref(w);
}
/* IO notification event for the X11 display connection */
@@ -115,7 +120,7 @@ static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
/* IO notification event for X11 internal connections */
static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
-
+
pa_assert(m);
pa_assert(e);
pa_assert(fd >= 0);
@@ -153,7 +158,7 @@ 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;
-
+
pa_assert(display);
pa_assert(w);
pa_assert(fd >= 0);
@@ -215,7 +220,7 @@ 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;
-
+
pa_core_assert_ref(c);
pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
@@ -251,15 +256,33 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
return w->display;
}
-pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {
+void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+ pa_x11_client *c, *n;
+
+ pa_assert(w);
+
+ pa_x11_wrapper_ref(w);
+
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->kill_cb)
+ c->kill_cb(w, c->userdata);
+ }
+
+ pa_x11_wrapper_unref(w);
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
pa_x11_client *c;
-
+
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
c = pa_xnew(pa_x11_client, 1);
c->wrapper = w;
- c->callback = cb;
+ c->event_cb = event_cb;
+ c->kill_cb = kill_cb;
c->userdata = userdata;
PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
@@ -271,7 +294,7 @@ void pa_x11_client_free(pa_x11_client *c) {
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/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
index 9bed2fce..badc3a1f 100644
--- a/src/pulsecore/x11wrap.h
+++ b/src/pulsecore/x11wrap.h
@@ -1,8 +1,6 @@
#ifndef foox11wraphfoo
#define foox11wraphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,6 +28,11 @@
typedef struct pa_x11_wrapper pa_x11_wrapper;
+typedef struct pa_x11_client pa_x11_client;
+
+typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata);
+
/* Return the X11 wrapper for this core. In case no wrapper was
existant before, allocate a new one */
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
@@ -43,10 +46,11 @@ void pa_x11_wrapper_unref(pa_x11_wrapper* w);
/* Return the X11 display object for this connection */
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
-typedef struct pa_x11_client pa_x11_client;
+/* Kill the connection to the X11 display */
+void pa_x11_wrapper_kill(pa_x11_wrapper *w);
/* Register an X11 client, that is called for each X11 event */
-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* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
/* Free an X11 client object */
void pa_x11_client_free(pa_x11_client *c);
diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c
index e7d662e1..08ad3dd4 100644
--- a/src/tests/asyncmsgq-test.c
+++ b/src/tests/asyncmsgq-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -91,7 +89,7 @@ int main(int argc, char *argv[]) {
printf("Operation B post\n");
pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL);
-
+
pa_thread_yield();
printf("Operation C send\n");
diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c
index 09b20047..4e8a1207 100644
--- a/src/tests/asyncq-test.c
+++ b/src/tests/asyncq-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@ static void producer(void *_q) {
pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1);
}
- pa_asyncq_push(q, PA_UINT_TO_PTR(-1), 1);
+ pa_asyncq_push(q, PA_UINT_TO_PTR(-1), TRUE);
printf("pushed end\n");
}
@@ -56,7 +54,7 @@ static void consumer(void *_q) {
sleep(1);
for (i = 0;; i++) {
- p = pa_asyncq_pop(q, 1);
+ p = pa_asyncq_pop(q, TRUE);
if (p == PA_UINT_TO_PTR(-1))
break;
diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c
index 98f36b61..9c234602 100644
--- a/src/tests/channelmap-test.c
+++ b/src/tests/channelmap-test.c
@@ -1,10 +1,8 @@
-/* $Id$ */
-
#include <stdio.h>
#include <assert.h>
#include <pulse/channelmap.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
@@ -22,12 +20,15 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+ pa_channel_map_init_extend(&map, 14, PA_CHANNEL_MAP_ALSA);
+
+ fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
pa_channel_map_parse(&map2, cm);
assert(pa_channel_map_equal(&map, &map2));
pa_channel_map_parse(&map2, "left,test");
-
return 0;
}
diff --git a/src/tests/close-test.c b/src/tests/close-test.c
new file mode 100644
index 00000000..7a6fec57
--- /dev/null
+++ b/src/tests/close-test.c
@@ -0,0 +1,20 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+
+ pa_close_all(5, -1);
+
+ return 0;
+}
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
index d582e9c5..b7145e8a 100644
--- a/src/tests/cpulimit-test.c
+++ b/src/tests/cpulimit-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,7 +28,7 @@
#include <signal.h>
#include <pulse/mainloop.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#ifdef TEST2
#include <pulse/mainloop-signal.h>
diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c
new file mode 100644
index 00000000..9f914553
--- /dev/null
+++ b/src/tests/envelope-test.c
@@ -0,0 +1,246 @@
+/***
+ 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 <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/envelope.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+const pa_envelope_def ramp_down = {
+ .n_points = 2,
+ .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 1.0, 0.2 },
+ .i = { 0x10000, 0x10000/5 }
+ }
+};
+
+const pa_envelope_def ramp_up = {
+ .n_points = 2,
+ .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.2, 1.0 },
+ .i = { 0x10000/5, 0x10000 }
+ }
+};
+
+const pa_envelope_def ramp_down2 = {
+ .n_points = 2,
+ .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.8, 0.7 },
+ .i = { 0x10000*4/5, 0x10000*7/10 }
+ }
+};
+
+const pa_envelope_def ramp_up2 = {
+ .n_points = 2,
+ .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.7, 0.9 },
+ .i = { 0x10000*7/10, 0x10000*9/10 }
+ }
+};
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+ void *d;
+ unsigned i;
+
+ static unsigned j = 0;
+
+ 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: {
+ int16_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("%i\t%i\n", j++, *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ int32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("%i\t%i\n", j++, *(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("%i\t%1.3g\n", j++, PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, *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 *block;
+ void *d;
+ unsigned n_samples;
+
+ block = pa_memblock_new(pool, pa_bytes_per_second(ss));
+ n_samples = pa_memblock_get_length(block) / pa_sample_size(ss);
+
+ d = pa_memblock_acquire(block);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ int16_t *i;
+
+ for (i = d; n_samples > 0; n_samples--, i++)
+ *i = 0x7FFF;
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ int32_t *i;
+
+ for (i = d; n_samples > 0; n_samples--, i++)
+ *i = 0x7FFFFFFF;
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32RE:
+ case PA_SAMPLE_FLOAT32NE: {
+ float *f;
+
+ for (f = d; n_samples > 0; n_samples--, f++)
+ *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0);
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(block);
+ return block;
+}
+
+int main(int argc, char *argv[]) {
+ pa_mempool *pool;
+ pa_memblock *block;
+ pa_memchunk chunk;
+ pa_envelope *envelope;
+ pa_envelope_item *item1, *item2;
+
+ const pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16NE,
+ .channels = 1,
+ .rate = 200
+ };
+
+ const pa_cvolume v = {
+ .channels = 1,
+ .values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
+ };
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+ pa_assert_se(envelope = pa_envelope_new(&ss));
+
+ block = generate_block(pool, &ss);
+
+ chunk.memblock = pa_memblock_ref(block);
+ chunk.length = pa_memblock_get_length(block);
+ chunk.index = 0;
+
+ pa_volume_memchunk(&chunk, &ss, &v);
+
+ item1 = pa_envelope_add(envelope, &ramp_down);
+ item2 = pa_envelope_add(envelope, &ramp_down2);
+ pa_envelope_apply(envelope, &chunk);
+ dump_block(&ss, &chunk);
+
+ pa_memblock_unref(chunk.memblock);
+
+ chunk.memblock = pa_memblock_ref(block);
+ chunk.length = pa_memblock_get_length(block);
+ chunk.index = 0;
+
+ item1 = pa_envelope_replace(envelope, item1, &ramp_up);
+ item2 = pa_envelope_replace(envelope, item2, &ramp_up2);
+ pa_envelope_apply(envelope, &chunk);
+ dump_block(&ss, &chunk);
+
+ pa_memblock_unref(chunk.memblock);
+
+ pa_envelope_remove(envelope, item1);
+ pa_envelope_remove(envelope, item2);
+ pa_envelope_free(envelope);
+
+ pa_memblock_unref(block);
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/flist-test.c b/src/tests/flist-test.c
index 7e54454e..b2c648da 100644
--- a/src/tests/flist-test.c
+++ b/src/tests/flist-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
index 29ebbe22..7c7a8996 100644
--- a/src/tests/get-binary-name-test.c
+++ b/src/tests/get-binary-name-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c
index 8628f521..60b965cd 100644
--- a/src/tests/hook-list-test.c
+++ b/src/tests/hook-list-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -7,12 +5,12 @@
#include <pulsecore/hook-list.h>
#include <pulsecore/log.h>
-static pa_hook_result_t func1(const char*hook_data, const char*call_data, const char*slot_data) {
+static pa_hook_result_t func1(const char *hook_data, const char *call_data, const char *slot_data) {
pa_log("(func1) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
return PA_HOOK_OK;
}
-static pa_hook_result_t func2(const char*hook_data, const char*call_data, const char*slot_data) {
+static pa_hook_result_t func2(const char *hook_data, const char *call_data, const char *slot_data) {
pa_log("(func2) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
return PA_HOOK_OK;
}
@@ -23,9 +21,9 @@ int main(int argc, char *argv[]) {
pa_hook_init(&hook, (void*) "hook");
- pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot1");
- slot = pa_hook_connect(&hook, (pa_hook_cb_t) func2, (void*) "slot2");
- pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot3");
+ pa_hook_connect(&hook, PA_HOOK_LATE, (pa_hook_cb_t) func1, (void*) "slot1");
+ slot = pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func2, (void*) "slot2");
+ pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func1, (void*) "slot3");
pa_hook_fire(&hook, (void*) "call1");
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index 85a509d4..9d930774 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,10 +40,9 @@ static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_mainloop_api *mainloop_api = NULL;
-static void stream_write_cb(pa_stream *p, size_t length, void *userdata) {
-
+static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
/* Just some silence */
- pa_stream_write(p, pa_xmalloc0(length), length, pa_xfree, 0, PA_SEEK_RELATIVE);
+ pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE);
}
/* This is called whenever the context status changes */
@@ -63,7 +60,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
- .channels = 1
+ .channels = 2
};
fprintf(stderr, "Connection established.\n");
@@ -112,9 +109,10 @@ int main(int argc, char *argv[]) {
pa_threaded_mainloop_start(m);
for (k = 0; k < 5000; k++) {
- int success = 0, changed = 0;
+ pa_bool_t success = FALSE, changed = FALSE;
pa_usec_t t, rtc;
struct timeval now, tv;
+ pa_bool_t playing = FALSE;
pa_threaded_mainloop_lock(m);
@@ -122,22 +120,26 @@ int main(int argc, char *argv[]) {
const pa_timing_info *info;
if (pa_stream_get_time(stream, &t) >= 0)
- success = 1;
+ success = TRUE;
- if ((info = pa_stream_get_timing_info(stream)))
- if (last_info.tv_usec != info->timestamp.tv_usec || last_info.tv_sec != info->timestamp.tv_sec) {
- changed = 1;
+ if ((info = pa_stream_get_timing_info(stream))) {
+ if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
+ changed = TRUE;
last_info = info->timestamp;
}
+ if (info->playing)
+ playing = TRUE;
+ }
}
pa_threaded_mainloop_unlock(m);
- if (success) {
- pa_gettimeofday(&now);
+ pa_gettimeofday(&now);
+ if (success) {
rtc = pa_timeval_diff(&now, &start);
- 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);
+ printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed, playing);
+ fflush(stdout);
old_t = t;
old_rtc = rtc;
}
diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c
index d1bcb3e3..bcdd469a 100644
--- a/src/tests/ipacl-test.c
+++ b/src/tests/ipacl-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c
index c386251c..9fa2e466 100644
--- a/src/tests/mainloop-test.c
+++ b/src/tests/mainloop-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,9 @@
#include <assert.h>
#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/gccmacro.h>
#ifdef GLIB_MAIN_LOOP
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
index d1013118..9e358359 100644
--- a/src/tests/mcalign-test.c
+++ b/src/tests/mcalign-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,9 +29,10 @@
#include <stdlib.h>
#include <time.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/core-util.h>
#include <pulsecore/mcalign.h>
-#include <pulsecore/gccmacro.h>
/* A simple program for testing pa_mcalign */
diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c
index 2b9d3401..6da1b1e9 100644
--- a/src/tests/memblock-test.c
+++ b/src/tests/memblock-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
index 25ea399b..7bf992a1 100644
--- a/src/tests/memblockq-test.c
+++ b/src/tests/memblockq-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,22 +29,48 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/log.h>
+static void dump(pa_memblockq *bq) {
+ printf(">");
+
+ for (;;) {
+ pa_memchunk out;
+ char *e;
+ size_t n;
+ void *q;
+
+ if (pa_memblockq_peek(bq, &out) < 0)
+ break;
+
+ q = pa_memblock_acquire(out.memblock);
+ for (e = (char*) q + 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.length);
+ }
+
+ printf("<\n");
+}
+
int main(int argc, char *argv[]) {
int ret;
pa_mempool *p;
pa_memblockq *bq;
pa_memchunk chunk1, chunk2, chunk3, chunk4;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_log_set_maximal_level(PA_LOG_DEBUG);
p = pa_mempool_new(0);
- silence = pa_memblock_new_fixed(p, (char*) "__", 2, 1);
- assert(silence);
+ silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1);
+ assert(silence.memblock);
+ silence.index = 0;
+ silence.length = pa_memblock_get_length(silence.memblock);
- bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, silence);
+ bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, &silence);
assert(bq);
chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1);
@@ -72,13 +96,13 @@ int main(int argc, char *argv[]) {
ret = pa_memblockq_push(bq, &chunk1);
assert(ret == 0);
- ret = pa_memblockq_push(bq, &chunk1);
+ ret = pa_memblockq_push(bq, &chunk2);
assert(ret == 0);
- ret = pa_memblockq_push(bq, &chunk2);
+ ret = pa_memblockq_push(bq, &chunk3);
assert(ret == 0);
- ret = pa_memblockq_push(bq, &chunk2);
+ ret = pa_memblockq_push(bq, &chunk4);
assert(ret == 0);
pa_memblockq_seek(bq, -6, 0);
@@ -86,7 +110,7 @@ int main(int argc, char *argv[]) {
assert(ret == 0);
pa_memblockq_seek(bq, -2, 0);
- ret = pa_memblockq_push(bq, &chunk3);
+ ret = pa_memblockq_push(bq, &chunk1);
assert(ret == 0);
pa_memblockq_seek(bq, -10, 0);
@@ -119,35 +143,22 @@ int main(int argc, char *argv[]) {
ret = pa_memblockq_push(bq, &chunk3);
assert(ret == 0);
- pa_memblockq_shorten(bq, pa_memblockq_get_length(bq)-2);
+ pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE);
- printf(">");
+ dump(bq);
- for (;;) {
- pa_memchunk out;
- char *e;
- size_t n;
+ pa_memblockq_rewind(bq, 52);
- if (pa_memblockq_peek(bq, &out) < 0)
- break;
-
- 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.length);
- }
-
- printf("<\n");
+ dump(bq);
pa_memblockq_free(bq);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
pa_memblock_unref(chunk1.memblock);
pa_memblock_unref(chunk2.memblock);
pa_memblock_unref(chunk3.memblock);
pa_memblock_unref(chunk4.memblock);
+ pa_mempool_free(p);
+
return 0;
}
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
new file mode 100644
index 00000000..f3f6f829
--- /dev/null
+++ b/src/tests/mix-test.c
@@ -0,0 +1,259 @@
+/***
+ 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_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%08x ", *(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.5f ", 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_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ u[0] = 0x00000001;
+ u[1] = 0xFFFF0002;
+ u[2] = 0x7FFF0003;
+ u[3] = 0x80000004;
+ u[4] = 0x9fff0005;
+ u[5] = 0x3fff0006;
+ u[6] = 0x10007;
+ u[7] = 0xF0000008;
+ u[8] = 0x200009;
+ u[9] = 0x21000A;
+ 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;
+ pa_cvolume v;
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+
+ a.channels = 1;
+ a.rate = 44100;
+
+ v.channels = a.channels;
+ v.values[0] = pa_sw_volume_from_linear(0.9);
+
+ for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+ pa_memchunk i, j, k;
+ pa_mix_info m[2];
+ void *ptr;
+
+ printf("=== mixing: %s\n", pa_sample_format_to_string(a.format));
+
+ /* Generate block */
+ i.memblock = generate_block(pool, &a);
+ i.length = pa_memblock_get_length(i.memblock);
+ i.index = 0;
+
+ /* Make a copy */
+ j = i;
+ pa_memblock_ref(j.memblock);
+ pa_memchunk_make_writable(&j, 0);
+
+ /* Adjust volume of the copy */
+ pa_volume_memchunk(&j, &a, &v);
+
+ m[0].chunk = i;
+ m[0].volume.values[0] = PA_VOLUME_NORM;
+ m[0].volume.channels = a.channels;
+ m[1].chunk = j;
+ m[1].volume.values[0] = PA_VOLUME_NORM;
+ m[1].volume.channels = a.channels;
+
+ k.memblock = pa_memblock_new(pool, i.length);
+ k.length = i.length;
+ k.index = 0;
+
+ ptr = (uint8_t*) pa_memblock_acquire(k.memblock) + k.index;
+ pa_mix(m, 2, ptr, k.length, &a, NULL, FALSE);
+ pa_memblock_release(k.memblock);
+
+ dump_block(&a, &i);
+ dump_block(&a, &j);
+ dump_block(&a, &k);
+
+ pa_memblock_unref(i.memblock);
+ pa_memblock_unref(j.memblock);
+ pa_memblock_unref(k.memblock);
+ }
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c
index 2da67c1a..b26e4b68 100644
--- a/src/tests/pacat-simple.c
+++ b/src/tests/pacat-simple.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,7 +29,7 @@
#include <pulse/simple.h>
#include <pulse/error.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#define BUFSIZE 1024
diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c
index d7d88360..6c0d529b 100644
--- a/src/tests/parec-simple.c
+++ b/src/tests/parec-simple.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,7 +28,7 @@
#include <pulse/simple.h>
#include <pulse/error.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#define BUFSIZE 1024
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
new file mode 100644
index 00000000..20041af6
--- /dev/null
+++ b/src/tests/proplist-test.c
@@ -0,0 +1,60 @@
+/***
+ 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/proplist.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+int main(int argc, char*argv[]) {
+ pa_proplist *a, *b;
+ char *s, *t;
+
+ a = pa_proplist_new();
+ pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
+ pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0);
+
+ b = pa_proplist_new();
+ pa_assert_se(pa_proplist_sets(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0);
+ pa_assert_se(pa_proplist_set(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0);
+
+ pa_proplist_update(a, PA_UPDATE_MERGE, b);
+
+ pa_assert_se(!pa_proplist_gets(a, PA_PROP_MEDIA_ICON));
+
+ printf("%s\n", pa_strnull(pa_proplist_gets(a, PA_PROP_MEDIA_TITLE)));
+ pa_assert_se(pa_proplist_unset(b, PA_PROP_MEDIA_TITLE) == 0);
+
+ s = pa_proplist_to_string(a);
+ t = pa_proplist_to_string(b);
+ printf("---\n%s---\n%s", s, t);
+ pa_xfree(s);
+ pa_xfree(t);
+
+ pa_proplist_free(a);
+ pa_proplist_free(b);
+
+ return 0;
+}
diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c
index 088b0741..105f094a 100644
--- a/src/tests/queue-test.c
+++ b/src/tests/queue-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,12 +38,12 @@ int main(int argc, char *argv[]) {
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");
@@ -62,7 +60,7 @@ int main(int argc, char *argv[]) {
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/remix-test.c b/src/tests/remix-test.c
new file mode 100644
index 00000000..4777c150
--- /dev/null
+++ b/src/tests/remix-test.c
@@ -0,0 +1,89 @@
+/***
+ 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>
+
+int main(int argc, char *argv[]) {
+
+ static const pa_channel_map maps[] = {
+ { 1, { PA_CHANNEL_POSITION_MONO } },
+ { 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_LFE } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_CENTER } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_REAR_CENTER } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT } },
+ { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+ { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE } },
+ { 6, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER } },
+ { 8, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT } },
+ { 0, { 0 } }
+ };
+
+ unsigned i, j;
+ pa_mempool *pool;
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+
+ for (i = 0; maps[i].channels > 0; i++)
+ for (j = 0; maps[j].channels > 0; j++) {
+ char a[PA_CHANNEL_MAP_SNPRINT_MAX], b[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_resampler *r;
+ pa_sample_spec ss1, ss2;
+
+ pa_log_info("Converting from '%s' to '%s'.\n", pa_channel_map_snprint(a, sizeof(a), &maps[i]), pa_channel_map_snprint(b, sizeof(b), &maps[j]));
+
+ ss1.channels = maps[i].channels;
+ ss2.channels = maps[j].channels;
+
+ ss1.rate = ss2.rate = 44100;
+ ss1.format = ss2.format = PA_SAMPLE_S16NE;
+
+ r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], PA_RESAMPLER_AUTO, 0);
+
+ /* We don't really care for the resampler. We just want to
+ * see the remixing debug output. */
+
+ pa_resampler_free(r);
+ }
+
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
index 3b4a7386..1a20be2c 100644
--- a/src/tests/resampler-test.c
+++ b/src/tests/resampler-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,6 +69,16 @@ static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
break;
}
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%08x ", *(u++));
+
+ break;
+ }
+
case PA_SAMPLE_FLOAT32NE:
case PA_SAMPLE_FLOAT32RE: {
float *u = d;
@@ -137,6 +145,23 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
break;
}
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ u[0] = 0x00000001;
+ u[1] = 0xFFFF0002;
+ u[2] = 0x7FFF0003;
+ u[3] = 0x80000004;
+ u[4] = 0x9fff0005;
+ u[5] = 0x3fff0006;
+ u[6] = 0x10007;
+ u[7] = 0xF0000008;
+ u[8] = 0x200009;
+ u[9] = 0x21000A;
+ break;
+ }
+
case PA_SAMPLE_FLOAT32NE:
case PA_SAMPLE_FLOAT32RE: {
float *u = d;
@@ -195,8 +220,8 @@ int main(int argc, char *argv[]) {
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));
+ pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, PA_RESAMPLER_AUTO, 0));
+ pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, PA_RESAMPLER_AUTO, 0));
i.memblock = generate_block(pool, &a);
i.length = pa_memblock_get_length(i.memblock);
diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c
index 3ab992a1..953fd61d 100644
--- a/src/tests/rtpoll-test.c
+++ b/src/tests/rtpoll-test.c
@@ -1,5 +1,3 @@
-/* $Id: thread-test.c 1621 2007-08-10 22:00:22Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,9 @@ int main(int argc, char *argv[]) {
pa_rtpoll_item *i, *w;
struct pollfd *pollfd;
+#ifdef SIGRTMIN
pa_rtsig_configure(SIGRTMIN+10, SIGRTMAX);
+#endif
p = pa_rtpoll_new();
@@ -65,7 +65,7 @@ int main(int argc, char *argv[]) {
pa_rtpoll_item_set_before_callback(w, worker);
pa_rtpoll_install(p);
- pa_rtpoll_set_timer_periodic(p, 10000000); /* 10 s */
+ pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */
pa_rtpoll_run(p, 1);
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
new file mode 100644
index 00000000..91e85c36
--- /dev/null
+++ b/src/tests/rtstutter.c
@@ -0,0 +1,117 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sched.h>
+#include <inttypes.h>
+#include <string.h>
+#include <pthread.h>
+
+#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+static int msec_lower, msec_upper;
+
+static void* work(void *p) PA_GCC_NORETURN;
+
+static void* work(void *p) {
+ cpu_set_t mask;
+ struct sched_param param;
+
+ pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p));
+
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = 12;
+ pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0);
+
+ CPU_ZERO(&mask);
+ CPU_SET(PA_PTR_TO_INT(p), &mask);
+ pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+
+ for (;;) {
+ struct timespec now, end;
+ uint64_t nsec;
+
+ pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p));
+ sleep(1);
+
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);
+
+ nsec =
+ (uint64_t) ((((double) rand())*(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) +
+ (uint64_t) (msec_lower*PA_NSEC_PER_MSEC);
+
+ pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC));
+
+ end.tv_sec += nsec / PA_NSEC_PER_SEC;
+ end.tv_nsec += nsec % PA_NSEC_PER_SEC;
+
+ while ((pa_usec_t) end.tv_nsec > PA_NSEC_PER_SEC) {
+ end.tv_sec++;
+ end.tv_nsec -= PA_NSEC_PER_SEC;
+ }
+
+ do {
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &now) == 0);
+ } while (now.tv_sec < end.tv_sec ||
+ (now.tv_sec == end.tv_sec && now.tv_nsec < end.tv_nsec));
+ }
+}
+
+int main(int argc, char*argv[]) {
+ int n;
+
+ srand(time(NULL));
+
+ if (argc >= 3) {
+ msec_lower = atoi(argv[1]);
+ msec_upper = atoi(argv[2]);
+ } else if (argc >= 2) {
+ msec_lower = 0;
+ msec_upper = atoi(argv[1]);
+ } else {
+ msec_lower = 0;
+ msec_upper = 1000;
+ }
+
+ pa_assert(msec_upper > 0);
+ pa_assert(msec_upper >= msec_lower);
+
+ pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper);
+
+ for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) {
+ pthread_t t;
+ pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
+ }
+
+ work(PA_INT_TO_PTR(0));
+
+ return 0;
+}
diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c
index e8f36292..d64a8902 100644
--- a/src/tests/sig2str-test.c
+++ b/src/tests/sig2str-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,7 +29,7 @@
int main(int argc, char *argv[]) {
int sig;
-
+
for (sig = -1; sig <= NSIG; sig++)
printf("%i = %s\n", sig, pa_sig2str(sig));
diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
index caa7df70..b78f3c91 100644
--- a/src/tests/smoother-test.c
+++ b/src/tests/smoother-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,31 +45,31 @@ int main(int argc, char*argv[]) {
srand(0);
- for (m = 0, u = 0; u < PA_ELEMENTSOF(msec)-2; u+= 2) {
+ for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) {
- msec[u] = m+1;
- msec[u+1] = m + rand() % 2000 - 1000;
+ msec[u] = m+1 + (rand() % 100) - 50;
+ msec[u+1] = m + (rand() % 2000) - 1000;
m += rand() % 100;
+ if (msec[u] < 0)
+ msec[u] = 0;
+
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);
+ s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE, 6);
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) {
+ while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) {
pa_smoother_put(s, msec[u]*PA_USEC_PER_MSEC, msec[u+1]*PA_USEC_PER_MSEC);
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);
+ printf("%llu\t%llu\n", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));
}
pa_smoother_free(s);
diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c
new file mode 100644
index 00000000..0ab06776
--- /dev/null
+++ b/src/tests/stripnul.c
@@ -0,0 +1,70 @@
+/***
+ 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 <string.h>
+#include <inttypes.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+int main(int argc, char *argv[]) {
+ FILE *i, *o;
+ size_t granularity;
+ pa_bool_t found;
+ uint8_t *zero;
+
+ pa_assert_se(argc >= 2);
+ pa_assert_se((granularity = atoi(argv[1])) >= 1);
+ pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin));
+ pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout));
+
+ zero = pa_xmalloc0(granularity);
+
+ for (;;) {
+ uint8_t buffer[16*1024], *p;
+ size_t k;
+
+ k = fread(buffer, granularity, sizeof(buffer)/granularity, i);
+
+ if (k <= 0)
+ break;
+
+ if (found)
+ pa_assert_se(fwrite(buffer, granularity, k, o) == k);
+ else {
+ for (p = buffer; (p-buffer)/granularity < k; p += granularity)
+ if (memcmp(p, zero, granularity)) {
+ size_t left;
+ found = TRUE;
+ left = k - (p-buffer)/granularity;
+ pa_assert_se(fwrite(p, granularity, left, o) == left);
+ break;
+ }
+ }
+ }
+
+ fflush(o);
+
+ return 0;
+}
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
index 47770b5d..2bd1645c 100644
--- a/src/tests/strlist-test.c
+++ b/src/tests/strlist-test.c
@@ -1,8 +1,9 @@
#include <stdio.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/strlist.h>
-#include <pulsecore/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
char *t, *u;
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
index 63510eb6..7ab3a25c 100644
--- a/src/tests/sync-playback.c
+++ b/src/tests/sync-playback.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
index be1f1ec2..7a62f85a 100644
--- a/src/tests/thread-mainloop-test.c
+++ b/src/tests/thread-mainloop-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,8 +28,8 @@
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/thread-mainloop.h>
+#include <pulse/gccmacro.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
@@ -54,7 +52,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
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 72dde6cb..f29b5e71 100644
--- a/src/tests/thread-test.c
+++ b/src/tests/thread-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/tests/utf8-test.c b/src/tests/utf8-test.c
index b9594dcc..f1708ad4 100644
--- a/src/tests/utf8-test.c
+++ b/src/tests/utf8-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
#include <stdio.h>
#include <assert.h>
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index dcc1ec51..d2c0ff69 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
#include <stdio.h>
#include <pulse/volume.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
pa_volume_t v;
diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c
index d88001ef..f2ed9553 100644
--- a/src/utils/pabrowse.c
+++ b/src/utils/pabrowse.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 96b6adb7..ee784a99 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,10 +38,12 @@
#define TIME_EVENT_USEC 50000
-#if PA_API_VERSION < 9
+#if PA_API_VERSION < 10
#error Invalid PulseAudio API version
#endif
+#define CLEAR_LINE "\x1B[K"
+
static enum { RECORD, PLAYBACK } mode = PLAYBACK;
static pa_context *context = NULL;
@@ -69,6 +69,10 @@ static pa_sample_spec sample_spec = {
static pa_channel_map channel_map;
static int channel_map_set = 0;
+static pa_stream_flags_t flags = 0;
+
+static size_t latency = 0, process_time=0;
+
/* A shortcut for terminating the application */
static void quit(int ret) {
assert(mainloop_api);
@@ -105,7 +109,8 @@ static void do_stream_write(size_t length) {
/* This is called whenever new data may be written to the stream */
static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
- assert(s && length);
+ assert(s);
+ assert(length > 0);
if (stdio_event)
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
@@ -119,7 +124,8 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
/* This is called whenever new data may is available */
static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
const void *data;
- assert(s && length);
+ assert(s);
+ assert(length > 0);
if (stdio_event)
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
@@ -130,7 +136,8 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
return;
}
- assert(data && length);
+ assert(data);
+ assert(length > 0);
if (buffer) {
fprintf(stderr, "Buffer overrun, dropping incoming data\n");
@@ -159,6 +166,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
case PA_STREAM_READY:
if (verbose) {
const pa_buffer_attr *a;
+ char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
fprintf(stderr, "Stream successfully created.\n");
@@ -172,9 +180,16 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
assert(mode == RECORD);
fprintf(stderr, "Buffer metrics: maxlength=%u, fragsize=%u\n", a->maxlength, a->fragsize);
}
-
}
+ fprintf(stderr, "Using sample spec '%s', channel map '%s'.\n",
+ pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
+ pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
+
+ fprintf(stderr, "Connected to device %s (%u, %ssuspended).\n",
+ pa_stream_get_device_name(s),
+ pa_stream_get_device_index(s),
+ pa_stream_is_suspended(s) ? "" : "not ");
}
break;
@@ -186,6 +201,45 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
}
}
+static void stream_suspended_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose) {
+ if (pa_stream_is_suspended(s))
+ fprintf(stderr, "Stream device suspended." CLEAR_LINE " \n");
+ else
+ fprintf(stderr, "Stream device resumed." CLEAR_LINE " \n");
+ }
+}
+
+static void stream_underflow_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream underrun." CLEAR_LINE " \n");
+}
+
+static void stream_overflow_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream overrun." CLEAR_LINE " \n");
+}
+
+static void stream_started_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream started." CLEAR_LINE " \n");
+}
+
+static void stream_moved_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream moved to device %s (%u, %ssuspended)." CLEAR_LINE " \n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not ");
+}
+
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
assert(c);
@@ -198,11 +252,13 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_READY: {
int r;
+ pa_buffer_attr buffer_attr;
- assert(c && !stream);
+ assert(c);
+ assert(!stream);
if (verbose)
- fprintf(stderr, "Connection established.\n");
+ fprintf(stderr, "Connection established." CLEAR_LINE " \n");
if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
@@ -212,16 +268,28 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_stream_set_state_callback(stream, stream_state_callback, NULL);
pa_stream_set_write_callback(stream, stream_write_callback, NULL);
pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+ pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
+ pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
+ pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
+ pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
+ pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+
+ if (latency > 0) {
+ memset(&buffer_attr, 0, sizeof(buffer_attr));
+ buffer_attr.tlength = latency;
+ buffer_attr.minreq = process_time;
+ flags |= PA_STREAM_ADJUST_LATENCY;
+ }
if (mode == PLAYBACK) {
pa_cvolume cv;
- if ((r = pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
+ if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
} else {
- if ((r = pa_stream_connect_record(stream, device, NULL, 0)) < 0) {
+ if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
@@ -280,7 +348,10 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
size_t l, w = 0;
ssize_t r;
- assert(a == mainloop_api && e && stdio_event == e);
+
+ assert(a == mainloop_api);
+ assert(e);
+ assert(stdio_event == e);
if (buffer) {
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
@@ -330,7 +401,10 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
/* Some data may be written to STDOUT */
static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
ssize_t r;
- assert(a == mainloop_api && e && stdio_event == e);
+
+ assert(a == mainloop_api);
+ assert(e);
+ assert(stdio_event == e);
if (!buffer) {
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
@@ -367,14 +441,14 @@ static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig,
/* Show the current latency */
static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
- pa_usec_t latency, usec;
+ pa_usec_t l, usec;
int negative = 0;
assert(s);
if (!success ||
pa_stream_get_time(s, &usec) < 0 ||
- pa_stream_get_latency(s, &latency, &negative) < 0) {
+ pa_stream_get_latency(s, &l, &negative) < 0) {
fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
return;
@@ -382,7 +456,7 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd
fprintf(stderr, "Time: %0.3f sec; Latency: %0.0f usec. \r",
(float) usec / 1000000,
- (float) latency * (negative?-1:1));
+ (float) l * (negative?-1:1));
}
/* Someone requested that the latency is shown */
@@ -429,7 +503,18 @@ static void help(const char *argv0) {
" float32be, ulaw, alaw (defaults to s16ne)\n"
" --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
" (defaults to 2)\n"
- " --channel-map=CHANNELMAP Channel map to use instead of the default\n",
+ " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
+ " --fix-format Take the sample format from the sink the stream is\n"
+ " being connected to.\n"
+ " --fix-rate Take the sampling rate from the sink the stream is\n"
+ " being connected to.\n"
+ " --fix-channels Take the number of channels and the channel map\n"
+ " from the sink the stream is being connected to.\n"
+ " --no-remix Don't upmix or downmix channels.\n"
+ " --no-remap Map channels by index instead of name.\n"
+ " --latency=BYTES Request the specified latency in bytes.\n"
+ " --process-time=BYTES Request the specified process time per request in bytes.\n"
+ ,
argv0);
}
@@ -441,6 +526,13 @@ enum {
ARG_SAMPLEFORMAT,
ARG_CHANNELS,
ARG_CHANNELMAP,
+ ARG_FIX_FORMAT,
+ ARG_FIX_RATE,
+ ARG_FIX_CHANNELS,
+ ARG_NO_REMAP,
+ ARG_NO_REMIX,
+ ARG_LATENCY,
+ ARG_PROCESS_TIME
};
int main(int argc, char *argv[]) {
@@ -450,21 +542,28 @@ int main(int argc, char *argv[]) {
pa_time_event *time_event = NULL;
static const struct option long_options[] = {
- {"record", 0, NULL, 'r'},
- {"playback", 0, NULL, 'p'},
- {"device", 1, NULL, 'd'},
- {"server", 1, NULL, 's'},
- {"client-name", 1, NULL, 'n'},
- {"stream-name", 1, NULL, ARG_STREAM_NAME},
- {"version", 0, NULL, ARG_VERSION},
- {"help", 0, NULL, 'h'},
- {"verbose", 0, NULL, 'v'},
- {"volume", 1, NULL, ARG_VOLUME},
- {"rate", 1, NULL, ARG_SAMPLERATE},
- {"format", 1, NULL, ARG_SAMPLEFORMAT},
- {"channels", 1, NULL, ARG_CHANNELS},
- {"channel-map", 1, NULL, ARG_CHANNELMAP},
- {NULL, 0, NULL, 0}
+ {"record", 0, NULL, 'r'},
+ {"playback", 0, NULL, 'p'},
+ {"device", 1, NULL, 'd'},
+ {"server", 1, NULL, 's'},
+ {"client-name", 1, NULL, 'n'},
+ {"stream-name", 1, NULL, ARG_STREAM_NAME},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"volume", 1, NULL, ARG_VOLUME},
+ {"rate", 1, NULL, ARG_SAMPLERATE},
+ {"format", 1, NULL, ARG_SAMPLEFORMAT},
+ {"channels", 1, NULL, ARG_CHANNELS},
+ {"channel-map", 1, NULL, ARG_CHANNELMAP},
+ {"fix-format", 0, NULL, ARG_FIX_FORMAT},
+ {"fix-rate", 0, NULL, ARG_FIX_RATE},
+ {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
+ {"no-remap", 0, NULL, ARG_NO_REMAP},
+ {"no-remix", 0, NULL, ARG_NO_REMIX},
+ {"latency", 1, NULL, ARG_LATENCY},
+ {"process-time", 1, NULL, ARG_PROCESS_TIME},
+ {NULL, 0, NULL, 0}
};
if (!(bn = strrchr(argv[0], '/')))
@@ -542,13 +641,47 @@ int main(int argc, char *argv[]) {
case ARG_CHANNELMAP:
if (!pa_channel_map_parse(&channel_map, optarg)) {
- fprintf(stderr, "Invalid channel map\n");
+ fprintf(stderr, "Invalid channel map '%s'\n", optarg);
goto quit;
}
channel_map_set = 1;
break;
+ case ARG_FIX_CHANNELS:
+ flags |= PA_STREAM_FIX_CHANNELS;
+ break;
+
+ case ARG_FIX_RATE:
+ flags |= PA_STREAM_FIX_RATE;
+ break;
+
+ case ARG_FIX_FORMAT:
+ flags |= PA_STREAM_FIX_FORMAT;
+ break;
+
+ case ARG_NO_REMIX:
+ flags |= PA_STREAM_NO_REMIX_CHANNELS;
+ break;
+
+ case ARG_NO_REMAP:
+ flags |= PA_STREAM_NO_REMAP_CHANNELS;
+ break;
+
+ case ARG_LATENCY:
+ if (((latency = atoi(optarg))) <= 0) {
+ fprintf(stderr, "Invalid latency specification '%s'\n", optarg);
+ goto quit;
+ }
+ break;
+
+ case ARG_PROCESS_TIME:
+ if (((process_time = atoi(optarg))) <= 0) {
+ fprintf(stderr, "Invalid process time specification '%s'\n", optarg);
+ goto quit;
+ }
+ break;
+
default:
goto quit;
}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index 16e5822f..67d95252 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -36,6 +34,7 @@
#include <pulse/error.h>
#include <pulse/util.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
@@ -49,8 +48,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
char ibuf[256], obuf[256];
size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
fd_set ifds, ofds;
+ char *cli;
- if (pa_pid_file_check_running(&pid) < 0) {
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
pa_log("no PulseAudio daemon running");
goto fail;
}
@@ -62,7 +62,12 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+ if (!(cli = pa_runtime_path("cli")))
+ goto fail;
+
+ pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+ pa_xfree(cli);
for (i = 0; i < 5; i++) {
int r;
@@ -75,12 +80,12 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
if (r >= 0)
break;
- if (pa_pid_file_kill(SIGUSR2, NULL) < 0) {
+ if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
pa_log("failed to kill PulseAudio daemon.");
goto fail;
}
- pa_msleep(50);
+ pa_msleep(300);
}
if (i >= 5) {
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 7c49007c..4cca2f86 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,6 +36,7 @@
#include <sndfile.h>
#include <pulse/pulseaudio.h>
+#include <pulsecore/core-util.h>
#if PA_API_VERSION < 10
#error Invalid PulseAudio API version
@@ -157,6 +156,7 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -178,32 +178,37 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
printf("*** Sink #%u ***\n"
"Name: %s\n"
"Driver: %s\n"
- "Description: %s\n"
"Sample Specification: %s\n"
"Channel Map: %s\n"
"Owner Module: %u\n"
"Volume: %s\n"
- "Monitor Source: %u\n"
- "Latency: %0.0f usec\n"
- "Flags: %s%s%s\n",
+ "Monitor Source: %s\n"
+ "Latency: %0.0f usec, configured %0.0f usec\n"
+ "Flags: %s%s%s%s%s%s\n"
+ "Properties:\n%s",
i->index,
i->name,
- i->driver,
- i->description,
+ pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
- i->monitor_source,
- (double) i->latency,
+ pa_strnull(i->monitor_source_name),
+ (double) i->latency, (double) i->configured_latency,
+ i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+ i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- i->flags & PA_SINK_HARDWARE ? "HARDWARE" : "");
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
}
static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
- char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -222,33 +227,35 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
printf("\n");
nl = 1;
- snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
-
printf("*** Source #%u ***\n"
"Name: %s\n"
"Driver: %s\n"
- "Description: %s\n"
"Sample Specification: %s\n"
"Channel Map: %s\n"
"Owner Module: %u\n"
"Volume: %s\n"
"Monitor of Sink: %s\n"
- "Latency: %0.0f usec\n"
- "Flags: %s%s%s\n",
+ "Latency: %0.0f usec, configured %0.0f usec\n"
+ "Flags: %s%s%s%s%s%s\n"
+ "Properties:\n%s",
i->index,
i->name,
- i->driver,
- i->description,
+ pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
- i->monitor_of_sink != PA_INVALID_INDEX ? t : "no",
- (double) i->latency,
+ i->monitor_of_sink_name ? i->monitor_of_sink_name : "n/a",
+ (double) i->latency, (double) i->configured_latency,
+ i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+ i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+ i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
- i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : "");
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
}
static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
@@ -280,13 +287,14 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
"Auto unload: %s\n",
i->index,
i->name,
- i->argument,
+ i->argument ? i->argument : "",
i->n_used != PA_INVALID_INDEX ? t : "n/a",
- i->auto_unload ? "yes" : "no");
+ pa_yes_no(i->auto_unload));
}
static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
char t[32];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -308,17 +316,20 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
snprintf(t, sizeof(t), "%u", i->owner_module);
printf("*** Client #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
- "Owner Module: %s\n",
+ "Owner Module: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
- i->owner_module != PA_INVALID_INDEX ? t : "n/a");
+ pa_strnull(i->driver),
+ i->owner_module != PA_INVALID_INDEX ? t : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -341,7 +352,6 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
snprintf(k, sizeof(k), "%u", i->client);
printf("*** Sink Input #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
"Owner Module: %s\n"
"Client: %s\n"
@@ -351,10 +361,10 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
"Volume: %s\n"
"Buffer Latency: %0.0f usec\n"
"Sink Latency: %0.0f usec\n"
- "Resample method: %s\n",
+ "Resample method: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
+ pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : "n/a",
i->client != PA_INVALID_INDEX ? k : "n/a",
i->sink,
@@ -363,12 +373,15 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
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");
-}
+ i->resample_method ? i->resample_method : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
+}
static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -392,7 +405,6 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
snprintf(k, sizeof(k), "%u", i->client);
printf("*** Source Output #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
"Owner Module: %s\n"
"Client: %s\n"
@@ -401,10 +413,10 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
"Channel Map: %s\n"
"Buffer Latency: %0.0f usec\n"
"Source Latency: %0.0f usec\n"
- "Resample method: %s\n",
+ "Resample method: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
+ pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : "n/a",
i->client != PA_INVALID_INDEX ? k : "n/a",
i->source,
@@ -412,11 +424,15 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
(double) i->buffer_usec,
(double) i->source_usec,
- i->resample_method ? i->resample_method : "n/a");
+ i->resample_method ? i->resample_method : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -446,7 +462,8 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
"Duration: %0.1fs\n"
"Size: %s\n"
"Lazy: %s\n"
- "Filename: %s\n",
+ "Filename: %s\n"
+ "Properties:\n%s",
i->index,
i->name,
pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
@@ -454,8 +471,11 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a",
(double) i->duration/1000000,
t,
- i->lazy ? "yes" : "no",
- i->filename ? i->filename : "n/a");
+ pa_yes_no(i->lazy),
+ i->filename ? i->filename : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
@@ -485,7 +505,7 @@ static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i,
i->name,
i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
i->module,
- i->argument);
+ i->argument ? i->argument : "");
}
static void simple_callback(pa_context *c, int success, void *userdata) {
@@ -506,7 +526,7 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
}
printf("%u\n", idx);
-
+
complete_action();
}
@@ -633,7 +653,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
else
pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
break;
-
+
default:
assert(0);
}
@@ -662,9 +682,9 @@ static void help(const char *argv0) {
"%s [options] exit\n"
"%s [options] upload-sample FILENAME [NAME]\n"
"%s [options] play-sample NAME [SINK]\n"
+ "%s [options] remove-sample NAME\n"
"%s [options] move-sink-input ID SINK\n"
"%s [options] move-source-output ID SOURCE\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"
@@ -814,34 +834,34 @@ int main(int argc, char *argv[]) {
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;
@@ -849,12 +869,12 @@ int main(int argc, char *argv[]) {
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]);
-
+
+ suspend = pa_parse_boolean(argv[argc-1]);
+
if (argc > optind+2)
sink_name = pa_xstrdup(argv[optind+1]);
-
+
} else if (!strcmp(argv[optind], "suspend-source")) {
action = SUSPEND_SOURCE;
@@ -863,10 +883,14 @@ int main(int argc, char *argv[]) {
goto quit;
}
- suspend = !!atoi(argv[argc-1]);
+ suspend = pa_parse_boolean(argv[argc-1]);
if (argc > optind+2)
source_name = pa_xstrdup(argv[optind+1]);
+ } else if (!strcmp(argv[optind], "help")) {
+ help(bn);
+ ret = 0;
+ goto quit;
}
}
diff --git a/src/utils/padsp b/src/utils/padsp
index c70c3af7..4fe175c2 100755
--- a/src/utils/padsp
+++ b/src/utils/padsp
@@ -1,7 +1,5 @@
#!/bin/sh
-# $Id$
-#
# This file is part of PulseAudio.
#
# Copyright 2006 Lennart Poettering
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index b48af93c..d650707e 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -53,8 +51,8 @@
#endif
#include <pulse/pulseaudio.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/llist.h>
-#include <pulsecore/gccmacro.h>
/* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
#if !defined(SIOCINQ) && defined(FIONREAD)
@@ -302,7 +300,6 @@ static int padsp_disabled(void) {
if (!sym_resolved) {
sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__");
sym_resolved = 1;
-
}
pthread_mutex_unlock(&func_mutex);
@@ -316,7 +313,7 @@ static int dsp_cloak_enable(void) {
if (padsp_disabled() & 1)
return 0;
- if (getenv("PADSP_NO_DSP"))
+ if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -326,7 +323,7 @@ static int sndstat_cloak_enable(void) {
if (padsp_disabled() & 2)
return 0;
- if (getenv("PADSP_NO_SNDSTAT"))
+ if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -336,7 +333,7 @@ static int mixer_cloak_enable(void) {
if (padsp_disabled() & 4)
return 0;
- if (getenv("PADSP_NO_MIXER"))
+ if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -1443,18 +1440,18 @@ fail:
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);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename?filename:"NULL");
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 (filename && 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 (filename && 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 (filename && sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0)
r = sndstat_open(flags, &_errno);
else {
function_exit();
@@ -2307,7 +2304,11 @@ fail:
return ret;
}
+#ifdef sun
+int ioctl(int fd, int request, ...) {
+#else
int ioctl(int fd, unsigned long request, ...) {
+#endif
fd_info *i;
va_list args;
void *argp;
@@ -2371,18 +2372,13 @@ int close(int fd) {
int access(const char *pathname, int mode) {
- if (!pathname) {
- /* Firefox needs this. See #27 */
- errno = EFAULT;
- return -1;
- }
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname);
-
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
+ if (!pathname ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_ACCESS_FUNC();
return _access(pathname, mode);
}
@@ -2406,16 +2402,13 @@ int stat(const char *pathname, struct stat *buf) {
#endif
int ret;
- if (!pathname || !buf) {
- errno = EFAULT;
- return -1;
- }
-
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname);
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname?pathname:"NULL");
LOAD_STAT_FUNC();
return _stat(pathname, buf);
}
@@ -2464,17 +2457,14 @@ int stat64(const char *pathname, struct stat64 *buf) {
struct stat oldbuf;
int ret;
- if (!pathname || !buf) {
- errno = EFAULT;
- return -1;
- }
-
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname?pathname:"NULL");
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_STAT64_FUNC();
return _stat64(pathname, buf);
}
@@ -2504,7 +2494,7 @@ int open64(const char *filename, int flags, ...) {
va_list args;
mode_t mode = 0;
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename?filename:"NULL");
if (flags & O_CREAT) {
va_start(args, flags);
@@ -2515,10 +2505,11 @@ int open64(const char *filename, int flags, ...) {
va_end(args);
}
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ if (!filename ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_OPEN64_FUNC();
return _open64(filename, flags, mode);
}
@@ -2531,17 +2522,14 @@ int open64(const char *filename, int flags, ...) {
#ifdef _STAT_VER
int __xstat(int ver, const char *pathname, struct stat *buf) {
- if (!pathname || !buf) {
- errno = EFAULT;
- return -1;
- }
-
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname);
-
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname?pathname:"NULL");
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_XSTAT_FUNC();
return ___xstat(ver, pathname, buf);
}
@@ -2557,17 +2545,14 @@ int __xstat(int ver, const char *pathname, struct stat *buf) {
#ifdef HAVE_OPEN64
int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
- if (!pathname || !buf) {
- errno = EFAULT;
- return -1;
- }
-
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname);
-
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname?pathname:"NULL");
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_XSTAT64_FUNC();
return ___xstat64(ver, pathname, buf);
}
@@ -2589,12 +2574,14 @@ FILE* fopen(const char *filename, const char *mode) {
int fd;
mode_t m;
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename?filename:"NULL");
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ if (!filename ||
+ !mode ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_FOPEN_FUNC();
return _fopen(filename, mode);
}
@@ -2630,12 +2617,14 @@ FILE* fopen(const char *filename, const char *mode) {
FILE *fopen64(const char *filename, const char *mode) {
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL");
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ if (!filename ||
+ !mode ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_FOPEN64_FUNC();
return _fopen64(filename, mode);
}
diff --git a/src/utils/paplay.c b/src/utils/paplay.c
index e7076d2d..1b6228b1 100644
--- a/src/utils/paplay.c
+++ b/src/utils/paplay.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -203,9 +201,9 @@ static void help(const char *argv0) {
printf("%s [options] [FILE]\n\n"
" -h, --help Show this help\n"
" --version Show version\n\n"
- " -v, --verbose Enable verbose operations\n\n"
+ " -v, --verbose Enable verbose operation\n\n"
" -s, --server=SERVER The name of the server to connect to\n"
- " -d, --device=DEVICE The name of the sink/source to connect to\n"
+ " -d, --device=DEVICE The name of the sink to connect to\n"
" -n, --client-name=NAME How to call this client on the server\n"
" --stream-name=NAME How to call this stream on the server\n"
" --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
index ad86b943..5b4885db 100644
--- a/src/utils/pasuspender.c
+++ b/src/utils/pasuspender.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -81,26 +79,26 @@ static void drain(void) {
}
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;
}
@@ -110,7 +108,7 @@ 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);
@@ -138,7 +136,7 @@ static void resume_complete(pa_context *c, int success, void *userdata) {
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:
@@ -149,11 +147,13 @@ static void context_state_callback(pa_context *c, void *userdata) {
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
+ } else {
+ fprintf(stderr, "WARNING: Sound server is not local, not suspending.\n");
start_child();
-
+ }
+
break;
-
+
case PA_CONTEXT_TERMINATED:
quit(0);
break;
@@ -184,14 +184,14 @@ static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, voi
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)) {
diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c
index 9a50f8ef..eee7b6a8 100644
--- a/src/utils/pax11publish.c
+++ b/src/utils/pax11publish.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.