From 6570620cc3717eb82acd19788538fda3786c7b99 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 May 2008 23:51:45 +0000 Subject: Start the raop sink. It's based on pipe sink and isn't anywhere near finished. It does however compile. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2335 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 417 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 src/modules/module-raop-sink.c (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c new file mode 100644 index 00000000..ad10d78f --- /dev/null +++ b/src/modules/module-raop-sink.c @@ -0,0 +1,417 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2008 Colin Guthrie + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtp.h" +#include "sdp.h" +#include "sap.h" +#include "rtsp.h" +#include "base64.h" + + +#include "module-raop-sink-symdef.h" + +#define JACK_STATUS_DISCONNECTED 0 +#define JACK_STATUS_CONNECTED 1 + +#define JACK_TYPE_ANALOG 0 +#define JACK_TYPE_DIGITAL 1 + +#define VOLUME_DEF -30 +#define VOLUME_MIN -144 +#define VOLUME_MAX 0 + + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airport)"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "server=
" + "sink_name= " + "format= " + "channels= " + "rate=" + "channel_map="); + +#define DEFAULT_SINK_NAME "airtunes" +#define AES_CHUNKSIZE 16 + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + + pa_thread *thread; + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + + char *server_name; + + // Encryption Related bits + AES_KEY aes; + uint8_t aes_iv[AES_CHUNKSIZE]; // initialization vector for aes-cbc + uint8_t aes_nv[AES_CHUNKSIZE]; // next vector for aes-cbc + uint8_t aes_key[AES_CHUNKSIZE]; // key for aes-cbc + + pa_rtsp_context *rtsp; + //pa_socket_client *client; + pa_memchunk memchunk; + + pa_rtpoll_item *rtpoll_item; +}; + +static const char* const valid_modargs[] = { + "server", + "rate", + "format", + "channels", + "sink_name", + "channel_map", + NULL +}; + +static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) { + char n[] = + "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC" + "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR" + "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB" + "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ" + "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh" + "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew=="; + char e[] = "AQAB"; + uint8_t modules[256]; + uint8_t exponent[8]; + int size; + RSA *rsa; + + rsa = RSA_new(); + size = pa_base64_decode(n, modules); + rsa->n = BN_bin2bn(modules, size, NULL); + size = pa_base64_decode(e, exponent); + rsa->e = BN_bin2bn(exponent, size, NULL); + + size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING); + RSA_free(rsa); + return size; +} + +static int aes_encrypt(struct userdata *u, uint8_t *data, int size) +{ + uint8_t *buf; + int i=0, j; + + pa_assert(u); + + memcpy(u->aes_nv, u->aes_iv, AES_CHUNKSIZE); + while (i+AES_CHUNKSIZE <= size) { + buf = data + i; + for (j=0; jaes_nv[j]; + + AES_encrypt(buf, buf, &u->aes); + memcpy(u->aes_nv, buf, AES_CHUNKSIZE); + i += AES_CHUNKSIZE; + } + return i; +} + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SINK(o)->userdata; + + switch (code) { + + case PA_SINK_MESSAGE_GET_LATENCY: { + size_t n = 0; + //int l; + +#ifdef TIOCINQ + /* + if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0) + n = (size_t) l; + */ +#endif + + n += u->memchunk.length; + + *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); + break; + } + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + //int write_type = 0; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + pa_thread_mq_install(&u->thread_mq); + pa_rtpoll_install(u->rtpoll); + + for (;;) { + struct pollfd *pollfd; + int ret; + + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + /* Render some data and write it to the fifo */ + if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) { + ssize_t l; + void *p; + + if (u->memchunk.length <= 0) + pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); + + pa_assert(u->memchunk.length > 0); + + p = pa_memblock_acquire(u->memchunk.memblock); + //l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); + // Fake the length of the "write". + l = u->memchunk.length; + pa_memblock_release(u->memchunk.memblock); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + continue; + else if (errno != EAGAIN) { + pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + goto fail; + } + + } else { + + u->memchunk.index += l; + u->memchunk.length -= l; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + pa_memchunk_reset(&u->memchunk); + } + + pollfd->revents = 0; + } + } + + /* Hmm, nothing to do. Let's sleep */ + pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0; + + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) + goto fail; + + if (ret == 0) + goto finish; + + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + if (pollfd->revents & ~POLLOUT) { + pa_log("FIFO shutdown."); + goto fail; + } + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { + struct userdata *u; + //struct stat st; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma; + char *t; + struct pollfd *pollfd; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + ss = m->core->default_sample_spec; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + m->userdata = u; + + // Initialise the AES encryption system + pa_random_seed(); + pa_random(u->aes_iv, sizeof(u->aes_iv)); + pa_random(u->aes_key, sizeof(u->aes_key)); + memcpy(u->aes_nv, u->aes_iv, sizeof(u->aes_nv)); + AES_set_encrypt_key(u->aes_key, 128, &u->aes); + + pa_memchunk_reset(&u->memchunk); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop); + u->rtpoll = pa_rtpoll_new(); + pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + + u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)); + + // Open a connection to the server... this is just to connect and test.... + /* + mkfifo(u->filename, 0666); + if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { + pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno)); + goto fail; + } + + pa_make_fd_cloexec(u->fd); + pa_make_fd_nonblock(u->fd); + + if (fstat(u->fd, &st) < 0) { + pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno)); + goto fail; + } + + if (!S_ISFIFO(st.st_mode)) { + pa_log("'%s' is not a FIFO.", u->filename); + 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_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("Airtunes sink '%s'", u->server_name)); + 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; + } + + pa_sink_put(u->sink); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) + pa_sink_unref(u->sink); + + if (u->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); + + pa_xfree(u->server_name); + pa_xfree(u); +} -- cgit From 66cf1d1f66c90b5f92d67a00ea2c1f6404453d97 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Tue, 6 May 2008 00:25:37 +0000 Subject: Some minor tidyup to remove code now in raop client. Still nowhere near functional. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2367 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 69 ++---------------------------------------- 1 file changed, 2 insertions(+), 67 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index ad10d78f..f2ddf1cf 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -36,11 +36,6 @@ #include #include #include -#include -#include -#include -#include -#include #include @@ -58,8 +53,7 @@ #include "rtp.h" #include "sdp.h" #include "sap.h" -#include "rtsp.h" -#include "base64.h" +#include "raop_client.h" #include "module-raop-sink-symdef.h" @@ -88,7 +82,6 @@ PA_MODULE_USAGE( "channel_map="); #define DEFAULT_SINK_NAME "airtunes" -#define AES_CHUNKSIZE 16 struct userdata { pa_core *core; @@ -101,13 +94,7 @@ struct userdata { char *server_name; - // Encryption Related bits - AES_KEY aes; - uint8_t aes_iv[AES_CHUNKSIZE]; // initialization vector for aes-cbc - uint8_t aes_nv[AES_CHUNKSIZE]; // next vector for aes-cbc - uint8_t aes_key[AES_CHUNKSIZE]; // key for aes-cbc - - pa_rtsp_context *rtsp; + pa_raop_client *raop; //pa_socket_client *client; pa_memchunk memchunk; @@ -124,51 +111,6 @@ static const char* const valid_modargs[] = { NULL }; -static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) { - char n[] = - "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC" - "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR" - "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB" - "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ" - "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh" - "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew=="; - char e[] = "AQAB"; - uint8_t modules[256]; - uint8_t exponent[8]; - int size; - RSA *rsa; - - rsa = RSA_new(); - size = pa_base64_decode(n, modules); - rsa->n = BN_bin2bn(modules, size, NULL); - size = pa_base64_decode(e, exponent); - rsa->e = BN_bin2bn(exponent, size, NULL); - - size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING); - RSA_free(rsa); - return size; -} - -static int aes_encrypt(struct userdata *u, uint8_t *data, int size) -{ - uint8_t *buf; - int i=0, j; - - pa_assert(u); - - memcpy(u->aes_nv, u->aes_iv, AES_CHUNKSIZE); - while (i+AES_CHUNKSIZE <= size) { - buf = data + i; - for (j=0; jaes_nv[j]; - - AES_encrypt(buf, buf, &u->aes); - memcpy(u->aes_nv, buf, AES_CHUNKSIZE); - i += AES_CHUNKSIZE; - } - return i; -} - 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; @@ -307,13 +249,6 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; - // Initialise the AES encryption system - pa_random_seed(); - pa_random(u->aes_iv, sizeof(u->aes_iv)); - pa_random(u->aes_key, sizeof(u->aes_key)); - memcpy(u->aes_nv, u->aes_iv, sizeof(u->aes_nv)); - AES_set_encrypt_key(u->aes_key, 128, &u->aes); - pa_memchunk_reset(&u->memchunk); pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); -- cgit From d51f5944b7248ec759ab71b0e811ec0f7c655e22 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 12:21:32 +0000 Subject: A very rough first version of the sink. I can actually play music to my airport now (woot). Still very rough round the edges and I need to handle disconnects etc. but it's all good progress :) git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2398 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 390 ++++++++++++++++++++++++++++------------- 1 file changed, 267 insertions(+), 123 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index f2ddf1cf..3d08cb86 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -3,8 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering - Copyright 2008 Colin Guthrie + 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 @@ -34,52 +33,50 @@ #include #include #include -#include #include +#include +#include +#include +#include + +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif #include +#include #include +#include #include #include #include #include #include -#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include "module-raop-sink-symdef.h" #include "rtp.h" #include "sdp.h" #include "sap.h" #include "raop_client.h" - -#include "module-raop-sink-symdef.h" - -#define JACK_STATUS_DISCONNECTED 0 -#define JACK_STATUS_CONNECTED 1 - -#define JACK_TYPE_ANALOG 0 -#define JACK_TYPE_DIGITAL 1 - -#define VOLUME_DEF -30 -#define VOLUME_MIN -144 -#define VOLUME_MAX 0 - - PA_MODULE_AUTHOR("Colin Guthrie"); -PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airport)"); +PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airtunes)"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( - "server=
" "sink_name= " + "server=
cookie= " "format= " "channels= " - "rate=" - "channel_map="); + "rate="); #define DEFAULT_SINK_NAME "airtunes" @@ -88,17 +85,35 @@ struct userdata { pa_module *module; pa_sink *sink; - pa_thread *thread; pa_thread_mq thread_mq; pa_rtpoll *rtpoll; + pa_rtpoll_item *rtpoll_item; + pa_thread *thread; + + pa_memchunk raw_memchunk; + pa_memchunk encoded_memchunk; + + void *write_data; + size_t write_length, write_index; + + void *read_data; + size_t read_length, read_index; + + pa_usec_t latency; + + /*esd_format_t format;*/ + int32_t rate; + + pa_smoother *smoother; + int fd; - char *server_name; + int64_t offset; + int64_t encoding_overhead; + double encoding_ratio; pa_raop_client *raop; - //pa_socket_client *client; - pa_memchunk memchunk; - pa_rtpoll_item *rtpoll_item; + size_t block_size; }; static const char* const valid_modargs[] = { @@ -107,31 +122,65 @@ static const char* const valid_modargs[] = { "format", "channels", "sink_name", - "channel_map", NULL }; +enum { + SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX +}; + static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { + case PA_SINK_MESSAGE_SET_STATE: + + switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + + case PA_SINK_SUSPENDED: + pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + + pa_smoother_pause(u->smoother, pa_rtclock_usec()); + break; + + case PA_SINK_IDLE: + case PA_SINK_RUNNING: + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) + pa_smoother_resume(u->smoother, pa_rtclock_usec()); + + break; + + case PA_SINK_UNLINKED: + case PA_SINK_INIT: + ; + } + + break; + case PA_SINK_MESSAGE_GET_LATENCY: { - size_t n = 0; - //int l; - -#ifdef TIOCINQ - /* - if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0) - n = (size_t) l; - */ -#endif + pa_usec_t w, r; - n += u->memchunk.length; + r = pa_smoother_get(u->smoother, pa_rtclock_usec()); + w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec); - *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); + *((pa_usec_t*) data) = w > r ? w - r : 0; break; } + + case SINK_MESSAGE_PASS_SOCKET: { + struct pollfd *pollfd; + + pa_assert(!u->rtpoll_item); + + u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + pollfd->fd = u->fd; + pollfd->events = pollfd->revents = 0; + + return 0; + } } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -139,7 +188,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse static void thread_func(void *userdata) { struct userdata *u = userdata; - //int write_type = 0; + int write_type = 0; pa_assert(u); @@ -148,55 +197,113 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + for (;;) { - struct pollfd *pollfd; int ret; - pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); - - /* Render some data and write it to the fifo */ - if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) { - ssize_t l; - void *p; - - if (u->memchunk.length <= 0) - pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); - - pa_assert(u->memchunk.length > 0); + if (u->rtpoll_item) { + struct pollfd *pollfd; + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + /* Render some data and write it to the fifo */ + if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) { + pa_usec_t usec; + int64_t n; + + for (;;) { + ssize_t l; + void *p; + + if (u->raw_memchunk.length <= 0) { + /* Grab unencoded data */ + pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); + } + pa_assert(u->raw_memchunk.length > 0); + + if (u->encoded_memchunk.length <= 0) { + /* Encode it */ + size_t rl = u->raw_memchunk.length; + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk); + u->encoding_overhead += (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); + u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); + } + pa_assert(u->encoded_memchunk.length > 0); + + p = pa_memblock_acquire(u->encoded_memchunk.memblock); + l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); + pa_memblock_release(u->encoded_memchunk.memblock); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + continue; + else if (errno == EAGAIN) { + + /* OK, we filled all socket buffers up + * now. */ + goto filled_up; + + } else { + pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + goto fail; + } + + } else { + u->offset += l; + + u->encoded_memchunk.index += l; + u->encoded_memchunk.length -= l; + + if (u->encoded_memchunk.length <= 0) { + pa_memblock_unref(u->encoded_memchunk.memblock); + pa_memchunk_reset(&u->encoded_memchunk); + } + + pollfd->revents = 0; + + if (u->encoded_memchunk.length > 0) + + /* OK, we wrote less that we asked for, + * hence we can assume that the socket + * buffers are full now */ + goto filled_up; + } + } - p = pa_memblock_acquire(u->memchunk.memblock); - //l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); - // Fake the length of the "write". - l = u->memchunk.length; - pa_memblock_release(u->memchunk.memblock); + filled_up: - pa_assert(l != 0); + /* At this spot we know that the socket buffers are + * fully filled up. This is the best time to estimate + * the playback position of the server */ - if (l < 0) { + n = u->offset; - if (errno == EINTR) - continue; - else if (errno != EAGAIN) { - pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); - goto fail; +#ifdef SIOCOUTQ + { + int l; + if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) + n -= l; } +#endif - } else { - - u->memchunk.index += l; - u->memchunk.length -= l; + usec = pa_bytes_to_usec(n, &u->sink->sample_spec); - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); - } + if (usec > u->latency) + usec -= u->latency; + else + usec = 0; - pollfd->revents = 0; + pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); } - } - /* Hmm, nothing to do. Let's sleep */ - pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0; + /* Hmm, nothing to do. Let's sleep */ + pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; + } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; @@ -204,11 +311,15 @@ static void thread_func(void *userdata) { if (ret == 0) goto finish; - pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + if (u->rtpoll_item) { + struct pollfd* pollfd; - if (pollfd->revents & ~POLLOUT) { - pa_log("FIFO shutdown."); - goto fail; + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + if (pollfd->revents & ~POLLOUT) { + pa_log("FIFO shutdown."); + goto fail; + } } } @@ -222,25 +333,41 @@ finish: pa_log_debug("Thread shutting down"); } +static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { + struct userdata *u = userdata; + pa_assert(u); + + pa_assert(u->fd < 0); + u->fd = fd; + + 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); +} + int pa__init(pa_module*m) { - struct userdata *u; - //struct stat st; + struct userdata *u = NULL; + const char *p; pa_sample_spec ss; - pa_channel_map map; - pa_modargs *ma; + pa_modargs *ma = NULL; char *t; - struct pollfd *pollfd; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); + pa_log("failed to parse module arguments"); goto fail; } ss = m->core->default_sample_spec; - if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("Invalid sample format specification or channel map"); + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log("invalid sample format specification"); + goto fail; + } + + if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) || + (ss.channels > 2)) { + pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data"); goto fail; } @@ -248,54 +375,58 @@ int pa__init(pa_module*m) { u->core = m->core; u->module = m; m->userdata = u; + u->fd = -1; + u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE); + pa_memchunk_reset(&u->raw_memchunk); + pa_memchunk_reset(&u->encoded_memchunk); + u->offset = 0; + u->encoding_overhead = 0; + u->encoding_ratio = 1.0; - pa_memchunk_reset(&u->memchunk); pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + u->rtpoll_item = NULL; - u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)); + /*u->format = + (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | + (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/ + u->rate = ss.rate; + u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss); - // Open a connection to the server... this is just to connect and test.... - /* - mkfifo(u->filename, 0666); - if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { - pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno)); - goto fail; - } - - pa_make_fd_cloexec(u->fd); - pa_make_fd_nonblock(u->fd); + u->read_data = u->write_data = NULL; + u->read_index = u->write_index = u->read_length = u->write_length = 0; - if (fstat(u->fd, &st) < 0) { - pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno)); - goto fail; - } + /*u->state = STATE_AUTH;*/ + u->latency = 0; - if (!S_ISFIFO(st.st_mode)) { - pa_log("'%s' is not a FIFO.", u->filename); - goto fail; - } - */ - 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_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; + u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK; 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("Airtunes sink '%s'", u->server_name)); + + if (!(p = pa_modargs_get_value(ma, "server", NULL))) { + pa_log("No server argument given."); + goto fail; + } + + if (!(u->raop = pa_raop_client_new(u->core->mainloop, p))) { + pa_log("Failed to connect to server."); + goto fail; + } + + pa_raop_client_set_callback(u->raop, on_connection, u); + pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p)); 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."); @@ -319,7 +450,6 @@ fail: void pa__done(pa_module*m) { struct userdata *u; - pa_assert(m); if (!(u = m->userdata)) @@ -338,15 +468,29 @@ void pa__done(pa_module*m) { if (u->sink) pa_sink_unref(u->sink); - 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); - pa_xfree(u->server_name); + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + + if (u->raop) + pa_raop_client_free(u->raop); + + pa_xfree(u->read_data); + pa_xfree(u->write_data); + + if (u->smoother) + pa_smoother_free(u->smoother); + + if (u->fd >= 0) + pa_close(u->fd); + pa_xfree(u); } -- cgit From 5eecfa2e3f3abcacc9df2776cba798598e5fb6ee Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 13:35:01 +0000 Subject: Move the ownership of the encoded data memchunk into the raop_client. This does not seem to fix the pool full messages so I'll have to try and suss that out. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2400 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 3d08cb86..f6f93a46 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $Id: module-esound-sink.c 2043 2007-11-09 18:25:40Z lennart $ */ /*** This file is part of PulseAudio. @@ -109,6 +109,7 @@ struct userdata { int64_t offset; int64_t encoding_overhead; + int32_t next_encoding_overhead; double encoding_ratio; pa_raop_client *raop; @@ -224,10 +225,9 @@ static void thread_func(void *userdata) { if (u->encoded_memchunk.length <= 0) { /* Encode it */ size_t rl = u->raw_memchunk.length; - if (u->encoded_memchunk.memblock) - pa_memblock_unref(u->encoded_memchunk.memblock); + u->encoding_overhead += u->next_encoding_overhead; u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk); - u->encoding_overhead += (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); + u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } pa_assert(u->encoded_memchunk.length > 0); @@ -259,11 +259,6 @@ static void thread_func(void *userdata) { u->encoded_memchunk.index += l; u->encoded_memchunk.length -= l; - if (u->encoded_memchunk.length <= 0) { - pa_memblock_unref(u->encoded_memchunk.memblock); - pa_memchunk_reset(&u->encoded_memchunk); - } - pollfd->revents = 0; if (u->encoded_memchunk.length > 0) @@ -381,6 +376,7 @@ int pa__init(pa_module*m) { pa_memchunk_reset(&u->encoded_memchunk); u->offset = 0; u->encoding_overhead = 0; + u->next_encoding_overhead = 0; u->encoding_ratio = 1.0; pa_thread_mq_init(&u->thread_mq, m->core->mainloop); @@ -477,9 +473,6 @@ void pa__done(pa_module*m) { if (u->raw_memchunk.memblock) pa_memblock_unref(u->raw_memchunk.memblock); - if (u->encoded_memchunk.memblock) - pa_memblock_unref(u->encoded_memchunk.memblock); - if (u->raop) pa_raop_client_free(u->raop); -- cgit From ec9a618768790055fef00a46866b4e0e1fa33048 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 14:19:41 +0000 Subject: Listen to the on_close callback. This still causes asserts in the mainloop, so this is not a complete solution git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2403 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index f6f93a46..090f04fa 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -340,6 +340,14 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); } +static void on_close(void*userdata) { + struct userdata *u = userdata; + pa_assert(u); + + pa_log_debug("Control connection closed."); + pa_module_unload_request(u->module); +} + int pa__init(pa_module*m) { struct userdata *u = NULL; const char *p; @@ -420,6 +428,7 @@ int pa__init(pa_module*m) { } pa_raop_client_set_callback(u->raop, on_connection, u); + pa_raop_client_set_closed_callback(u->raop, on_close, u); pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p)); pa_xfree(t); -- cgit From e00127fe245cc2065f74617dada3b474b88907af Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 14:57:30 +0000 Subject: Various changes suggested by Lennart. Store the core* rather than just the mainloop as we can reuse the mempool without passing it in as an argument. const'ify and deconst'ify some vars git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2404 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 090f04fa..0a7ce171 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -226,7 +226,7 @@ static void thread_func(void *userdata) { /* Encode it */ size_t rl = u->raw_memchunk.length; u->encoding_overhead += u->next_encoding_overhead; - u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk); + u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, &u->raw_memchunk); u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } @@ -422,7 +422,7 @@ int pa__init(pa_module*m) { goto fail; } - if (!(u->raop = pa_raop_client_new(u->core->mainloop, p))) { + if (!(u->raop = pa_raop_client_new(u->core, p))) { pa_log("Failed to connect to server."); goto fail; } -- cgit From d195d06da7009db985c0a5827b096bc39dd994bf Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 15:06:14 +0000 Subject: Change suggested by Lennart. Do not return a memchunk, instead pass in the pointer. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2405 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 0a7ce171..3d0eaeff 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -226,7 +226,7 @@ static void thread_func(void *userdata) { /* Encode it */ size_t rl = u->raw_memchunk.length; u->encoding_overhead += u->next_encoding_overhead; - u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, &u->raw_memchunk); + pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } @@ -482,6 +482,9 @@ void pa__done(pa_module*m) { if (u->raw_memchunk.memblock) pa_memblock_unref(u->raw_memchunk.memblock); + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + if (u->raop) pa_raop_client_free(u->raop); -- cgit From be73d378f5fd5ea1aedcc75b0c7c1e1a82a27047 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 11 May 2008 15:43:56 +0000 Subject: unref the raw data memblock before requesting more data. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2408 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 3d0eaeff..79c517aa 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -217,6 +217,10 @@ static void thread_func(void *userdata) { void *p; if (u->raw_memchunk.length <= 0) { + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + pa_memchunk_reset(&u->raw_memchunk); + /* Grab unencoded data */ pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); } -- cgit From 6c1dd6e54b4b5b4213467d156abc9f260c63aaa3 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 26 May 2008 21:04:45 +0000 Subject: Move the encoding loop around a bit such that it does not grab the data and keep it for the next loop iteration. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2481 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 79c517aa..e17198cd 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -216,17 +216,19 @@ static void thread_func(void *userdata) { ssize_t l; void *p; - if (u->raw_memchunk.length <= 0) { - if (u->raw_memchunk.memblock) - pa_memblock_unref(u->raw_memchunk.memblock); - pa_memchunk_reset(&u->raw_memchunk); - - /* Grab unencoded data */ - pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); - } - pa_assert(u->raw_memchunk.length > 0); - if (u->encoded_memchunk.length <= 0) { + if (u->raw_memchunk.length <= 0) { + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + pa_memchunk_reset(&u->raw_memchunk); + + /* Grab unencoded data */ + pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); + p = pa_memblock_acquire(u->raw_memchunk.memblock); + pa_memblock_release(u->raw_memchunk.memblock); + } + pa_assert(u->raw_memchunk.length > 0); + /* Encode it */ size_t rl = u->raw_memchunk.length; u->encoding_overhead += u->next_encoding_overhead; -- cgit From 8108121fa76e51c36772a592f74b075dcaf7c4cb Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 26 May 2008 21:10:08 +0000 Subject: Set forgotten keyword property git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2483 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index e17198cd..42092e06 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -1,4 +1,4 @@ -/* $Id: module-esound-sink.c 2043 2007-11-09 18:25:40Z lennart $ */ +/* $Id$ */ /*** This file is part of PulseAudio. -- cgit From 13bc07587564977a64222f5a4075b7fa63ac5548 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 26 May 2008 23:43:51 +0000 Subject: A few related changes: * Change the encode_sample routine to simply return normal memchunks allocated from the mempool. * unref the memchunks returned from encode_sample when we are done with them. * Create an encoded 'silence' sample and play this at all times to prevent hangup and to 'hog' the airtunes device This now works and can be used as a regular sink albeit with a constant latency of about 8 seconds :s git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2485 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 79 +++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 21 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 42092e06..f78abbae 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -178,7 +178,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse 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; + pollfd->events = POLLOUT; + pollfd->revents = 0; return 0; } @@ -190,6 +191,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0; + pa_memchunk silence; + uint32_t silence_overhead; + double silence_ratio; pa_assert(u); @@ -200,6 +204,9 @@ static void thread_func(void *userdata) { pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + /* Create a chunk of memory that is our encoded silence sample. */ + pa_memchunk_reset(&silence); + for (;;) { int ret; @@ -208,33 +215,61 @@ 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 (pollfd->revents) { pa_usec_t usec; int64_t n; + void *p; + + if (!silence.memblock) { + pa_memchunk silence_tmp; + + pa_memchunk_reset(&silence_tmp); + silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); + silence_tmp.length = 4096; + p = pa_memblock_acquire(silence_tmp.memblock); + memset(p, 0, 4096); + pa_memblock_release(silence_tmp.memblock); + pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); + pa_assert(0 == silence_tmp.length); + silence_overhead = silence_tmp.length - 4096; + silence_ratio = silence_tmp.length / 4096; + pa_memblock_unref(silence_tmp.memblock); + } for (;;) { ssize_t l; - void *p; if (u->encoded_memchunk.length <= 0) { - if (u->raw_memchunk.length <= 0) { - if (u->raw_memchunk.memblock) - pa_memblock_unref(u->raw_memchunk.memblock); - pa_memchunk_reset(&u->raw_memchunk); - - /* Grab unencoded data */ - pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); - p = pa_memblock_acquire(u->raw_memchunk.memblock); - pa_memblock_release(u->raw_memchunk.memblock); + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + if (PA_SINK_OPENED(u->sink->thread_info.state)) { + size_t rl; + + /* We render real data */ + if (u->raw_memchunk.length <= 0) { + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + pa_memchunk_reset(&u->raw_memchunk); + + /* Grab unencoded data */ + pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); + } + pa_assert(u->raw_memchunk.length > 0); + + /* Encode it */ + rl = u->raw_memchunk.length; + u->encoding_overhead += u->next_encoding_overhead; + pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); + u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); + u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); + } else { + /* We render some silence into our memchunk */ + u->encoding_overhead += u->next_encoding_overhead; + memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); + pa_memblock_ref(silence.memblock); + u->next_encoding_overhead = silence_overhead; + u->encoding_ratio = silence_ratio; } - pa_assert(u->raw_memchunk.length > 0); - - /* Encode it */ - size_t rl = u->raw_memchunk.length; - u->encoding_overhead += u->next_encoding_overhead; - pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); - u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); - u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } pa_assert(u->encoded_memchunk.length > 0); @@ -303,7 +338,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_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; */ } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) @@ -331,6 +366,8 @@ fail: pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: + if (silence.memblock) + pa_memblock_unref(silence.memblock); pa_log_debug("Thread shutting down"); } -- cgit From 7f0cf0c9adc5176ba41d549754ea25ed6f12bdce Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Tue, 3 Jun 2008 23:07:48 +0000 Subject: Fix up a couple of values related to encoding overhead. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2497 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index f78abbae..96c98a6f 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -317,13 +317,13 @@ static void thread_func(void *userdata) { * fully filled up. This is the best time to estimate * the playback position of the server */ - n = u->offset; + n = u->offset - u->encoding_overhead; #ifdef SIOCOUTQ { int l; if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) - n -= l; + n -= (l / u->encoding_ratio); } #endif -- cgit From 15e8420a251ec4912d2df9d29dfb728bdc97f33c Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Wed, 11 Jun 2008 00:02:10 +0000 Subject: Still send silence when we are not doing anything else, but also flush the buffers correctly upon recovery from suspension. Close the RTP socket correctly after passing messages about. When not sending silence, the RTSP socket will be closed after some period of inactivity. I'm not sure why this is. Sending silence keeps things working and with the flushes after suspension we now get a better latency. As this relies on the auto-suspend feature, it's not exactly ideal. Typical latencies are currently about 3s which makes it more or less usuable for listening to music. If the connection is disconnected, it will reconnect but I've found that the second connection is silent. Hopefully the silence will prevent the first connection dropping. Refs #69 git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2504 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 111 ++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 29 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 96c98a6f..51c2368a 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -127,9 +127,31 @@ static const char* const valid_modargs[] = { }; enum { - SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX + SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX, + SINK_MESSAGE_RIP_SOCKET }; +static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { + struct userdata *u = userdata; + pa_assert(u); + + pa_assert(u->fd < 0); + u->fd = fd; + + pa_log_debug("Connection authenticated, handing fd to IO thread..."); + + pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); +} + +static void on_close(void*userdata) { + struct userdata *u = userdata; + pa_assert(u); + + pa_log_debug("Connection closed, informing IO thread..."); + + pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL); +} + static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -143,14 +165,27 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); pa_smoother_pause(u->smoother, pa_rtclock_usec()); + + /* Issue a FLUSH if we are connected */ + if (u->fd >= 0) { + pa_raop_flush(u->raop); + } break; case PA_SINK_IDLE: case PA_SINK_RUNNING: - if (u->sink->thread_info.state == PA_SINK_SUSPENDED) + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { pa_smoother_resume(u->smoother, pa_rtclock_usec()); + /* The connection can be closed when idle, so check to + see if we need to reestablish it */ + if (u->fd < 0) + pa_raop_connect(u->raop); + else + pa_raop_flush(u->raop); + } + break; case PA_SINK_UNLINKED: @@ -179,8 +214,34 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->fd = u->fd; pollfd->events = POLLOUT; - pollfd->revents = 0; + /*pollfd->events = */pollfd->revents = 0; + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + /* Our stream has been suspended so we just flush it.... */ + pa_raop_flush(u->raop); + } + return 0; + } + + case SINK_MESSAGE_RIP_SOCKET: { + pa_assert(u->fd >= 0); + pa_close(u->fd); + u->fd = -1; + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + + pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later"); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; + } else { + /* Quesiton: is this valid here: or should we do some sort of: + return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); + ?? */ + pa_module_unload_request(u->module); + } return 0; } } @@ -215,7 +276,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 (pollfd->revents) { + if (/*PA_SINK_OPENED(u->sink->thread_info.state) && */pollfd->revents) { pa_usec_t usec; int64_t n; void *p; @@ -264,9 +325,10 @@ static void thread_func(void *userdata) { u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } else { /* We render some silence into our memchunk */ - u->encoding_overhead += u->next_encoding_overhead; memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); pa_memblock_ref(silence.memblock); + + /* Calculate/store some values to be used with the smoother */ u->next_encoding_overhead = silence_overhead; u->encoding_ratio = silence_ratio; } @@ -302,12 +364,15 @@ static void thread_func(void *userdata) { pollfd->revents = 0; - if (u->encoded_memchunk.length > 0) + if (u->encoded_memchunk.length > 0) { + /* we've completely written the encoded data, so update our overhead */ + u->encoding_overhead += u->next_encoding_overhead; /* OK, we wrote less that we asked for, * hence we can assume that the socket * buffers are full now */ goto filled_up; + } } } @@ -338,7 +403,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 = POLLOUT; /*PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) @@ -353,8 +418,16 @@ static void thread_func(void *userdata) { pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~POLLOUT) { - pa_log("FIFO shutdown."); - goto fail; + if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { + pa_log("FIFO shutdown."); + goto fail; + } + + /* We expect this to happen on occasion if we are not sending data. + It's perfectly natural and normal and natural */ + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; } } } @@ -371,26 +444,6 @@ finish: pa_log_debug("Thread shutting down"); } -static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { - struct userdata *u = userdata; - pa_assert(u); - - pa_assert(u->fd < 0); - u->fd = fd; - - pa_log_debug("Connection authenticated, handing fd to IO thread..."); - - pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); -} - -static void on_close(void*userdata) { - struct userdata *u = userdata; - pa_assert(u); - - pa_log_debug("Control connection closed."); - pa_module_unload_request(u->module); -} - int pa__init(pa_module*m) { struct userdata *u = NULL; const char *p; -- cgit From d997420791cb8defbda65acdf08fe18072d01d7b Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Wed, 11 Jun 2008 22:43:27 +0000 Subject: Minor correction of help text git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2518 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 51c2368a..545266d5 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -73,7 +73,7 @@ PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "sink_name= " - "server=
cookie= " + "server=
" "format= " "channels= " "rate="); -- cgit From 0ff75aea053cb8a7af958aa72d99b27cc90becf6 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Wed, 11 Jun 2008 23:01:07 +0000 Subject: Add Lennart back in to Copyright as I copied these files from his originals and was a bit overzealous in changing things ;) git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2520 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-raop-sink.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 545266d5..39374a36 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -3,6 +3,7 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering Copyright 2008 Colin Guthrie PulseAudio is free software; you can redistribute it and/or modify -- cgit From ded09d1f9b00ac793ccc26caea0f8706e88bd521 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 3 Jul 2008 23:49:01 +0100 Subject: Implement hardware volume control. This allows near instant change of volume when controlling the hardware but the stream volume still suffers from a sizable delay. --- src/modules/module-raop-sink.c | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 39374a36..50ef985e 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -102,6 +102,9 @@ struct userdata { pa_usec_t latency; + pa_volume_t volume; + pa_bool_t muted; + /*esd_format_t format;*/ int32_t rate; @@ -139,6 +142,9 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { pa_assert(u->fd < 0); u->fd = fd; + /* Set the initial volume */ + pa_raop_client_set_volume(u->raop, u->volume); + pa_log_debug("Connection authenticated, handing fd to IO thread..."); pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); @@ -250,12 +256,68 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return pa_sink_process_msg(o, code, data, offset, chunk); } +static int sink_get_volume_cb(pa_sink *s) { + struct userdata *u = s->userdata; + int i; + + pa_assert(u); + + for (i = 0; i < s->sample_spec.channels; i++) { + s->volume.values[i] = u->volume; + } + + return 0; +} + +static int sink_set_volume_cb(pa_sink *s) { + struct userdata *u = s->userdata; + int rv; + + pa_assert(u); + + /* If we're muted, we fake it */ + if (u->muted) + return 0; + + pa_assert(s->sample_spec.channels > 0); + + /* Avoid pointless volume sets */ + if (u->volume == s->volume.values[0]) + return 0; + + rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]); + if (0 == rv) + u->volume = s->volume.values[0]; + + return rv; +} + +static int sink_get_mute_cb(pa_sink *s) { + struct userdata *u = s->userdata; + + pa_assert(u); + + s->muted = u->muted; + return 0; +} + +static int sink_set_mute_cb(pa_sink *s) { + struct userdata *u = s->userdata; + int rv; + + pa_assert(u); + + rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); + u->muted = s->muted; + return rv; +} + static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0; pa_memchunk silence; - uint32_t silence_overhead; - double silence_ratio; + uint32_t silence_overhead = 0; + double silence_ratio = 0; pa_assert(u); @@ -484,6 +546,9 @@ int pa__init(pa_module*m) { u->next_encoding_overhead = 0; u->encoding_ratio = 1.0; + u->volume = roundf(0.7 * PA_VOLUME_NORM); + u->muted = 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); @@ -508,7 +573,11 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK; + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->get_mute = sink_get_mute_cb; + u->sink->set_mute = sink_set_mute_cb; + u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); -- cgit From 19d28319733f92cc518d0d45a15f15b704f603d8 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 3 Aug 2008 20:52:35 +0100 Subject: Make module-raop-sink/discover work with 0.9.11 API --- src/modules/module-raop-sink.c | 52 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 50ef985e..b90d4e23 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -169,7 +167,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()); @@ -334,12 +332,16 @@ static void thread_func(void *userdata) { for (;;) { int ret; + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (u->sink->thread_info.rewind_requested) + pa_sink_process_rewind(u->sink, 0); + if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ - if (/*PA_SINK_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; void *p; @@ -366,7 +368,7 @@ static void thread_func(void *userdata) { if (u->encoded_memchunk.length <= 0) { if (u->encoded_memchunk.memblock) pa_memblock_unref(u->encoded_memchunk.memblock); - if (PA_SINK_OPENED(u->sink->thread_info.state)) { + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { size_t rl; /* We render real data */ @@ -466,7 +468,7 @@ static void thread_func(void *userdata) { } /* Hmm, nothing to do. Let's sleep */ - pollfd->events = POLLOUT; /*PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ + pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) @@ -509,10 +511,10 @@ finish: 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 *server; + pa_sink_new_data data; pa_assert(m); @@ -538,7 +540,7 @@ 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->raw_memchunk); pa_memchunk_reset(&u->encoded_memchunk); u->offset = 0; @@ -549,9 +551,8 @@ int pa__init(pa_module*m) { u->volume = roundf(0.7 * PA_VOLUME_NORM); u->muted = 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->rtpoll_item = NULL; /*u->format = @@ -566,7 +567,23 @@ 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))) { + if (!(server = pa_modargs_get_value(ma, "server", NULL))) { + pa_log("No server argument given."); + goto fail; + } + + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Airtunes sink '%s'", server); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } @@ -579,25 +596,16 @@ int pa__init(pa_module*m) { u->sink->set_mute = sink_set_mute_cb; u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - if (!(p = pa_modargs_get_value(ma, "server", NULL))) { - pa_log("No server argument given."); - goto fail; - } - - if (!(u->raop = pa_raop_client_new(u->core, p))) { + if (!(u->raop = pa_raop_client_new(u->core, server))) { pa_log("Failed to connect to server."); goto fail; } pa_raop_client_set_callback(u->raop, on_connection, u); pa_raop_client_set_closed_callback(u->raop, on_close, u); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p)); - pa_xfree(t); - if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); -- cgit From 59eb64987fdf5dde71638f5f77e705e490ec7133 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 22 Aug 2008 09:51:41 +0100 Subject: Follow master change r34dd4a and fix shutdown when --disallow-module-loading=1 is passed --- src/modules/module-raop-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules/module-raop-sink.c') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index b90d4e23..62f0a73c 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -245,7 +245,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse /* Quesiton: is this valid here: or should we do some sort of: return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); ?? */ - pa_module_unload_request(u->module); + pa_module_unload_request(u->module, TRUE); } return 0; } -- cgit