153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2008 Colin Guthrie 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 <pulse/xmalloc.h> 2553a5a1b3Sopenharmony_ci 2653a5a1b3Sopenharmony_ci#include <pulsecore/core.h> 2753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3253a5a1b3Sopenharmony_ci 3353a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Colin Guthrie"); 3453a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION(_("Always keeps at least one sink loaded even if it's a null one")); 3553a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 3653a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true); 3753a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 3853a5a1b3Sopenharmony_ci "sink_name=<name of sink>"); 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "auto_null" 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 4353a5a1b3Sopenharmony_ci "sink_name", 4453a5a1b3Sopenharmony_ci NULL, 4553a5a1b3Sopenharmony_ci}; 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_cistruct userdata { 4853a5a1b3Sopenharmony_ci uint32_t null_module; 4953a5a1b3Sopenharmony_ci bool ignore; 5053a5a1b3Sopenharmony_ci char *sink_name; 5153a5a1b3Sopenharmony_ci}; 5253a5a1b3Sopenharmony_ci 5353a5a1b3Sopenharmony_cistatic void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) { 5453a5a1b3Sopenharmony_ci pa_sink *target; 5553a5a1b3Sopenharmony_ci uint32_t idx; 5653a5a1b3Sopenharmony_ci char *t; 5753a5a1b3Sopenharmony_ci pa_module *m; 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci pa_assert(c); 6053a5a1b3Sopenharmony_ci pa_assert(u); 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci if (u->null_module != PA_INVALID_INDEX) 6353a5a1b3Sopenharmony_ci return; /* We've already got a null-sink loaded */ 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci /* Loop through all sinks and check to see if we have *any* 6653a5a1b3Sopenharmony_ci * sinks. Ignore the sink passed in (if it's not null), and 6753a5a1b3Sopenharmony_ci * don't count filter sinks. */ 6853a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(target, c->sinks, idx) 6953a5a1b3Sopenharmony_ci if (!sink || ((target != sink) && !pa_sink_is_filter(target))) 7053a5a1b3Sopenharmony_ci break; 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci if (target) 7353a5a1b3Sopenharmony_ci return; 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci pa_log_debug("Autoloading null-sink as no other sinks detected."); 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci u->ignore = true; 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci t = pa_sprintf_malloc("sink_name=%s sink_properties='device.description=\"%s\"'", u->sink_name, 8053a5a1b3Sopenharmony_ci _("Dummy Output")); 8153a5a1b3Sopenharmony_ci pa_module_load(&m, c, "module-null-sink", t); 8253a5a1b3Sopenharmony_ci u->null_module = m ? m->index : PA_INVALID_INDEX; 8353a5a1b3Sopenharmony_ci pa_xfree(t); 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_ci u->ignore = false; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci if (!m) 8853a5a1b3Sopenharmony_ci pa_log_warn("Unable to load module-null-sink"); 8953a5a1b3Sopenharmony_ci} 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_cistatic pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { 9253a5a1b3Sopenharmony_ci struct userdata *u = userdata; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci pa_assert(c); 9553a5a1b3Sopenharmony_ci pa_assert(sink); 9653a5a1b3Sopenharmony_ci pa_assert(u); 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci /* This is us detecting ourselves on load... just ignore this. */ 9953a5a1b3Sopenharmony_ci if (u->ignore) 10053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_ci /* There's no point in doing anything if the core is shut down anyway */ 10353a5a1b3Sopenharmony_ci if (c->state == PA_CORE_SHUTDOWN) 10453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_ci /* Auto-loaded null-sink not active, so ignoring newly detected sink. */ 10753a5a1b3Sopenharmony_ci if (u->null_module == PA_INVALID_INDEX) 10853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 10953a5a1b3Sopenharmony_ci 11053a5a1b3Sopenharmony_ci /* This is us detecting ourselves on load in a different way... just ignore this too. */ 11153a5a1b3Sopenharmony_ci if (sink->module && sink->module->index == u->null_module) 11253a5a1b3Sopenharmony_ci return PA_HOOK_OK; 11353a5a1b3Sopenharmony_ci 11453a5a1b3Sopenharmony_ci /* We don't count filter sinks since they need a real sink */ 11553a5a1b3Sopenharmony_ci if (pa_sink_is_filter(sink)) 11653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci pa_log_info("A new sink has been discovered. Unloading null-sink."); 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci pa_module_unload_request_by_index(c, u->null_module, true); 12153a5a1b3Sopenharmony_ci u->null_module = PA_INVALID_INDEX; 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 12453a5a1b3Sopenharmony_ci} 12553a5a1b3Sopenharmony_ci 12653a5a1b3Sopenharmony_cistatic pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { 12753a5a1b3Sopenharmony_ci struct userdata *u = userdata; 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci pa_assert(c); 13053a5a1b3Sopenharmony_ci pa_assert(sink); 13153a5a1b3Sopenharmony_ci pa_assert(u); 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_ci /* First check to see if it's our own null-sink that's been removed... */ 13453a5a1b3Sopenharmony_ci if (u->null_module != PA_INVALID_INDEX && sink->module && sink->module->index == u->null_module) { 13553a5a1b3Sopenharmony_ci pa_log_debug("Autoloaded null-sink removed"); 13653a5a1b3Sopenharmony_ci u->null_module = PA_INVALID_INDEX; 13753a5a1b3Sopenharmony_ci return PA_HOOK_OK; 13853a5a1b3Sopenharmony_ci } 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci /* There's no point in doing anything if the core is shut down anyway */ 14153a5a1b3Sopenharmony_ci if (c->state == PA_CORE_SHUTDOWN) 14253a5a1b3Sopenharmony_ci return PA_HOOK_OK; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci load_null_sink_if_needed(c, sink, u); 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 14753a5a1b3Sopenharmony_ci} 14853a5a1b3Sopenharmony_ci 14953a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 15053a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 15153a5a1b3Sopenharmony_ci struct userdata *u; 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci pa_assert(m); 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 15653a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments"); 15753a5a1b3Sopenharmony_ci return -1; 15853a5a1b3Sopenharmony_ci } 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew(struct userdata, 1); 16153a5a1b3Sopenharmony_ci u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); 16253a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u); 16353a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u); 16453a5a1b3Sopenharmony_ci u->null_module = PA_INVALID_INDEX; 16553a5a1b3Sopenharmony_ci u->ignore = false; 16653a5a1b3Sopenharmony_ci 16753a5a1b3Sopenharmony_ci pa_modargs_free(ma); 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci load_null_sink_if_needed(m->core, NULL, u); 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci return 0; 17253a5a1b3Sopenharmony_ci} 17353a5a1b3Sopenharmony_ci 17453a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 17553a5a1b3Sopenharmony_ci struct userdata *u; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci pa_assert(m); 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 18053a5a1b3Sopenharmony_ci return; 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN) 18353a5a1b3Sopenharmony_ci pa_module_unload_request_by_index(m->core, u->null_module, true); 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_ci pa_xfree(u->sink_name); 18653a5a1b3Sopenharmony_ci pa_xfree(u); 18753a5a1b3Sopenharmony_ci} 188