153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdlib.h> 2553a5a1b3Sopenharmony_ci#include <stdio.h> 2653a5a1b3Sopenharmony_ci#include <errno.h> 2753a5a1b3Sopenharmony_ci#include <unistd.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 3053a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3153a5a1b3Sopenharmony_ci#include <pulse/util.h> 3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 4453a5a1b3Sopenharmony_ci 4553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 4653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION(_("Clocked NULL sink")); 4753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 4853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 4953a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 5053a5a1b3Sopenharmony_ci "sink_name=<name of sink> " 5153a5a1b3Sopenharmony_ci "sink_properties=<properties for the sink> " 5253a5a1b3Sopenharmony_ci "format=<sample format> " 5353a5a1b3Sopenharmony_ci "rate=<sample rate> " 5453a5a1b3Sopenharmony_ci "channels=<number of channels> " 5553a5a1b3Sopenharmony_ci "channel_map=<channel map>" 5653a5a1b3Sopenharmony_ci "formats=<semi-colon separated sink formats>" 5753a5a1b3Sopenharmony_ci "norewinds=<disable rewinds>"); 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "null" 6053a5a1b3Sopenharmony_ci#define BLOCK_USEC (2 * PA_USEC_PER_SEC) 6153a5a1b3Sopenharmony_ci#define BLOCK_USEC_NOREWINDS (50 * PA_USEC_PER_MSEC) 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_cistruct userdata { 6453a5a1b3Sopenharmony_ci pa_core *core; 6553a5a1b3Sopenharmony_ci pa_module *module; 6653a5a1b3Sopenharmony_ci pa_sink *sink; 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci pa_thread *thread; 6953a5a1b3Sopenharmony_ci pa_thread_mq thread_mq; 7053a5a1b3Sopenharmony_ci pa_rtpoll *rtpoll; 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci pa_usec_t block_usec; 7353a5a1b3Sopenharmony_ci pa_usec_t timestamp; 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci pa_idxset *formats; 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci bool norewinds; 7853a5a1b3Sopenharmony_ci}; 7953a5a1b3Sopenharmony_ci 8053a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 8153a5a1b3Sopenharmony_ci "sink_name", 8253a5a1b3Sopenharmony_ci "sink_properties", 8353a5a1b3Sopenharmony_ci "format", 8453a5a1b3Sopenharmony_ci "rate", 8553a5a1b3Sopenharmony_ci "channels", 8653a5a1b3Sopenharmony_ci "channel_map", 8753a5a1b3Sopenharmony_ci "formats", 8853a5a1b3Sopenharmony_ci "norewinds", 8953a5a1b3Sopenharmony_ci NULL 9053a5a1b3Sopenharmony_ci}; 9153a5a1b3Sopenharmony_ci 9253a5a1b3Sopenharmony_cistatic int sink_process_msg( 9353a5a1b3Sopenharmony_ci pa_msgobject *o, 9453a5a1b3Sopenharmony_ci int code, 9553a5a1b3Sopenharmony_ci void *data, 9653a5a1b3Sopenharmony_ci int64_t offset, 9753a5a1b3Sopenharmony_ci pa_memchunk *chunk) { 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci switch (code) { 10253a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: { 10353a5a1b3Sopenharmony_ci pa_usec_t now; 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ci now = pa_rtclock_now(); 10653a5a1b3Sopenharmony_ci *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now; 10753a5a1b3Sopenharmony_ci 10853a5a1b3Sopenharmony_ci return 0; 10953a5a1b3Sopenharmony_ci } 11053a5a1b3Sopenharmony_ci } 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 11353a5a1b3Sopenharmony_ci} 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 11653a5a1b3Sopenharmony_cistatic void sink_recalculate_max_request_and_rewind(pa_sink *s) { 11753a5a1b3Sopenharmony_ci struct userdata *u; 11853a5a1b3Sopenharmony_ci size_t nbytes; 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 12153a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); 12453a5a1b3Sopenharmony_ci 12553a5a1b3Sopenharmony_ci if (u->norewinds) { 12653a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(s, 0); 12753a5a1b3Sopenharmony_ci } else { 12853a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(s, nbytes); 12953a5a1b3Sopenharmony_ci } 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(s, nbytes); 13253a5a1b3Sopenharmony_ci} 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 13553a5a1b3Sopenharmony_cistatic int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 13653a5a1b3Sopenharmony_ci struct userdata *u; 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci pa_assert(s); 13953a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci if (s->thread_info.state == PA_SINK_SUSPENDED || s->thread_info.state == PA_SINK_INIT) { 14253a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(new_state)) { 14353a5a1b3Sopenharmony_ci u->timestamp = pa_rtclock_now(); 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci /* If sink was suspended to change sample formats, both 14653a5a1b3Sopenharmony_ci * thread_info.max_request and thread_info.max_rewind 14753a5a1b3Sopenharmony_ci * must be updated before first block is rendered 14853a5a1b3Sopenharmony_ci */ 14953a5a1b3Sopenharmony_ci sink_recalculate_max_request_and_rewind(s); 15053a5a1b3Sopenharmony_ci } 15153a5a1b3Sopenharmony_ci } 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci return 0; 15453a5a1b3Sopenharmony_ci} 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 15753a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) { 15853a5a1b3Sopenharmony_ci struct userdata *u; 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 16153a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 16253a5a1b3Sopenharmony_ci 16353a5a1b3Sopenharmony_ci u->block_usec = pa_sink_get_requested_latency_within_thread(s); 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci if (u->block_usec == (pa_usec_t) -1) 16653a5a1b3Sopenharmony_ci u->block_usec = s->thread_info.max_latency; 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci sink_recalculate_max_request_and_rewind(s); 16953a5a1b3Sopenharmony_ci} 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_cistatic void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) { 17253a5a1b3Sopenharmony_ci /* We don't need to do anything */ 17353a5a1b3Sopenharmony_ci s->sample_spec = *spec; 17453a5a1b3Sopenharmony_ci} 17553a5a1b3Sopenharmony_ci 17653a5a1b3Sopenharmony_cistatic bool sink_set_formats_cb(pa_sink *s, pa_idxset *formats) { 17753a5a1b3Sopenharmony_ci struct userdata *u = s->userdata; 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci pa_assert(u); 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_ci pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free); 18253a5a1b3Sopenharmony_ci u->formats = pa_idxset_copy(formats, (pa_copy_func_t) pa_format_info_copy); 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci return true; 18553a5a1b3Sopenharmony_ci} 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_cistatic pa_idxset* sink_get_formats_cb(pa_sink *s) { 18853a5a1b3Sopenharmony_ci struct userdata *u = s->userdata; 18953a5a1b3Sopenharmony_ci 19053a5a1b3Sopenharmony_ci pa_assert(u); 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci return pa_idxset_copy(u->formats, (pa_copy_func_t) pa_format_info_copy); 19353a5a1b3Sopenharmony_ci} 19453a5a1b3Sopenharmony_ci 19553a5a1b3Sopenharmony_cistatic void process_rewind(struct userdata *u, pa_usec_t now) { 19653a5a1b3Sopenharmony_ci size_t rewind_nbytes, in_buffer; 19753a5a1b3Sopenharmony_ci pa_usec_t delay; 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci pa_assert(u); 20053a5a1b3Sopenharmony_ci 20153a5a1b3Sopenharmony_ci rewind_nbytes = u->sink->thread_info.rewind_nbytes; 20253a5a1b3Sopenharmony_ci 20353a5a1b3Sopenharmony_ci if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0) 20453a5a1b3Sopenharmony_ci goto do_nothing; 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci if (u->timestamp <= now) 20953a5a1b3Sopenharmony_ci goto do_nothing; 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci delay = u->timestamp - now; 21253a5a1b3Sopenharmony_ci in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_ci if (in_buffer <= 0) 21553a5a1b3Sopenharmony_ci goto do_nothing; 21653a5a1b3Sopenharmony_ci 21753a5a1b3Sopenharmony_ci if (rewind_nbytes > in_buffer) 21853a5a1b3Sopenharmony_ci rewind_nbytes = in_buffer; 21953a5a1b3Sopenharmony_ci 22053a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, rewind_nbytes); 22153a5a1b3Sopenharmony_ci u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); 22253a5a1b3Sopenharmony_ci 22353a5a1b3Sopenharmony_ci pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); 22453a5a1b3Sopenharmony_ci return; 22553a5a1b3Sopenharmony_ci 22653a5a1b3Sopenharmony_cido_nothing: 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 22953a5a1b3Sopenharmony_ci} 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_cistatic void process_render(struct userdata *u, pa_usec_t now) { 23253a5a1b3Sopenharmony_ci size_t ate = 0; 23353a5a1b3Sopenharmony_ci 23453a5a1b3Sopenharmony_ci pa_assert(u); 23553a5a1b3Sopenharmony_ci 23653a5a1b3Sopenharmony_ci /* This is the configured latency. Sink inputs connected to us 23753a5a1b3Sopenharmony_ci might not have a single frame more than the maxrequest value 23853a5a1b3Sopenharmony_ci queued. Hence: at maximum read this many bytes from the sink 23953a5a1b3Sopenharmony_ci inputs. */ 24053a5a1b3Sopenharmony_ci 24153a5a1b3Sopenharmony_ci /* Fill the buffer up the latency size */ 24253a5a1b3Sopenharmony_ci while (u->timestamp < now + u->block_usec) { 24353a5a1b3Sopenharmony_ci pa_memchunk chunk; 24453a5a1b3Sopenharmony_ci size_t request_size; 24553a5a1b3Sopenharmony_ci 24653a5a1b3Sopenharmony_ci request_size = pa_usec_to_bytes(now + u->block_usec - u->timestamp, &u->sink->sample_spec); 24753a5a1b3Sopenharmony_ci request_size = PA_MIN(request_size, u->sink->thread_info.max_request); 24853a5a1b3Sopenharmony_ci pa_sink_render(u->sink, request_size, &chunk); 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci pa_memblock_unref(chunk.memblock); 25153a5a1b3Sopenharmony_ci 25253a5a1b3Sopenharmony_ci/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */ 25353a5a1b3Sopenharmony_ci u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci ate += chunk.length; 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci if (ate >= u->sink->thread_info.max_request) 25853a5a1b3Sopenharmony_ci break; 25953a5a1b3Sopenharmony_ci } 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_ci/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */ 26253a5a1b3Sopenharmony_ci} 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) { 26553a5a1b3Sopenharmony_ci struct userdata *u = userdata; 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_ci pa_assert(u); 26853a5a1b3Sopenharmony_ci 26953a5a1b3Sopenharmony_ci pa_log_debug("Thread starting up"); 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci if (u->core->realtime_scheduling) 27253a5a1b3Sopenharmony_ci pa_thread_make_realtime(u->core->realtime_priority); 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_ci pa_thread_mq_install(&u->thread_mq); 27553a5a1b3Sopenharmony_ci 27653a5a1b3Sopenharmony_ci u->timestamp = pa_rtclock_now(); 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci for (;;) { 27953a5a1b3Sopenharmony_ci pa_usec_t now = 0; 28053a5a1b3Sopenharmony_ci int ret; 28153a5a1b3Sopenharmony_ci 28253a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) 28353a5a1b3Sopenharmony_ci now = pa_rtclock_now(); 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) 28653a5a1b3Sopenharmony_ci process_rewind(u, now); 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci /* Render some data and drop it immediately */ 28953a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { 29053a5a1b3Sopenharmony_ci if (u->timestamp <= now) 29153a5a1b3Sopenharmony_ci process_render(u, now); 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); 29453a5a1b3Sopenharmony_ci } else 29553a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_disabled(u->rtpoll); 29653a5a1b3Sopenharmony_ci 29753a5a1b3Sopenharmony_ci /* Hmm, nothing to do. Let's sleep */ 29853a5a1b3Sopenharmony_ci if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) 29953a5a1b3Sopenharmony_ci goto fail; 30053a5a1b3Sopenharmony_ci 30153a5a1b3Sopenharmony_ci if (ret == 0) 30253a5a1b3Sopenharmony_ci goto finish; 30353a5a1b3Sopenharmony_ci } 30453a5a1b3Sopenharmony_ci 30553a5a1b3Sopenharmony_cifail: 30653a5a1b3Sopenharmony_ci /* If this was no regular exit from the loop we have to continue 30753a5a1b3Sopenharmony_ci * processing messages until we received PA_MESSAGE_SHUTDOWN */ 30853a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); 30953a5a1b3Sopenharmony_ci pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 31053a5a1b3Sopenharmony_ci 31153a5a1b3Sopenharmony_cifinish: 31253a5a1b3Sopenharmony_ci pa_log_debug("Thread shutting down"); 31353a5a1b3Sopenharmony_ci} 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 31653a5a1b3Sopenharmony_ci struct userdata *u = NULL; 31753a5a1b3Sopenharmony_ci pa_sample_spec ss; 31853a5a1b3Sopenharmony_ci pa_channel_map map; 31953a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 32053a5a1b3Sopenharmony_ci pa_sink_new_data data; 32153a5a1b3Sopenharmony_ci pa_format_info *format; 32253a5a1b3Sopenharmony_ci const char *formats; 32353a5a1b3Sopenharmony_ci size_t nbytes; 32453a5a1b3Sopenharmony_ci 32553a5a1b3Sopenharmony_ci pa_assert(m); 32653a5a1b3Sopenharmony_ci 32753a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 32853a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 32953a5a1b3Sopenharmony_ci goto fail; 33053a5a1b3Sopenharmony_ci } 33153a5a1b3Sopenharmony_ci 33253a5a1b3Sopenharmony_ci ss = m->core->default_sample_spec; 33353a5a1b3Sopenharmony_ci map = m->core->default_channel_map; 33453a5a1b3Sopenharmony_ci if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { 33553a5a1b3Sopenharmony_ci pa_log("Invalid sample format specification or channel map"); 33653a5a1b3Sopenharmony_ci goto fail; 33753a5a1b3Sopenharmony_ci } 33853a5a1b3Sopenharmony_ci 33953a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 34053a5a1b3Sopenharmony_ci u->core = m->core; 34153a5a1b3Sopenharmony_ci u->module = m; 34253a5a1b3Sopenharmony_ci u->rtpoll = pa_rtpoll_new(); 34353a5a1b3Sopenharmony_ci u->block_usec = BLOCK_USEC; 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_ci if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { 34653a5a1b3Sopenharmony_ci pa_log("pa_thread_mq_init() failed."); 34753a5a1b3Sopenharmony_ci goto fail; 34853a5a1b3Sopenharmony_ci } 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci pa_sink_new_data_init(&data); 35153a5a1b3Sopenharmony_ci data.driver = __FILE__; 35253a5a1b3Sopenharmony_ci data.module = m; 35353a5a1b3Sopenharmony_ci pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); 35453a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&data, &ss); 35553a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&data, &map); 35653a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Null Output")); 35753a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); 35853a5a1b3Sopenharmony_ci 35953a5a1b3Sopenharmony_ci u->formats = pa_idxset_new(NULL, NULL); 36053a5a1b3Sopenharmony_ci if ((formats = pa_modargs_get_value(ma, "formats", NULL))) { 36153a5a1b3Sopenharmony_ci char *f = NULL; 36253a5a1b3Sopenharmony_ci const char *state = NULL; 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci while ((f = pa_split(formats, ";", &state))) { 36553a5a1b3Sopenharmony_ci format = pa_format_info_from_string(pa_strip(f)); 36653a5a1b3Sopenharmony_ci 36753a5a1b3Sopenharmony_ci if (!format) { 36853a5a1b3Sopenharmony_ci pa_log(_("Failed to set format: invalid format string %s"), f); 36953a5a1b3Sopenharmony_ci pa_xfree(f); 37053a5a1b3Sopenharmony_ci goto fail; 37153a5a1b3Sopenharmony_ci } 37253a5a1b3Sopenharmony_ci pa_xfree(f); 37353a5a1b3Sopenharmony_ci 37453a5a1b3Sopenharmony_ci pa_idxset_put(u->formats, format, NULL); 37553a5a1b3Sopenharmony_ci } 37653a5a1b3Sopenharmony_ci } else { 37753a5a1b3Sopenharmony_ci format = pa_format_info_new(); 37853a5a1b3Sopenharmony_ci format->encoding = PA_ENCODING_PCM; 37953a5a1b3Sopenharmony_ci pa_idxset_put(u->formats, format, NULL); 38053a5a1b3Sopenharmony_ci } 38153a5a1b3Sopenharmony_ci 38253a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { 38353a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 38453a5a1b3Sopenharmony_ci pa_sink_new_data_done(&data); 38553a5a1b3Sopenharmony_ci goto fail; 38653a5a1b3Sopenharmony_ci } 38753a5a1b3Sopenharmony_ci 38853a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_SET_FORMATS); 38953a5a1b3Sopenharmony_ci pa_sink_new_data_done(&data); 39053a5a1b3Sopenharmony_ci 39153a5a1b3Sopenharmony_ci if (!u->sink) { 39253a5a1b3Sopenharmony_ci pa_log("Failed to create sink object."); 39353a5a1b3Sopenharmony_ci goto fail; 39453a5a1b3Sopenharmony_ci } 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg; 39753a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 39853a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency_cb; 39953a5a1b3Sopenharmony_ci u->sink->reconfigure = sink_reconfigure_cb; 40053a5a1b3Sopenharmony_ci u->sink->get_formats = sink_get_formats_cb; 40153a5a1b3Sopenharmony_ci u->sink->set_formats = sink_set_formats_cb; 40253a5a1b3Sopenharmony_ci u->sink->userdata = u; 40353a5a1b3Sopenharmony_ci 40453a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); 40553a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, u->rtpoll); 40653a5a1b3Sopenharmony_ci 40753a5a1b3Sopenharmony_ci if(pa_modargs_get_value_boolean(ma, "norewinds", &u->norewinds) < 0){ 40853a5a1b3Sopenharmony_ci pa_log("Invalid argument, norewinds expects a boolean value."); 40953a5a1b3Sopenharmony_ci } 41053a5a1b3Sopenharmony_ci 41153a5a1b3Sopenharmony_ci if (u->norewinds) 41253a5a1b3Sopenharmony_ci u->block_usec = BLOCK_USEC_NOREWINDS; 41353a5a1b3Sopenharmony_ci 41453a5a1b3Sopenharmony_ci nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_ci if(u->norewinds){ 41753a5a1b3Sopenharmony_ci pa_sink_set_max_rewind(u->sink, 0); 41853a5a1b3Sopenharmony_ci } else { 41953a5a1b3Sopenharmony_ci pa_sink_set_max_rewind(u->sink, nbytes); 42053a5a1b3Sopenharmony_ci } 42153a5a1b3Sopenharmony_ci 42253a5a1b3Sopenharmony_ci pa_sink_set_max_request(u->sink, nbytes); 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci if (!(u->thread = pa_thread_new("null-sink", thread_func, u))) { 42553a5a1b3Sopenharmony_ci pa_log("Failed to create thread."); 42653a5a1b3Sopenharmony_ci goto fail; 42753a5a1b3Sopenharmony_ci } 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci pa_sink_set_latency_range(u->sink, 0, u->block_usec); 43053a5a1b3Sopenharmony_ci 43153a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 43453a5a1b3Sopenharmony_ci 43553a5a1b3Sopenharmony_ci return 0; 43653a5a1b3Sopenharmony_ci 43753a5a1b3Sopenharmony_cifail: 43853a5a1b3Sopenharmony_ci if (ma) 43953a5a1b3Sopenharmony_ci pa_modargs_free(ma); 44053a5a1b3Sopenharmony_ci 44153a5a1b3Sopenharmony_ci pa__done(m); 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci return -1; 44453a5a1b3Sopenharmony_ci} 44553a5a1b3Sopenharmony_ci 44653a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 44753a5a1b3Sopenharmony_ci struct userdata *u; 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci pa_assert(m); 45053a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 45153a5a1b3Sopenharmony_ci 45253a5a1b3Sopenharmony_ci return pa_sink_linked_by(u->sink); 45353a5a1b3Sopenharmony_ci} 45453a5a1b3Sopenharmony_ci 45553a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 45653a5a1b3Sopenharmony_ci struct userdata *u; 45753a5a1b3Sopenharmony_ci 45853a5a1b3Sopenharmony_ci pa_assert(m); 45953a5a1b3Sopenharmony_ci 46053a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 46153a5a1b3Sopenharmony_ci return; 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_ci if (u->sink) 46453a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 46553a5a1b3Sopenharmony_ci 46653a5a1b3Sopenharmony_ci if (u->thread) { 46753a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 46853a5a1b3Sopenharmony_ci pa_thread_free(u->thread); 46953a5a1b3Sopenharmony_ci } 47053a5a1b3Sopenharmony_ci 47153a5a1b3Sopenharmony_ci pa_thread_mq_done(&u->thread_mq); 47253a5a1b3Sopenharmony_ci 47353a5a1b3Sopenharmony_ci if (u->sink) 47453a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 47553a5a1b3Sopenharmony_ci 47653a5a1b3Sopenharmony_ci if (u->rtpoll) 47753a5a1b3Sopenharmony_ci pa_rtpoll_free(u->rtpoll); 47853a5a1b3Sopenharmony_ci 47953a5a1b3Sopenharmony_ci if (u->formats) 48053a5a1b3Sopenharmony_ci pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free); 48153a5a1b3Sopenharmony_ci 48253a5a1b3Sopenharmony_ci pa_xfree(u); 48353a5a1b3Sopenharmony_ci} 484