1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2008 Lennart Poettering 5 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published 9 by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <stdlib.h> 26#include <stdio.h> 27#include <errno.h> 28#include <unistd.h> 29 30#include <pulse/rtclock.h> 31#include <pulse/timeval.h> 32#include <pulse/util.h> 33#include <pulse/xmalloc.h> 34 35#include <pulsecore/core-util.h> 36#include <pulsecore/log.h> 37#include <pulsecore/macro.h> 38#include <pulsecore/modargs.h> 39#include <pulsecore/module.h> 40#include <pulsecore/rtpoll.h> 41#include <pulsecore/source.h> 42#include <pulsecore/thread-mq.h> 43#include <pulsecore/thread.h> 44 45PA_MODULE_AUTHOR("Lennart Poettering & Marc-Andre Lureau"); 46PA_MODULE_DESCRIPTION("Clocked NULL source"); 47PA_MODULE_VERSION(PACKAGE_VERSION); 48PA_MODULE_LOAD_ONCE(false); 49PA_MODULE_USAGE( 50 "format=<sample format> " 51 "channels=<number of channels> " 52 "rate=<sample rate> " 53 "source_name=<name of source> " 54 "channel_map=<channel map> " 55 "max_latency_msec=<maximum latency in ms> " 56 "description=<description for the source> "); 57 58#define DEFAULT_SOURCE_NAME "source.null" 59#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2) 60#define MIN_LATENCY_USEC (500) 61 62struct userdata { 63 pa_core *core; 64 pa_module *module; 65 pa_source *source; 66 67 pa_thread *thread; 68 pa_thread_mq thread_mq; 69 pa_rtpoll *rtpoll; 70 71 size_t block_size; 72 73 pa_usec_t block_usec; 74 pa_usec_t timestamp; 75}; 76 77static const char* const valid_modargs[] = { 78 "rate", 79 "format", 80 "channels", 81 "source_name", 82 "channel_map", 83 "max_latency_msec", 84 "description", 85 NULL 86}; 87 88static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 89 struct userdata *u = PA_SOURCE(o)->userdata; 90 91 switch (code) { 92 case PA_SOURCE_MESSAGE_GET_LATENCY: { 93 pa_usec_t now; 94 95 now = pa_rtclock_now(); 96 *((int64_t*) data) = (int64_t)now - (int64_t)u->timestamp; 97 98 return 0; 99 } 100 } 101 102 return pa_source_process_msg(o, code, data, offset, chunk); 103} 104 105/* Called from the IO thread. */ 106static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 107 struct userdata *u; 108 109 pa_assert(s); 110 pa_assert_se(u = s->userdata); 111 112 if (s->thread_info.state == PA_SOURCE_SUSPENDED || s->thread_info.state == PA_SOURCE_INIT) { 113 if (PA_SOURCE_IS_OPENED(new_state)) 114 u->timestamp = pa_rtclock_now(); 115 } 116 117 return 0; 118} 119 120static void source_update_requested_latency_cb(pa_source *s) { 121 struct userdata *u; 122 123 pa_source_assert_ref(s); 124 u = s->userdata; 125 pa_assert(u); 126 127 u->block_usec = pa_source_get_requested_latency_within_thread(s); 128 129 if (u->block_usec == (pa_usec_t)-1) 130 u->block_usec = u->source->thread_info.max_latency; 131 132 pa_source_set_max_rewind_within_thread(s, pa_usec_to_bytes(u->block_usec, &u->source->sample_spec)); 133} 134 135static void thread_func(void *userdata) { 136 struct userdata *u = userdata; 137 bool timer_elapsed = false; 138 size_t max_block_size; 139 140 pa_assert(u); 141 142 pa_log_debug("Thread starting up"); 143 144 if (u->core->realtime_scheduling) 145 pa_thread_make_realtime(u->core->realtime_priority); 146 147 pa_thread_mq_install(&u->thread_mq); 148 149 max_block_size = pa_frame_align(pa_mempool_block_size_max(u->core->mempool), &u->source->sample_spec); 150 u->timestamp = pa_rtclock_now(); 151 152 for (;;) { 153 int ret; 154 155 /* Generate some null data */ 156 if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { 157 pa_usec_t now; 158 pa_memchunk chunk; 159 160 now = pa_rtclock_now(); 161 162 if (timer_elapsed && (chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) { 163 164 chunk.length = PA_MIN(max_block_size, chunk.length); 165 166 chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); 167 chunk.index = 0; 168 pa_silence_memchunk(&chunk, &u->source->sample_spec); 169 pa_source_post(u->source, &chunk); 170 pa_memblock_unref(chunk.memblock); 171 172 u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); 173 } 174 175 pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->block_usec); 176 } else 177 pa_rtpoll_set_timer_disabled(u->rtpoll); 178 179 /* Hmm, nothing to do. Let's sleep */ 180 if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) 181 goto fail; 182 183 timer_elapsed = pa_rtpoll_timer_elapsed(u->rtpoll); 184 185 if (ret == 0) 186 goto finish; 187 } 188 189fail: 190 /* If this was no regular exit from the loop we have to continue 191 * processing messages until we received PA_MESSAGE_SHUTDOWN */ 192 pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); 193 pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 194 195finish: 196 pa_log_debug("Thread shutting down"); 197} 198 199int pa__init(pa_module*m) { 200 struct userdata *u = NULL; 201 pa_sample_spec ss; 202 pa_channel_map map; 203 pa_modargs *ma = NULL; 204 pa_source_new_data data; 205 uint32_t max_latency_msec; 206 207 pa_assert(m); 208 209 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 210 pa_log("Failed to parse module arguments."); 211 goto fail; 212 } 213 214 ss = m->core->default_sample_spec; 215 map = m->core->default_channel_map; 216 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { 217 pa_log("Invalid sample format specification or channel map"); 218 goto fail; 219 } 220 221 m->userdata = u = pa_xnew0(struct userdata, 1); 222 u->core = m->core; 223 u->module = m; 224 u->rtpoll = pa_rtpoll_new(); 225 226 if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { 227 pa_log("pa_thread_mq_init() failed."); 228 goto fail; 229 } 230 231 pa_source_new_data_init(&data); 232 data.driver = __FILE__; 233 data.module = m; 234 pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); 235 pa_source_new_data_set_sample_spec(&data, &ss); 236 pa_source_new_data_set_channel_map(&data, &map); 237 pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Input")); 238 pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); 239 240 u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY); 241 pa_source_new_data_done(&data); 242 243 if (!u->source) { 244 pa_log("Failed to create source object."); 245 goto fail; 246 } 247 248 u->source->parent.process_msg = source_process_msg; 249 u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; 250 u->source->update_requested_latency = source_update_requested_latency_cb; 251 u->source->userdata = u; 252 253 pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); 254 pa_source_set_rtpoll(u->source, u->rtpoll); 255 256 max_latency_msec = MAX_LATENCY_USEC / PA_USEC_PER_MSEC; 257 if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec) < 0) { 258 pa_log("Failed to get max_latency_msec."); 259 goto fail; 260 } 261 262 pa_source_set_latency_range(u->source, MIN_LATENCY_USEC, max_latency_msec * PA_USEC_PER_MSEC); 263 264 u->block_usec = u->source->thread_info.max_latency; 265 266 u->source->thread_info.max_rewind = 267 pa_usec_to_bytes(u->block_usec, &u->source->sample_spec); 268 269 if (!(u->thread = pa_thread_new("null-source", thread_func, u))) { 270 pa_log("Failed to create thread."); 271 goto fail; 272 } 273 274 pa_source_put(u->source); 275 276 pa_modargs_free(ma); 277 278 return 0; 279 280fail: 281 if (ma) 282 pa_modargs_free(ma); 283 284 pa__done(m); 285 286 return -1; 287} 288 289void pa__done(pa_module*m) { 290 struct userdata *u; 291 292 pa_assert(m); 293 294 if (!(u = m->userdata)) 295 return; 296 297 if (u->source) 298 pa_source_unlink(u->source); 299 300 if (u->thread) { 301 pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 302 pa_thread_free(u->thread); 303 } 304 305 pa_thread_mq_done(&u->thread_mq); 306 307 if (u->source) 308 pa_source_unref(u->source); 309 310 if (u->rtpoll) 311 pa_rtpoll_free(u->rtpoll); 312 313 pa_xfree(u); 314} 315