diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 47 | ||||
-rw-r--r-- | src/modules/coreaudio/module-coreaudio-detect.c | 229 | ||||
-rw-r--r-- | src/modules/coreaudio/module-coreaudio-device.c | 820 | ||||
-rw-r--r-- | src/modules/module-bonjour-publish.c | 516 | ||||
-rw-r--r-- | src/modules/module-sine-source.c | 2 | ||||
-rw-r--r-- | src/modules/raop/module-raop-sink.c | 2 | ||||
-rw-r--r-- | src/modules/raop/raop_client.c | 5 | ||||
-rw-r--r-- | src/modules/rtp/rtsp_client.c | 5 | ||||
-rw-r--r-- | src/pulse/mainloop.c | 9 | ||||
-rw-r--r-- | src/pulsecore/core-rtclock.c | 69 | ||||
-rw-r--r-- | src/pulsecore/lock-autospawn.c | 3 | ||||
-rw-r--r-- | src/pulsecore/poll.c | 33 | ||||
-rw-r--r-- | src/pulsecore/poll.h | 18 | ||||
-rw-r--r-- | src/pulsecore/rtpoll.c | 9 | ||||
-rw-r--r-- | src/tests/lock-autospawn-test.c | 4 | ||||
-rw-r--r-- | src/tests/rtpoll-test.c | 2 | ||||
-rw-r--r-- | src/utils/pacmd.c | 4 |
17 files changed, 1694 insertions, 83 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 11826a42..0224cb3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -640,12 +640,21 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \ pulsecore/mutex-win32.c pulsecore/mutex.h \ pulsecore/thread-win32.c pulsecore/thread.h \ pulsecore/semaphore-win32.c pulsecore/semaphore.h -else +else !OS_IS_WIN32 +if OS_IS_DARWIN +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \ + pulsecore/mutex-posix.c pulsecore/mutex.h \ + pulsecore/thread-posix.c pulsecore/thread.h \ + pulsecore/semaphore-osx.c pulsecore/semaphore.h +libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += "-I/Developer/Headers/FlatCarbon/" +#libpulsecommon_@PA_MAJORMINORMICRO@_la_LDFLAGS += "-framework CoreServices" +else !OS_IS_DARWIN libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \ pulsecore/mutex-posix.c pulsecore/mutex.h \ pulsecore/thread-posix.c pulsecore/thread.h \ pulsecore/semaphore-posix.c pulsecore/semaphore.h -endif +endif !OS_IS_DARWIN +endif !OS_IS_WIN32 if HAVE_X11 libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11prop.c pulsecore/x11prop.h @@ -1057,6 +1066,12 @@ modlibexec_LTLIBRARIES += \ module-oss.la endif +if HAVE_COREAUDIO +modlibexec_LTLIBRARIES += \ + module-coreaudio-detect.la \ + module-coreaudio-device.la +endif + pulselibexec_PROGRAMS = if HAVE_ALSA @@ -1108,6 +1123,11 @@ modlibexec_LTLIBRARIES += \ module-zeroconf-discover.la endif +if HAVE_BONJOUR +modlibexec_LTLIBRARIES += \ + module-bonjour-publish.la +endif + if HAVE_LIRC modlibexec_LTLIBRARIES += \ module-lirc.la @@ -1216,6 +1236,7 @@ SYMDEF_FILES = \ modules/module-esound-sink-symdef.h \ modules/module-zeroconf-publish-symdef.h \ modules/module-zeroconf-discover-symdef.h \ + modules/module-bonjour-publish-symdef.h \ modules/module-lirc-symdef.h \ modules/module-mmkbd-evdev-symdef.h \ modules/module-http-protocol-tcp-symdef.h \ @@ -1229,6 +1250,8 @@ SYMDEF_FILES = \ modules/alsa/module-alsa-sink-symdef.h \ modules/alsa/module-alsa-source-symdef.h \ modules/alsa/module-alsa-card-symdef.h \ + modules/coreaudio/module-coreaudio-detect-symdef.h \ + modules/coreaudio/module-coreaudio-device-symdef.h \ modules/module-solaris-symdef.h \ modules/module-waveout-symdef.h \ modules/module-detect-symdef.h \ @@ -1460,6 +1483,19 @@ module_oss_la_SOURCES = modules/oss/module-oss.c module_oss_la_LDFLAGS = $(MODULE_LDFLAGS) module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +# COREAUDIO + +module_coreaudio_detect_la_SOURCES = modules/coreaudio/module-coreaudio-detect.c +module_coreaudio_detect_la_LDFLAGS = $(MODULE_LDFLAGS) \ + -Wl,-framework -Wl,Cocoa -framework CoreAudio \ + -Wl,-framework -Wl,AudioUnit -framework AudioUnit +module_coreaudio_detect_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + +module_coreaudio_device_la_SOURCES = modules/coreaudio/module-coreaudio-device.c +module_coreaudio_device_la_LDFLAGS = $(MODULE_LDFLAGS) \ + -Wl,-framework -Wl,Cocoa -framework CoreAudio \ + -Wl,-framework -Wl,AudioUnit -framework AudioUnit +module_coreaudio_device_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la # ALSA libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h @@ -1518,6 +1554,13 @@ module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS) module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) +# Bonjour + +module_bonjour_publish_la_SOURCES = modules/module-bonjour-publish.c +module_bonjour_publish_la_LDFLAGS = $(MODULE_LDFLAGS) \ + -Wl,-framework -Wl,CoreFoundation -framework CoreFoundation +module_bonjour_publish_la_LIBADD = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + # LIRC module_lirc_la_SOURCES = modules/module-lirc.c diff --git a/src/modules/coreaudio/module-coreaudio-detect.c b/src/modules/coreaudio/module-coreaudio-detect.c new file mode 100644 index 00000000..872678e7 --- /dev/null +++ b/src/modules/coreaudio/module-coreaudio-detect.c @@ -0,0 +1,229 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Daniel Mack <daniel@caiaq.de> + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/llist.h> + +#include <CoreAudio/CoreAudio.h> + +#include "module-coreaudio-detect-symdef.h" + +#define DEVICE_MODULE_NAME "module-coreaudio-device" + +PA_MODULE_AUTHOR("Daniel Mack"); +PA_MODULE_DESCRIPTION("CoreAudio device detection"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE(""); + +typedef struct ca_device ca_device; + +struct ca_device { + AudioDeviceID id; + unsigned int module_index; + PA_LLIST_FIELDS(ca_device); +}; + +struct userdata { + int detect_fds[2]; + pa_io_event *detect_io; + + PA_LLIST_HEAD(ca_device, devices); +}; + +static int ca_device_added(struct pa_module *m, AudioDeviceID id) { + pa_module *mod; + struct userdata *u = m->userdata; + struct ca_device *dev; + char *args; + + pa_assert(u); + + args = pa_sprintf_malloc("device_id=%d", (int) id); + pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME, args); + mod = pa_module_load(m->core, DEVICE_MODULE_NAME, args); + pa_xfree(args); + + if (!mod) { + pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME, args); + return -1; + } + + dev = pa_xnew0(ca_device, 1); + dev->module_index = mod->index; + dev->id = id; + + PA_LLIST_INIT(ca_device, dev); + PA_LLIST_PREPEND(ca_device, u->devices, dev); + + return 0; +} + +static int ca_update_device_list(struct pa_module *m) { + OSStatus err; + UInt32 i, size, num_devices; + Boolean writable; + AudioDeviceID *device_id; + struct ca_device *dev; + struct userdata *u = m->userdata; + + pa_assert(u); + + /* get the number of currently available audio devices */ + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &writable); + if (err) { + pa_log("Unable to get info for kAudioHardwarePropertyDevices."); + return -1; + } + + num_devices = size / sizeof(AudioDeviceID); + device_id = pa_xnew(AudioDeviceID, num_devices); + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, device_id); + if (err) { + pa_log("Unable to get kAudioHardwarePropertyDevices."); + pa_xfree(device_id); + return -1; + } + + /* scan for devices which are reported but not in our cached list */ + for (i = 0; i < num_devices; i++) { + bool found = FALSE; + + PA_LLIST_FOREACH(dev, u->devices) + if (dev->id == device_id[i]) { + found = TRUE; + break; + } + + if (!found) + ca_device_added(m, device_id[i]); + } + + /* scan for devices which are in our cached list but are not reported */ +scan_removed: + + PA_LLIST_FOREACH(dev, u->devices) { + bool found = FALSE; + + for (i = 0; i < num_devices; i++) + if (dev->id == device_id[i]) { + found = TRUE; + break; + } + + if (!found) { + pa_log_debug("device id %d has been removed (module index %d) %p", (unsigned int) dev->id, dev->module_index, dev); + pa_module_unload_request_by_index(m->core, dev->module_index, TRUE); + PA_LLIST_REMOVE(ca_device, u->devices, dev); + pa_xfree(dev); + /* the current list item pointer is not valid anymore, so start over. */ + goto scan_removed; + } + } + + pa_xfree(device_id); + return 0; +} + +static OSStatus property_listener_proc(AudioHardwarePropertyID property, void *data) { + struct userdata *u = data; + char dummy = 1; + + pa_assert(u); + + /* dispatch module load/unload operations in main thread */ + if (property == kAudioHardwarePropertyDevices) + write(u->detect_fds[1], &dummy, 1); + + return 0; +} + +static void detect_handle(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { + pa_module *m = userdata; + char dummy; + + pa_assert(m); + + read(fd, &dummy, 1); + ca_update_device_list(m); +} + +int pa__init(pa_module *m) { + struct userdata *u = pa_xnew0(struct userdata, 1); + + m->userdata = u; + + if (AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, property_listener_proc, u)) { + pa_log("AudioHardwareAddPropertyListener() failed."); + goto fail; + } + + if (ca_update_device_list(m)) + goto fail; + + pa_assert_se(pipe(u->detect_fds) == 0); + pa_assert_se(u->detect_io = m->core->mainloop->io_new(m->core->mainloop, u->detect_fds[0], PA_IO_EVENT_INPUT, detect_handle, m)); + + return 0; + +fail: + pa_xfree(u); + return -1; +} + +void pa__done(pa_module *m) { + struct userdata *u = m->userdata; + struct ca_device *dev = u->devices; + + pa_assert(u); + + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, property_listener_proc); + + while (dev) { + struct ca_device *next = dev->next; + + pa_module_unload_request_by_index(m->core, dev->module_index, TRUE); + pa_xfree(dev); + + dev = next; + } + + if (u->detect_fds[0] >= 0) + close(u->detect_fds[0]); + + if (u->detect_fds[1] >= 0) + close(u->detect_fds[1]); + + if (u->detect_io) + m->core->mainloop->io_free(u->detect_io); + + pa_xfree(u); +} diff --git a/src/modules/coreaudio/module-coreaudio-device.c b/src/modules/coreaudio/module-coreaudio-device.c new file mode 100644 index 00000000..a3699e68 --- /dev/null +++ b/src/modules/coreaudio/module-coreaudio-device.c @@ -0,0 +1,820 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Daniel Mack <daniel@caiaq.de> + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* TODO: + - implement hardware volume controls + - handle audio device stream format changes (will require changes to the core) +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/module.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/llist.h> +#include <pulsecore/card.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> + +#include <CoreAudio/CoreAudio.h> +#include <CoreAudio/CoreAudioTypes.h> +#include <CoreAudio/AudioHardware.h> + +#include "module-coreaudio-device-symdef.h" + +#define DEFAULT_FRAMES_PER_IOPROC 512 + +PA_MODULE_AUTHOR("Daniel Mack"); +PA_MODULE_DESCRIPTION("CoreAudio device"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE("device_id=<the CoreAudio device id> " + "ioproc_frames=<audio frames per IOProc call> "); + +static const char* const valid_modargs[] = { + "device_id", + "ioproc_frames", + NULL +}; + +enum { + CA_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX, +}; + +typedef struct coreaudio_sink coreaudio_sink; +typedef struct coreaudio_source coreaudio_source; + +struct userdata { + AudioDeviceID device_id; + AudioDeviceIOProcID proc_id; + + pa_thread_mq thread_mq; + pa_asyncmsgq *async_msgq; + + pa_rtpoll *rtpoll; + pa_thread *thread; + + pa_module *module; + pa_card *card; + pa_bool_t running; + + char *device_name, *vendor_name; + + const AudioBufferList *render_input_data; + AudioBufferList *render_output_data; + + AudioStreamBasicDescription stream_description; + + PA_LLIST_HEAD(coreaudio_sink, sinks); + PA_LLIST_HEAD(coreaudio_source, sources); +}; + +struct coreaudio_sink { + pa_sink *pa_sink; + struct userdata *userdata; + + char *name; + unsigned int channel_idx; + pa_bool_t active; + + pa_channel_map map; + pa_sample_spec ss; + + PA_LLIST_FIELDS(coreaudio_sink); +}; + +struct coreaudio_source { + pa_source *pa_source; + struct userdata *userdata; + + char *name; + unsigned int channel_idx; + pa_bool_t active; + + pa_channel_map map; + pa_sample_spec ss; + + PA_LLIST_FIELDS(coreaudio_source); +}; + +static OSStatus io_render_proc (AudioDeviceID device, + const AudioTimeStamp *now, + const AudioBufferList *inputData, + const AudioTimeStamp *inputTime, + AudioBufferList *outputData, + const AudioTimeStamp *outputTime, + void *clientData) +{ + struct userdata *u = clientData; + + pa_assert(u); + pa_assert(device == u->device_id); + + u->render_input_data = inputData; + u->render_output_data = outputData; + + if (u->sinks) + pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sinks->pa_sink), + CA_MESSAGE_RENDER, NULL, 0, NULL) == 0); + + if (u->sources) + pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sources->pa_source), + CA_MESSAGE_RENDER, NULL, 0, NULL) == 0); + + return 0; +} + +static OSStatus ca_stream_format_changed(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void *inClientData) +{ + struct userdata *u = inClientData; + + pa_assert(u); + + /* REVISIT: PA can't currently handle external format change requests. + * Hence, we set the original format back in this callback to avoid horrible audio artefacts. + * The device settings will appear to be 'locked' for any application as long as the PA daemon is running. + * Once we're able to propagate such events up in the core, this needs to be changed. */ + + return AudioDeviceSetProperty(inDevice, NULL, inChannel, isInput, + kAudioDevicePropertyStreamFormat, sizeof(u->stream_description), &u->stream_description); +} + +static pa_usec_t get_latency_us(pa_object *o) { + struct userdata *u; + pa_sample_spec *ss; + bool is_source; + UInt32 v, total = 0; + UInt32 err, size = sizeof(v); + + if (pa_sink_isinstance(o)) { + coreaudio_sink *sink = PA_SINK(o)->userdata; + + u = sink->userdata; + ss = &sink->ss; + is_source = FALSE; + } else if (pa_source_isinstance(o)) { + coreaudio_source *source = PA_SOURCE(o)->userdata; + + u = source->userdata; + ss = &source->ss; + is_source = TRUE; + } else + pa_assert_not_reached(); + + pa_assert(u); + + /* get the device latency */ + size = sizeof(total); + AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertyLatency, &size, &v); + total += v; + + /* the the IOProc buffer size */ + size = sizeof(v); + AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertyBufferFrameSize, &size, &v); + total += v; + + /* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */ + size = sizeof(v); + AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertySafetyOffset, &size, &v); + total += v / 2; + + /* get the stream latency. + * FIXME: this assumes the stream latency is the same for all streams */ + err = AudioStreamGetProperty(0, is_source, kAudioStreamPropertyLatency, &size, &v); + if (!err) + total += v; + + return pa_bytes_to_usec(total * pa_frame_size(ss), ss); +} + +static void ca_device_check_device_state(struct userdata *u) { + coreaudio_sink *ca_sink; + coreaudio_source *ca_source; + pa_bool_t active = FALSE; + + pa_assert(u); + + for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) + if (ca_sink->active) + active = TRUE; + + for (ca_source = u->sources; ca_source; ca_source = ca_source->next) + if (ca_source->active) + active = TRUE; + + if (active && !u->running) + AudioDeviceStart(u->device_id, u->proc_id); + else if (!active && u->running) + AudioDeviceStop(u->device_id, u->proc_id); + + u->running = active; +} + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + coreaudio_sink *sink = PA_SINK(o)->userdata; + struct userdata *u = sink->userdata; + unsigned int i; + pa_memchunk audio_chunk; + + switch (code) { + case CA_MESSAGE_RENDER: { + /* audio out */ + for (i = 0; i < u->render_output_data->mNumberBuffers; i++) { + AudioBuffer *buf = u->render_output_data->mBuffers + i; + + pa_assert(sink); + + if (PA_SINK_IS_OPENED(sink->pa_sink->thread_info.state)) { + if (sink->pa_sink->thread_info.rewind_requested) + pa_sink_process_rewind(sink->pa_sink, 0); + + audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, FALSE); + audio_chunk.length = buf->mDataByteSize; + audio_chunk.index = 0; + + pa_sink_render_into_full(sink->pa_sink, &audio_chunk); + pa_memblock_unref_fixed(audio_chunk.memblock); + } + + sink = sink->next; + } + + return 0; + } + + case PA_SINK_MESSAGE_GET_LATENCY: { + *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o)); + return 0; + } + + case PA_SINK_MESSAGE_SET_STATE: + switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + case PA_SINK_SUSPENDED: + case PA_SINK_IDLE: + sink->active = FALSE; + break; + + case PA_SINK_RUNNING: + sink->active = TRUE; + break; + + case PA_SINK_UNLINKED: + case PA_SINK_INIT: + case PA_SINK_INVALID_STATE: + ; + } + + ca_device_check_device_state(sink->userdata); + break; + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + coreaudio_source *source = PA_SOURCE(o)->userdata; + struct userdata *u = source->userdata; + unsigned int i; + pa_memchunk audio_chunk; + + switch (code) { + case CA_MESSAGE_RENDER: { + /* audio in */ + for (i = 0; i < u->render_input_data->mNumberBuffers; i++) { + const AudioBuffer *buf = u->render_input_data->mBuffers + i; + + pa_assert(source); + + if (PA_SOURCE_IS_OPENED(source->pa_source->thread_info.state)) { + audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, TRUE); + audio_chunk.length = buf->mDataByteSize; + audio_chunk.index = 0; + + pa_source_post(source->pa_source, &audio_chunk); + pa_memblock_unref_fixed(audio_chunk.memblock); + } + + source = source->next; + } + + return 0; + } + + case PA_SOURCE_MESSAGE_GET_LATENCY: { + *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o)); + return 0; + } + + case PA_SOURCE_MESSAGE_SET_STATE: + switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { + case PA_SOURCE_SUSPENDED: + case PA_SOURCE_IDLE: + source->active = FALSE; + break; + + case PA_SOURCE_RUNNING: + source->active = TRUE; + break; + + case PA_SOURCE_UNLINKED: + case PA_SOURCE_INIT: + case PA_SOURCE_INVALID_STATE: + ; + } + + ca_device_check_device_state(source->userdata); + break; + } + + return pa_source_process_msg(o, code, data, offset, chunk); +} + +static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx) { + OSStatus err; + UInt32 size; + struct userdata *u = m->userdata; + pa_sink_new_data new_data; + pa_sink_flags_t flags = PA_SINK_LATENCY | PA_SINK_HARDWARE; + coreaudio_sink *ca_sink; + pa_sink *sink; + unsigned int i; + char tmp[255]; + pa_strbuf *strbuf; + + ca_sink = pa_xnew0(coreaudio_sink, 1); + ca_sink->map.channels = buf->mNumberChannels; + ca_sink->ss.channels = buf->mNumberChannels; + ca_sink->channel_idx = channel_idx; + + /* build a name for this stream */ + strbuf = pa_strbuf_new(); + + for (i = 0; i < buf->mNumberChannels; i++) { + size = sizeof(tmp); + err = AudioDeviceGetProperty(u->device_id, channel_idx + i + 1, 0, kAudioObjectPropertyElementName, &size, tmp); + if (err || !strlen(tmp)) + snprintf(tmp, sizeof(tmp), "Channel %d", channel_idx + i + 1); + + if (i > 0) + pa_strbuf_puts(strbuf, ", "); + + pa_strbuf_puts(strbuf, tmp); + } + + ca_sink->name = pa_strbuf_tostring_free(strbuf); + + pa_log_debug("Stream name is >%s<", ca_sink->name); + + /* default to mono streams */ + for (i = 0; i < ca_sink->map.channels; i++) + ca_sink->map.map[i] = PA_CHANNEL_POSITION_MONO; + + if (buf->mNumberChannels == 2) { + ca_sink->map.map[0] = PA_CHANNEL_POSITION_LEFT; + ca_sink->map.map[1] = PA_CHANNEL_POSITION_RIGHT; + } + + ca_sink->ss.rate = u->stream_description.mSampleRate; + ca_sink->ss.format = PA_SAMPLE_FLOAT32LE; + + pa_sink_new_data_init(&new_data); + new_data.card = u->card; + new_data.driver = __FILE__; + new_data.module = u->module; + new_data.namereg_fail = FALSE; + pa_sink_new_data_set_name(&new_data, ca_sink->name); + pa_sink_new_data_set_channel_map(&new_data, &ca_sink->map); + pa_sink_new_data_set_sample_spec(&new_data, &ca_sink->ss); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap"); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio"); + pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize); + + if (u->vendor_name) + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name); + + sink = pa_sink_new(m->core, &new_data, flags); + pa_sink_new_data_done(&new_data); + + if (!sink) { + pa_log("unable to create sink."); + return -1; + } + + sink->parent.process_msg = sink_process_msg; + sink->userdata = ca_sink; + + pa_sink_set_asyncmsgq(sink, u->thread_mq.inq); + pa_sink_set_rtpoll(sink, u->rtpoll); + + ca_sink->pa_sink = sink; + ca_sink->userdata = u; + + PA_LLIST_PREPEND(coreaudio_sink, u->sinks, ca_sink); + + return 0; +} + +static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_idx) { + OSStatus err; + UInt32 size; + struct userdata *u = m->userdata; + pa_source_new_data new_data; + pa_source_flags_t flags = PA_SOURCE_LATENCY | PA_SOURCE_HARDWARE; + coreaudio_source *ca_source; + pa_source *source; + unsigned int i; + char tmp[255]; + pa_strbuf *strbuf; + + ca_source = pa_xnew0(coreaudio_source, 1); + ca_source->map.channels = buf->mNumberChannels; + ca_source->ss.channels = buf->mNumberChannels; + ca_source->channel_idx = channel_idx; + + /* build a name for this stream */ + strbuf = pa_strbuf_new(); + + for (i = 0; i < buf->mNumberChannels; i++) { + size = sizeof(tmp); + err = AudioDeviceGetProperty(u->device_id, channel_idx + i + 1, 0, kAudioObjectPropertyElementName, &size, tmp); + if (err || !strlen(tmp)) + snprintf(tmp, sizeof(tmp), "Channel %d", channel_idx + i + 1); + + if (i > 0) + pa_strbuf_puts(strbuf, ", "); + + pa_strbuf_puts(strbuf, tmp); + } + + ca_source->name = pa_strbuf_tostring_free(strbuf); + + pa_log_debug("Stream name is >%s<", ca_source->name); + + /* default to mono streams */ + for (i = 0; i < ca_source->map.channels; i++) + ca_source->map.map[i] = PA_CHANNEL_POSITION_MONO; + + if (buf->mNumberChannels == 2) { + ca_source->map.map[0] = PA_CHANNEL_POSITION_LEFT; + ca_source->map.map[1] = PA_CHANNEL_POSITION_RIGHT; + } + + ca_source->ss.rate = u->stream_description.mSampleRate; + ca_source->ss.format = PA_SAMPLE_FLOAT32LE; + + pa_source_new_data_init(&new_data); + new_data.card = u->card; + new_data.driver = __FILE__; + new_data.module = u->module; + new_data.namereg_fail = FALSE; + pa_source_new_data_set_name(&new_data, ca_source->name); + pa_source_new_data_set_channel_map(&new_data, &ca_source->map); + pa_source_new_data_set_sample_spec(&new_data, &ca_source->ss); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap"); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio"); + pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize); + + if (u->vendor_name) + pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name); + + source = pa_source_new(m->core, &new_data, flags); + pa_source_new_data_done(&new_data); + + if (!source) { + pa_log("unable to create source."); + return -1; + } + + source->parent.process_msg = source_process_msg; + source->userdata = ca_source; + + pa_source_set_asyncmsgq(source, u->thread_mq.inq); + pa_source_set_rtpoll(source, u->rtpoll); + + ca_source->pa_source = source; + ca_source->userdata = u; + + PA_LLIST_PREPEND(coreaudio_source, u->sources, ca_source); + + return 0; +} + +static int ca_device_create_streams(pa_module *m, bool direction_in) { + OSStatus err; + UInt32 size, i, channel_idx; + struct userdata *u = m->userdata; + int section = direction_in ? 1 : 0; + AudioBufferList *buffer_list; + + /* get current stream format */ + size = sizeof(AudioStreamBasicDescription); + err = AudioDeviceGetProperty(u->device_id, 0, section, kAudioDevicePropertyStreamFormat, &size, &u->stream_description); + if (err) { + /* no appropriate streams found - silently bail. */ + return -1; + } + + if (u->stream_description.mFormatID != kAudioFormatLinearPCM) { + pa_log("Unsupported audio format '%c%c%c%c'", + (char) (u->stream_description.mFormatID >> 24), + (char) (u->stream_description.mFormatID >> 16) & 0xff, + (char) (u->stream_description.mFormatID >> 8) & 0xff, + (char) (u->stream_description.mFormatID & 0xff)); + return -1; + } + + /* get stream configuration */ + size = 0; + err = AudioDeviceGetPropertyInfo(u->device_id, 0, section, kAudioDevicePropertyStreamConfiguration, &size, NULL); + if (err) { + pa_log("Failed to get kAudioDevicePropertyStreamConfiguration (%s).", direction_in ? "input" : "output"); + return -1; + } + + if (!size) + return 0; + + buffer_list = (AudioBufferList *) pa_xmalloc(size); + err = AudioDeviceGetProperty(u->device_id, 0, section, kAudioDevicePropertyStreamConfiguration, &size, buffer_list); + + if (!err) { + pa_log_debug("Sample rate: %f", u->stream_description.mSampleRate); + pa_log_debug("%d bytes per packet", (unsigned int) u->stream_description.mBytesPerPacket); + pa_log_debug("%d frames per packet", (unsigned int) u->stream_description.mFramesPerPacket); + pa_log_debug("%d bytes per frame", (unsigned int) u->stream_description.mBytesPerFrame); + pa_log_debug("%d channels per frame", (unsigned int) u->stream_description.mChannelsPerFrame); + pa_log_debug("%d bits per channel", (unsigned int) u->stream_description.mBitsPerChannel); + + for (channel_idx = 0, i = 0; i < buffer_list->mNumberBuffers; i++) { + AudioBuffer *buf = buffer_list->mBuffers + i; + + if (direction_in) + ca_device_create_source(m, buf, channel_idx); + else + ca_device_create_sink(m, buf, channel_idx); + + channel_idx += buf->mNumberChannels; + } + } + + pa_xfree(buffer_list); + return 0; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + pa_assert(u->module); + pa_assert(u->module->core); + + pa_log_debug("Thread starting up"); + + if (u->module->core->realtime_scheduling) + pa_make_realtime(u->module->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + + for (;;) { + int ret; + + ret = pa_rtpoll_run(u->rtpoll, TRUE); + + if (ret < 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->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module *m) { + OSStatus err; + UInt32 size, frames; + struct userdata *u = NULL; + pa_modargs *ma = NULL; + char tmp[64]; + pa_card_new_data card_new_data; + coreaudio_sink *ca_sink; + coreaudio_source *ca_source; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + u = pa_xnew0(struct userdata, 1); + u->module = m; + m->userdata = u; + + if (pa_modargs_get_value_u32(ma, "device_id", (unsigned int *) &u->device_id) != 0) { + pa_log("Failed to parse device_id argument."); + goto fail; + } + + /* get device product name */ + size = sizeof(tmp); + err = AudioDeviceGetProperty(u->device_id, 0, 0, kAudioDevicePropertyDeviceName, &size, &tmp); + if (err) { + pa_log("Failed to get kAudioDevicePropertyDeviceName (err = %08x).", (int) err); + goto fail; + } + + u->device_name = pa_xstrdup(tmp); + + pa_card_new_data_init(&card_new_data); + pa_proplist_sets(card_new_data.proplist, PA_PROP_DEVICE_STRING, tmp); + card_new_data.driver = __FILE__; + pa_card_new_data_set_name(&card_new_data, tmp); + pa_log_info("Initializing module for CoreAudio device '%s' (id %d)", tmp, (unsigned int) u->device_id); + + /* get device vendor name (may fail) */ + size = sizeof(tmp); + err = AudioDeviceGetProperty(u->device_id, 0, 0, kAudioDevicePropertyDeviceManufacturer, &size, &tmp); + if (!err) + u->vendor_name = pa_xstrdup(tmp); + + /* create the card object */ + u->card = pa_card_new(m->core, &card_new_data); + if (!u->card) { + pa_log("Unable to create card.\n"); + goto fail; + } + + pa_card_new_data_done(&card_new_data); + u->card->userdata = u; + + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + u->async_msgq = pa_asyncmsgq_new(0); + pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->async_msgq); + + PA_LLIST_HEAD_INIT(coreaudio_sink, u->sinks); + + /* create sinks */ + ca_device_create_streams(m, FALSE); + + /* create sources */ + ca_device_create_streams(m, TRUE); + + /* create the message thread */ + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + + /* register notification callback for stream format changes */ + AudioDeviceAddPropertyListener(u->device_id, 0, 0, kAudioDevicePropertyStreamFormat, ca_stream_format_changed, u); + + /* set number of frames in IOProc */ + frames = DEFAULT_FRAMES_PER_IOPROC; + pa_modargs_get_value_u32(ma, "ioproc_frames", (unsigned int *) &frames); + + AudioDeviceSetProperty(u->device_id, NULL, 0, 0, kAudioDevicePropertyBufferFrameSize, sizeof(frames), &frames); + pa_log_debug("%u frames per IOProc\n", (unsigned int) frames); + + /* create one ioproc for both directions */ + err = AudioDeviceCreateIOProcID(u->device_id, io_render_proc, u, &u->proc_id); + if (err) { + pa_log("AudioDeviceCreateIOProcID() failed (err = %08x\n).", (int) err); + goto fail; + } + + for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) + pa_sink_put(ca_sink->pa_sink); + + for (ca_source = u->sources; ca_source; ca_source = ca_source->next) + pa_source_put(ca_source->pa_source); + + pa_modargs_free(ma); + + return 0; + +fail: + if (u) + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module *m) { + struct userdata *u; + coreaudio_sink *ca_sink; + coreaudio_source *ca_source; + + pa_assert(m); + + u = m->userdata; + pa_assert(u); + + /* unlink sinks */ + for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) + if (ca_sink->pa_sink) + pa_sink_unlink(ca_sink->pa_sink); + + /* unlink sources */ + for (ca_source = u->sources; ca_source; ca_source = ca_source->next) + if (ca_source->pa_source) + pa_source_unlink(ca_source->pa_source); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + pa_thread_mq_done(&u->thread_mq); + pa_asyncmsgq_unref(u->async_msgq); + } + + /* free sinks */ + for (ca_sink = u->sinks; ca_sink;) { + coreaudio_sink *next = ca_sink->next; + + if (ca_sink->pa_sink) + pa_sink_unref(ca_sink->pa_sink); + + pa_xfree(ca_sink->name); + pa_xfree(ca_sink); + ca_sink = next; + } + + /* free sources */ + for (ca_source = u->sources; ca_source;) { + coreaudio_source *next = ca_source->next; + + if (ca_source->pa_source) + pa_source_unref(ca_source->pa_source); + + pa_xfree(ca_source->name); + pa_xfree(ca_source); + ca_source = next; + } + + if (u->proc_id) { + AudioDeviceStop(u->device_id, u->proc_id); + AudioDeviceDestroyIOProcID(u->device_id, u->proc_id); + } + + AudioDeviceRemovePropertyListener(u->device_id, 0, 0, kAudioDevicePropertyStreamFormat, ca_stream_format_changed); + + pa_xfree(u->device_name); + pa_xfree(u->vendor_name); + pa_rtpoll_free(u->rtpoll); + pa_card_free(u->card); + + pa_xfree(u); +} diff --git a/src/modules/module-bonjour-publish.c b/src/modules/module-bonjour-publish.c new file mode 100644 index 00000000..81b59763 --- /dev/null +++ b/src/modules/module-bonjour-publish.c @@ -0,0 +1,516 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Daniel Mack + based on module-zeroconf-publish.c + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dns_sd.h> + +#include <CoreFoundation/CoreFoundation.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/parseaddr.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/dynarray.h> +#include <pulsecore/modargs.h> +#include <pulsecore/avahi-wrap.h> +#include <pulsecore/endianmacros.h> +#include <pulsecore/protocol-native.h> + +#include "module-bonjour-publish-symdef.h" + +PA_MODULE_AUTHOR("Daniel Mack"); +PA_MODULE_DESCRIPTION("Mac OS X Bonjour Service Publisher"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +#define SERVICE_TYPE_SINK "_pulse-sink._tcp" +#define SERVICE_TYPE_SOURCE "_pulse-source._tcp" +#define SERVICE_TYPE_SERVER "_pulse-server._tcp" + +static const char* const valid_modargs[] = { + NULL +}; + +enum service_subtype { + SUBTYPE_HARDWARE, + SUBTYPE_VIRTUAL, + SUBTYPE_MONITOR +}; + +struct service { + struct userdata *userdata; + DNSServiceRef service; + DNSRecordRef rec, rec2; + char *service_name; + pa_object *device; + enum service_subtype subtype; +}; + +struct userdata { + pa_core *core; + pa_module *module; + + pa_hashmap *services; + char *service_name; + + pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot; + + pa_native_protocol *native; + DNSServiceRef main_service; +}; + +static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) { + pa_assert(s); + pa_assert(ret_ss); + pa_assert(ret_proplist); + pa_assert(ret_subtype); + + if (pa_sink_isinstance(s->device)) { + pa_sink *sink = PA_SINK(s->device); + + *ret_ss = sink->sample_spec; + *ret_map = sink->channel_map; + *ret_name = sink->name; + *ret_proplist = sink->proplist; + *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); + + *ret_ss = source->sample_spec; + *ret_map = source->channel_map; + *ret_name = source->name; + *ret_proplist = source->proplist; + *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL); + + } else + pa_assert_not_reached(); +} + +static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) { + char s[128]; + char *t; + + pa_assert(c); + + TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION); + + t = pa_get_user_name_malloc(); + TXTRecordSetValue(txt, "user-name", strlen(t), t); + pa_xfree(t); + + t = pa_machine_id(); + TXTRecordSetValue(txt, "machine-id", strlen(t), t); + pa_xfree(t); + + t = pa_uname_string(); + TXTRecordSetValue(txt, "uname", strlen(t), t); + pa_xfree(t); + + t = pa_get_fqdn(s, sizeof(s)); + TXTRecordSetValue(txt, "fqdn", strlen(t), t); + + snprintf(s, sizeof(s), "0x%08x", c->cookie); + TXTRecordSetValue(txt, "cookie", strlen(s), s); +} + +static void service_free(struct service *s); + +static void dns_service_register_reply(DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context) { + struct service *s = context; + + pa_assert(s); + + switch (errorCode) { + case kDNSServiceErr_NameConflict: + pa_log("DNS service reported kDNSServiceErr_NameConflict\n"); + service_free(s); + break; + + case kDNSServiceErr_NoError: + default: + break; + } +} + +static uint16_t compute_port(struct userdata *u) { + pa_strlist *i; + + pa_assert(u); + + for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) { + pa_parsed_address a; + + if (pa_parse_address(pa_strlist_data(i), &a) >= 0 && + (a.type == PA_PARSED_ADDRESS_TCP4 || + a.type == PA_PARSED_ADDRESS_TCP6 || + a.type == PA_PARSED_ADDRESS_TCP_AUTO) && + a.port > 0) { + + pa_xfree(a.path_or_host); + return a.port; + } + + pa_xfree(a.path_or_host); + } + + return PA_NATIVE_DEFAULT_PORT; +} + +static int publish_service(struct service *s) { + int r = -1; + TXTRecordRef txt; + DNSServiceErrorType err; + const char *name = NULL, *t; + pa_proplist *proplist = NULL; + pa_sample_spec ss; + pa_channel_map map; + char cm[PA_CHANNEL_MAP_SNPRINT_MAX], tmp[64]; + enum service_subtype subtype; + + const char * const subtype_text[] = { + [SUBTYPE_HARDWARE] = "hardware", + [SUBTYPE_VIRTUAL] = "virtual", + [SUBTYPE_MONITOR] = "monitor" + }; + + pa_assert(s); + + if (s->service) { + DNSServiceRefDeallocate(s->service); + s->service = NULL; + } + + TXTRecordCreate(&txt, 0, NULL); + + txt_record_server_data(s->userdata->core, &txt); + + get_service_data(s, &ss, &map, &name, &proplist, &subtype); + TXTRecordSetValue(&txt, "device", strlen(name), name); + + snprintf(tmp, sizeof(tmp), "%u", ss.rate); + TXTRecordSetValue(&txt, "rate", strlen(tmp), tmp); + + snprintf(tmp, sizeof(tmp), "%u", ss.channels); + TXTRecordSetValue(&txt, "channels", strlen(tmp), tmp); + + t = pa_sample_format_to_string(ss.format); + TXTRecordSetValue(&txt, "format", strlen(t), t); + + t = pa_channel_map_snprint(cm, sizeof(cm), &map); + TXTRecordSetValue(&txt, "channel_map", strlen(t), t); + + t = subtype_text[subtype]; + TXTRecordSetValue(&txt, "subtype", strlen(t), t); + + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION))) + TXTRecordSetValue(&txt, "description", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME))) + TXTRecordSetValue(&txt, "icon-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME))) + TXTRecordSetValue(&txt, "vendor-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME))) + TXTRecordSetValue(&txt, "product-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS))) + TXTRecordSetValue(&txt, "class", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR))) + TXTRecordSetValue(&txt, "form-factor", strlen(t), t); + + err = DNSServiceRegister(&s->service, + 0, /* flags */ + kDNSServiceInterfaceIndexAny, + s->service_name, + pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE, + NULL, /* domain */ + NULL, /* host */ + compute_port(s->userdata), + TXTRecordGetLength(&txt), + TXTRecordGetBytesPtr(&txt), + dns_service_register_reply, s); + + if (err != kDNSServiceErr_NoError) { + pa_log("DNSServiceRegister() returned err %d", err); + goto finish; + } + + pa_log_debug("Successfully registered Bonjour services for >%s<.", s->service_name); + return 0; + +finish: + + /* Remove this service */ + if (r < 0) + service_free(s); + + TXTRecordDeallocate(&txt); + + return r; +} + +static struct service *get_service(struct userdata *u, pa_object *device) { + struct service *s; + char *hn, *un; + const char *n; + + pa_assert(u); + pa_object_assert_ref(device); + + if ((s = pa_hashmap_get(u->services, device))) + return s; + + s = pa_xnew0(struct service, 1); + s->userdata = u; + s->device = device; + + if (pa_sink_isinstance(device)) { + if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) + n = PA_SINK(device)->name; + } else { + if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) + n = PA_SOURCE(device)->name; + } + + hn = pa_get_host_name_malloc(); + un = pa_get_user_name_malloc(); + + s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1); + + pa_xfree(un); + pa_xfree(hn); + + pa_hashmap_put(u->services, s->device, s); + + return s; +} + +static void service_free(struct service *s) { + pa_assert(s); + + pa_hashmap_remove(s->userdata->services, s->device); + + if (s->service) + DNSServiceRefDeallocate(s->service); + + pa_xfree(s->service_name); + 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); + + if (!shall_ignore(o)) + publish_service(get_service(u, o)); + + return PA_HOOK_OK; +} + +static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) { + struct service *s; + + pa_assert(c); + pa_object_assert_ref(o); + + if ((s = pa_hashmap_get(u->services, o))) + service_free(s); + + return PA_HOOK_OK; +} + +static int publish_main_service(struct userdata *u) { + DNSServiceErrorType err; + TXTRecordRef txt; + + pa_assert(u); + + if (u->main_service) { + DNSServiceRefDeallocate(u->main_service); + u->main_service = NULL; + } + + TXTRecordCreate(&txt, 0, NULL); + txt_record_server_data(u->core, &txt); + + err = DNSServiceRegister(&u->main_service, + 0, /* flags */ + kDNSServiceInterfaceIndexAny, + u->service_name, + SERVICE_TYPE_SERVER, + NULL, /* domain */ + NULL, /* host */ + compute_port(u), + TXTRecordGetLength(&txt), + TXTRecordGetBytesPtr(&txt), + NULL, NULL); + + if (err != kDNSServiceErr_NoError) { + pa_log("%s(): DNSServiceRegister() returned err %d", __func__, err); + return err; + } + + TXTRecordDeallocate(&txt); + + return 0; +} + +static int publish_all_services(struct userdata *u) { + pa_sink *sink; + pa_source *source; + uint32_t idx; + + pa_assert(u); + + pa_log_debug("Publishing services in Bonjour"); + + for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx))) + 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))) + if (!shall_ignore(PA_OBJECT(source))) + publish_service(get_service(u, PA_OBJECT(source))); + + return publish_main_service(u); +} + +static void unpublish_all_services(struct userdata *u) { + void *state = NULL; + struct service *s; + + pa_assert(u); + + pa_log_debug("Unpublishing services in Bonjour"); + + while ((s = pa_hashmap_iterate(u->services, &state, NULL))) + service_free(s); + + if (u->main_service) + DNSServiceRefDeallocate(u->main_service); +} + +int pa__init(pa_module*m) { + + struct userdata *u; + pa_modargs *ma = NULL; + char *hn, *un; + + 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->core = m->core; + u->module = m; + u->native = pa_native_protocol_get(u->core); + + 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_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); + + un = pa_get_user_name_malloc(); + hn = pa_get_host_name_malloc(); + u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1); + pa_xfree(un); + pa_xfree(hn); + + publish_all_services(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; + + unpublish_all_services(u); + + if (u->services) + pa_hashmap_free(u->services, NULL, NULL); + + if (u->sink_new_slot) + pa_hook_slot_free(u->sink_new_slot); + if (u->source_new_slot) + pa_hook_slot_free(u->source_new_slot); + if (u->sink_changed_slot) + pa_hook_slot_free(u->sink_changed_slot); + if (u->source_changed_slot) + pa_hook_slot_free(u->source_changed_slot); + if (u->sink_unlink_slot) + pa_hook_slot_free(u->sink_unlink_slot); + if (u->source_unlink_slot) + pa_hook_slot_free(u->source_unlink_slot); + + if (u->native) + pa_native_protocol_unref(u->native); + + pa_xfree(u->service_name); + pa_xfree(u); +} diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c index 9826e5f4..53f53353 100644 --- a/src/modules/module-sine-source.c +++ b/src/modules/module-sine-source.c @@ -32,7 +32,6 @@ #include <unistd.h> #include <limits.h> #include <sys/ioctl.h> -#include <sys/poll.h> #include <pulse/rtclock.h> #include <pulse/timeval.h> @@ -48,6 +47,7 @@ #include <pulsecore/thread.h> #include <pulsecore/thread-mq.h> #include <pulsecore/rtpoll.h> +#include <pulsecore/poll.h> #include "module-sine-source-symdef.h" diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index ac48ab10..ce534ce3 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -32,7 +32,6 @@ #include <fcntl.h> #include <unistd.h> #include <limits.h> -#include <poll.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> @@ -60,6 +59,7 @@ #include <pulsecore/thread.h> #include <pulsecore/time-smoother.h> #include <pulsecore/socket-util.h> +#include <pulsecore/poll.h> #include "module-raop-sink-symdef.h" #include "rtp.h" diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c index c4b02371..e3152dd3 100644 --- a/src/modules/raop/raop_client.c +++ b/src/modules/raop/raop_client.c @@ -51,12 +51,7 @@ #include <pulsecore/macro.h> #include <pulsecore/strbuf.h> #include <pulsecore/random.h> - -#ifdef HAVE_POLL_H -#include <poll.h> -#else #include <pulsecore/poll.h> -#endif #include "raop_client.h" #include "rtsp_client.h" diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c index 59618064..6094eb82 100644 --- a/src/modules/rtp/rtsp_client.c +++ b/src/modules/rtp/rtsp_client.c @@ -45,12 +45,7 @@ #include <pulsecore/macro.h> #include <pulsecore/strbuf.h> #include <pulsecore/ioline.h> - -#ifdef HAVE_POLL_H -#include <poll.h> -#else #include <pulsecore/poll.h> -#endif #include "rtsp_client.h" diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 6cd089ef..8f743ec7 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -32,12 +32,6 @@ #include <fcntl.h> #include <errno.h> -#ifdef HAVE_POLL_H -#include <poll.h> -#else -#include <pulsecore/poll.h> -#endif - #ifndef HAVE_PIPE #include <pulsecore/pipe.h> #endif @@ -47,6 +41,7 @@ #include <pulse/timeval.h> #include <pulse/xmalloc.h> +#include <pulsecore/poll.h> #include <pulsecore/core-rtclock.h> #include <pulsecore/core-util.h> #include <pulsecore/llist.h> @@ -887,7 +882,7 @@ int pa_mainloop_poll(pa_mainloop *m) { m->prepared_timeout == PA_USEC_INVALID ? NULL : pa_timespec_store(&ts, m->prepared_timeout), NULL); #else - m->poll_func_ret = poll( + m->poll_func_ret = pa_poll( m->pollfds, m->n_pollfds, usec_to_timeout(m->prepared_timeout)); #endif diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c index 4fe0a47b..110158ba 100644 --- a/src/pulsecore/core-rtclock.c +++ b/src/pulsecore/core-rtclock.c @@ -37,6 +37,7 @@ #include <CoreServices/CoreServices.h> #include <mach/mach.h> #include <mach/mach_time.h> +#include <unistd.h> #endif #include <pulse/timeval.h> @@ -54,7 +55,19 @@ pa_usec_t pa_rtclock_age(const struct timeval *tv) { struct timeval *pa_rtclock_get(struct timeval *tv) { -#if defined(HAVE_CLOCK_GETTIME) +#if defined(OS_IS_DARWIN) + uint64_t val, abs_time = mach_absolute_time(); + Nanoseconds nanos; + + nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time); + val = *(uint64_t *) &nanos; + + tv->tv_sec = val / PA_NSEC_PER_SEC; + tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC; + + return tv; + +#elif defined(HAVE_CLOCK_GETTIME) struct timespec ts; #ifdef CLOCK_MONOTONIC @@ -75,65 +88,39 @@ struct timeval *pa_rtclock_get(struct timeval *tv) { tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC; return tv; - -#elif defined(OS_IS_DARWIN) - static mach_timebase_info_data_t tbi; - uint64_t nticks; - uint64_t time_nsec; - - /* Refer Apple ADC QA1398 - Also: http://devworld.apple.com/documentation/Darwin/Conceptual/KernelProgramming/services/services.html - - Note: argument is timespec NOT timeval (timespec uses nsec, timeval uses usec) - */ - - /* try and be a mite efficient - maybe I should keep the N/D as a float !? */ - if (tbi.denom == 0) - mach_timebase_info(&tbi); - - nticks = mach_absolute_time(); - time_nsec = nticks * tbi.numer / tbi.denom; // see above - - tv->tv_sec = time_nsec / PA_NSEC_PER_SEC; - tv->tv_usec = time_nsec / PA_NSEC_PER_USEC; - - return tv; - -#else /* OS_IS_DARWIN */ +#endif /* HAVE_CLOCK_GETTIME */ return pa_gettimeofday(tv); - -#endif } pa_bool_t pa_rtclock_hrtimer(void) { -#if defined(HAVE_CLOCK_GETTIME) +#if defined (OS_IS_DARWIN) + mach_timebase_info_data_t tbi; + uint64_t time_nsec; + + mach_timebase_info(&tbi); + + /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */ + time_nsec = tbi.numer / tbi.denom; + return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); + +#elif defined(HAVE_CLOCK_GETTIME) struct timespec ts; #ifdef CLOCK_MONOTONIC if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); + #endif /* CLOCK_MONOTONIC */ pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); -#elif defined (OS_IS_DARWIN) - mach_timebase_info_data_t tbi; - uint64_t time_nsec; - - mach_timebase_info(&tbi); +#endif /* HAVE_CLOCK_GETTIME */ - /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */ - time_nsec = tbi.numer / tbi.denom; - return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); - -#else /* OS_IS_DARWIN */ return FALSE; - -#endif } #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC)) diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c index 65e35634..95ca04a8 100644 --- a/src/pulsecore/lock-autospawn.c +++ b/src/pulsecore/lock-autospawn.c @@ -33,6 +33,7 @@ #include <pulse/i18n.h> #include <pulse/xmalloc.h> +#include <pulsecore/poll.h> #include <pulsecore/mutex.h> #include <pulsecore/thread.h> #include <pulsecore/core-util.h> @@ -182,7 +183,7 @@ static void wait_for_ping(void) { pfd.fd = pipe_fd[0]; pfd.events = POLLIN; - if ((k = poll(&pfd, 1, -1)) != 1) { + if ((k = pa_poll(&pfd, 1, -1)) != 1) { pa_assert(k < 0); pa_assert(errno == EINTR); } else if ((s = read(pipe_fd[0], &x, 1)) != 1) { diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c index 46a69c5f..b98fb169 100644 --- a/src/pulsecore/poll.c +++ b/src/pulsecore/poll.c @@ -35,6 +35,10 @@ #include <config.h> #endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + #include <errno.h> #ifdef HAVE_SYS_SELECT_H @@ -43,19 +47,26 @@ #include "winsock.h" -#ifndef HAVE_POLL_H - #include <pulsecore/core-util.h> +#include <pulse/util.h> #include "poll.h" -int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { +/* Mac OSX fails to implement poll() in a working way since 10.4. IOW, for + * several years. We need to enable a dirty workaround and emulate that call + * with select(), just like for Windows. sic! */ + +#if !defined(HAVE_POLL_H) || defined(OS_IS_DARWIN) + +int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) { struct timeval tv; fd_set rset, wset, xset; struct pollfd *f; int ready; int maxfd = 0; +#ifdef OS_IS_WIN32 char data[64]; +#endif FD_ZERO (&rset); FD_ZERO (&wset); @@ -98,6 +109,7 @@ int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); + if ((ready == -1) && (errno == EBADF)) { ready = 0; @@ -160,6 +172,8 @@ int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { #endif if (ready > 0) { + int r; + ready = 0; for (f = fds; f < &fds[nfds]; ++f) { f->revents = 0; @@ -167,6 +181,18 @@ int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { if (FD_ISSET (f->fd, &rset)) { /* support for POLLHUP. An hung up descriptor does not increase the return value! */ +#ifdef OS_IS_DARWIN + /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK + * for some kinds of descriptors. Detect if this descriptor is a + * connected socket, a server socket, or something else using a + * 0-byte recv, and use ioctl(2) to detect POLLHUP. */ + r = recv(f->fd, NULL, 0, MSG_PEEK); + if (r == 0 || (r < 0 && errno == ENOTSOCK)) + ioctl(f->fd, FIONREAD, &r); + + if (r == 0) + f->revents |= POLLHUP; +#else /* !OS_IS_DARWIN */ if (recv (f->fd, data, 64, MSG_PEEK) == -1) { if (errno == ESHUTDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ENETRESET) { @@ -174,6 +200,7 @@ int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { f->revents |= POLLHUP; } } +#endif if (f->revents == 0) f->revents |= POLLIN; diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h index fe0c6af6..a137d974 100644 --- a/src/pulsecore/poll.h +++ b/src/pulsecore/poll.h @@ -24,6 +24,10 @@ Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc. ***/ +#if defined(HAVE_POLL_H) +#include <poll.h> +#else + /* Event types that can be polled for. These bits may be set in `events' to indicate the interesting event types; they will appear in `revents' to indicate the status of the file descriptor. */ @@ -38,10 +42,6 @@ #define POLLHUP 0x010 /* Hung up. */ #define POLLNVAL 0x020 /* Invalid polling request. */ - -/* Type used for the number of file descriptors. */ -typedef unsigned long int nfds_t; - /* Data structure describing a polling request. */ struct pollfd { @@ -50,9 +50,17 @@ struct pollfd short int revents; /* Types of events that actually occurred. */ }; + /* Poll the file descriptors described by the NFDS structures starting at FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for an event to occur; if TIMEOUT is -1, block until an event occurs. Returns the number of file descriptors with events, zero if timed out, or -1 for errors. */ -extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); + +#endif /* HAVE_POLL_H */ + +#if defined(HAVE_POLL_H) && !defined(OS_IS_DARWIN) +#define pa_poll(fds,nfds,timeout) poll((fds),(nfds),(timeout)) +#else +int pa_poll (struct pollfd *fds, unsigned long nfds, int timeout); +#endif diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index e2f82626..2ef78736 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -30,15 +30,10 @@ #include <string.h> #include <errno.h> -#ifdef HAVE_POLL_H -#include <poll.h> -#else -#include <pulsecore/poll.h> -#endif - #include <pulse/xmalloc.h> #include <pulse/timeval.h> +#include <pulsecore/poll.h> #include <pulsecore/core-error.h> #include <pulsecore/core-rtclock.h> #include <pulsecore/macro.h> @@ -321,7 +316,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) { r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL); } #else - r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1); + r = pa_poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1); #endif p->timer_elapsed = r == 0; diff --git a/src/tests/lock-autospawn-test.c b/src/tests/lock-autospawn-test.c index c754e230..6f7156d7 100644 --- a/src/tests/lock-autospawn-test.c +++ b/src/tests/lock-autospawn-test.c @@ -23,9 +23,9 @@ #include <config.h> #endif -#include <sys/poll.h> #include <string.h> +#include <pulsecore/poll.h> #include <pulsecore/macro.h> #include <pulsecore/thread.h> #include <pulsecore/lock-autospawn.h> @@ -69,7 +69,7 @@ static void thread_func2(void *k) { pollfd.fd = fd; pollfd.events = POLLIN; - pa_assert_se(poll(&pollfd, 1, -1) == 1); + pa_assert_se(pa_poll(&pollfd, 1, -1) == 1); pa_log("%i, woke up", PA_PTR_TO_INT(k)); } diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c index 1706cdfa..6a6b73a8 100644 --- a/src/tests/rtpoll-test.c +++ b/src/tests/rtpoll-test.c @@ -22,8 +22,8 @@ #endif #include <signal.h> -#include <poll.h> +#include <pulsecore/poll.h> #include <pulsecore/log.h> #include <pulsecore/rtpoll.h> diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c index ef58e9ce..6ffe94fe 100644 --- a/src/utils/pacmd.c +++ b/src/utils/pacmd.c @@ -25,7 +25,6 @@ #include <assert.h> #include <signal.h> -#include <sys/poll.h> #include <sys/socket.h> #include <unistd.h> #include <errno.h> @@ -38,6 +37,7 @@ #include <pulse/xmalloc.h> #include <pulse/i18n.h> +#include <pulsecore/poll.h> #include <pulsecore/macro.h> #include <pulsecore/core-util.h> #include <pulsecore/log.h> @@ -153,7 +153,7 @@ int main(int argc, char*argv[]) { else if (!ibuf_eof) pollfd[WATCH_STDIN].events |= POLLIN; - if (poll(pollfd, N_WATCH, -1) < 0) { + if (pa_poll(pollfd, N_WATCH, -1) < 0) { if (errno == EINTR) continue; |