X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/a67c21f093202f142438689d3f7cfbdf4ea82eea..d806b197144733607b0ecb8678c6ee5d99ccc9ea:/src/modules/module-esound-sink.c diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 8b46637e..8b74ea43 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. @@ -7,7 +5,7 @@ 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, + 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 @@ -26,26 +24,32 @@ #endif #include -#include #include #include #include -#include #include -#include -#include -#include + +#ifdef HAVE_NETINET_IN_H #include +#endif + +#ifdef HAVE_NETINET_TCP_H #include +#endif + +#ifdef HAVE_SYS_IOCTL_H #include +#endif #ifdef HAVE_LINUX_SOCKIOS_H #include #endif -#include +#include #include +#include +#include #include #include #include @@ -59,20 +63,23 @@ #include #include #include -#include #include +#include +#include #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= " + "sink_properties= " "server=
cookie= " "format= " - "channels= " - "rate=") + "rate= " + "channels="); #define DEFAULT_SINK_NAME "esound_out" @@ -119,12 +126,13 @@ struct userdata { }; static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", "server", "cookie", - "rate", "format", + "rate", "channels", - "sink_name", NULL }; @@ -142,21 +150,22 @@ 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()); + pa_smoother_pause(u->smoother, pa_rtclock_now()); break; case PA_SINK_IDLE: case PA_SINK_RUNNING: if (u->sink->thread_info.state == PA_SINK_SUSPENDED) - pa_smoother_resume(u->smoother, pa_rtclock_usec()); + pa_smoother_resume(u->smoother, pa_rtclock_now(), true); break; case PA_SINK_UNLINKED: case PA_SINK_INIT: + case PA_SINK_INVALID_STATE: ; } @@ -165,11 +174,11 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t w, r; - r = pa_smoother_get(u->smoother, pa_rtclock_usec()); - w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec); + r = pa_smoother_get(u->smoother, pa_rtclock_now()); + w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec); *((pa_usec_t*) data) = w > r ? w - r : 0; - break; + return 0; } case SINK_MESSAGE_PASS_SOCKET: { @@ -198,19 +207,21 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); - pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); for (;;) { int ret; + if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) + pa_sink_process_rewind(u->sink, 0); + if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ - if (PA_SINK_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; @@ -247,8 +258,8 @@ static void thread_func(void *userdata) { } else { u->offset += l; - u->memchunk.index += l; - u->memchunk.length -= l; + u->memchunk.index += (size_t) l; + u->memchunk.length -= (size_t) l; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); @@ -282,21 +293,21 @@ static void thread_func(void *userdata) { } #endif - usec = pa_bytes_to_usec(n, &u->sink->sample_spec); + usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec); if (usec > u->latency) usec -= u->latency; else usec = 0; - pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); + pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ - pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; + pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0); } - if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) + if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) goto fail; if (ret == 0) @@ -334,12 +345,12 @@ static int do_write(struct userdata *u) { if (u->write_data) { pa_assert(u->write_index < u->write_length); - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) { + if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) < 0) { pa_log("write() failed: %s", pa_cstrerror(errno)); return -1; } - u->write_index += r; + u->write_index += (size_t) r; pa_assert(u->write_index <= u->write_length); if (u->write_index == u->write_length) { @@ -350,19 +361,29 @@ static int do_write(struct userdata *u) { } if (!u->write_data && u->state == STATE_PREPARE) { + int so_sndbuf = 0; + socklen_t sl = sizeof(int); + /* OK, we're done with sending all control data we need to, so * let's hand the socket over to the IO thread now */ pa_assert(u->fd < 0); u->fd = pa_iochannel_get_send_fd(u->io); - pa_iochannel_set_noclose(u->io, TRUE); + pa_iochannel_set_noclose(u->io, true); pa_iochannel_free(u->io); u->io = NULL; pa_make_tcp_socket_low_delay(u->fd); - pa_log_info("Connection authenticated, handing fd to IO thread..."); + if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, (void *) &so_sndbuf, &sl) < 0) + pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno)); + else { + pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf); + pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size)); + } + + 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; @@ -455,7 +476,7 @@ static int do_read(struct userdata *u) { return -1; } - u->read_index += r; + u->read_index += (size_t) r; pa_assert(u->read_index <= u->read_length); if (u->read_index == u->read_length) @@ -465,7 +486,7 @@ static int do_read(struct userdata *u) { return 0; } -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { +static void io_callback(pa_iochannel *io, void*userdata) { struct userdata *u = userdata; pa_assert(u); @@ -476,11 +497,11 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { u->io = NULL; } - pa_module_unload_request(u->module); + pa_module_unload_request(u->module, true); } } -static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { +static void on_connection(pa_socket_client *c, pa_iochannel*io, void *userdata) { struct userdata *u = userdata; pa_socket_client_unref(u->client); @@ -488,7 +509,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo if (!io) { pa_log("Connection failed: %s", pa_cstrerror(errno)); - pa_module_unload_request(u->module); + pa_module_unload_request(u->module, true); return; } @@ -496,16 +517,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,19 +552,25 @@ 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, + true, + 10, + 0, + false); 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 = (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | (ss.channels == 2 ? ESD_STEREO : ESD_MONO); - u->rate = ss.rate; + u->rate = (int32_t) ss.rate; u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss); u->read_data = u->write_data = NULL; @@ -552,44 +579,61 @@ 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_sets(data.proplist, PA_PROP_DEVICE_API, "esd"); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "EsounD Output on %s", espeaker); + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&data); + goto fail; + } + + 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, true, espeaker, ESD_DEFAULT_PORT))) { pa_log("Failed to connect to server."); goto fail; } - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p)); - pa_xfree(t); - pa_socket_client_set_callback(u->client, on_connection, u); /* Prepare the initial request */ u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t)); - if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) { + if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), true, u->write_data, ESD_KEY_LEN) < 0) { 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)); - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("esound-sink", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } @@ -609,6 +653,15 @@ fail: return -1; } +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + void pa__done(pa_module*m) { struct userdata *u; pa_assert(m);