aboutsummaryrefslogtreecommitdiffstats
path: root/src/output
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output_all.c19
-rw-r--r--src/output_all.h7
-rw-r--r--src/output_command.c9
-rw-r--r--src/output_control.c37
-rw-r--r--src/output_control.h12
-rw-r--r--src/output_init.c1
-rw-r--r--src/output_internal.h8
-rw-r--r--src/output_plugin.h34
-rw-r--r--src/output_thread.c50
10 files changed, 178 insertions, 1 deletions
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index 39c8222c5..88cdebfde 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -309,6 +309,8 @@ pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
pa_threaded_mainloop_unlock(po->mainloop);
+ po->stream = NULL;
+
return po;
}
diff --git a/src/output_all.c b/src/output_all.c
index b7a42a6a3..a16be7386 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -153,6 +153,25 @@ audio_output_all_finish(void)
notify_deinit(&audio_output_client_notify);
}
+void
+audio_output_all_enable_disable(void)
+{
+ for (unsigned i = 0; i < num_audio_outputs; i++) {
+ struct audio_output *ao = &audio_outputs[i];
+ bool enabled;
+
+ g_mutex_lock(ao->mutex);
+ enabled = ao->really_enabled;
+ g_mutex_unlock(ao->mutex);
+
+ if (ao->enabled != enabled) {
+ if (ao->enabled)
+ audio_output_enable(ao);
+ else
+ audio_output_disable(ao);
+ }
+ }
+}
/**
* Determine if all (active) outputs have finished the current
diff --git a/src/output_all.h b/src/output_all.h
index e124d3150..2f5101f1b 100644
--- a/src/output_all.h
+++ b/src/output_all.h
@@ -66,6 +66,13 @@ struct audio_output *
audio_output_find(const char *name);
/**
+ * Checks the "enabled" flag of all audio outputs, and if one has
+ * changed, commit the change.
+ */
+void
+audio_output_all_enable_disable(void);
+
+/**
* Opens all audio outputs which are not disabled.
*
* @param audio_format the preferred audio format, or NULL to reuse
diff --git a/src/output_command.c b/src/output_command.c
index 9d948c8cc..b47890043 100644
--- a/src/output_command.c
+++ b/src/output_command.c
@@ -29,6 +29,7 @@
#include "output_internal.h"
#include "output_plugin.h"
#include "mixer_control.h"
+#include "player_control.h"
#include "idle.h"
extern unsigned audio_output_state_version;
@@ -42,10 +43,14 @@ audio_output_enable_index(unsigned idx)
return false;
ao = audio_output_get(idx);
+ if (ao->enabled)
+ return true;
ao->enabled = true;
idle_add(IDLE_OUTPUT);
+ pc_update_audio();
+
++audio_output_state_version;
return true;
@@ -61,6 +66,8 @@ audio_output_disable_index(unsigned idx)
return false;
ao = audio_output_get(idx);
+ if (!ao->enabled)
+ return true;
ao->enabled = false;
idle_add(IDLE_OUTPUT);
@@ -71,6 +78,8 @@ audio_output_disable_index(unsigned idx)
idle_add(IDLE_MIXER);
}
+ pc_update_audio();
+
++audio_output_state_version;
return true;
diff --git a/src/output_control.c b/src/output_control.c
index b833fb08d..6512cbe74 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -59,6 +59,41 @@ static void ao_command_async(struct audio_output *ao,
notify_signal(&ao->notify);
}
+void
+audio_output_enable(struct audio_output *ao)
+{
+ if (ao->thread == NULL) {
+ if (ao->plugin->enable == NULL) {
+ /* don't bother to start the thread now if the
+ device doesn't even have a enable() method;
+ just assign the variable and we're done */
+ ao->really_enabled = true;
+ return;
+ }
+
+ audio_output_thread_start(ao);
+ }
+
+ ao_command(ao, AO_COMMAND_ENABLE);
+}
+
+void
+audio_output_disable(struct audio_output *ao)
+{
+ if (ao->thread == NULL) {
+ if (ao->plugin->disable == NULL)
+ ao->really_enabled = false;
+ else
+ /* if there's no thread yet, the device cannot
+ be enabled */
+ assert(!ao->really_enabled);
+
+ return;
+ }
+
+ ao_command(ao, AO_COMMAND_DISABLE);
+}
+
static bool
audio_output_open(struct audio_output *ao,
const struct audio_format *audio_format,
@@ -122,7 +157,7 @@ audio_output_update(struct audio_output *ao,
{
assert(mp != NULL);
- if (ao->enabled) {
+ if (ao->enabled && ao->really_enabled) {
if (ao->fail_timer == NULL ||
g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER)
return audio_output_open(ao, audio_format, mp);
diff --git a/src/output_control.h b/src/output_control.h
index 72e3ed468..b2e48fa8d 100644
--- a/src/output_control.h
+++ b/src/output_control.h
@@ -41,6 +41,18 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
GError **error_r);
/**
+ * Enables the device.
+ */
+void
+audio_output_enable(struct audio_output *ao);
+
+/**
+ * Disables the device.
+ */
+void
+audio_output_disable(struct audio_output *ao);
+
+/**
* Opens or closes the device, depending on the "enabled" flag.
*
* @return true if the device is open
diff --git a/src/output_init.c b/src/output_init.c
index 745b63e30..5cb9ac92c 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -180,6 +180,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
ao->plugin = plugin;
ao->enabled = config_get_block_bool(param, "enabled", true);
+ ao->really_enabled = false;
ao->open = false;
ao->pause = false;
ao->fail_timer = NULL;
diff --git a/src/output_internal.h b/src/output_internal.h
index f27a10ec7..0c25348a0 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -27,6 +27,8 @@
enum audio_output_command {
AO_COMMAND_NONE = 0,
+ AO_COMMAND_ENABLE,
+ AO_COMMAND_DISABLE,
AO_COMMAND_OPEN,
/**
@@ -71,6 +73,12 @@ struct audio_output {
bool enabled;
/**
+ * Is this device actually enabled, i.e. the "enable" method
+ * has succeeded?
+ */
+ bool really_enabled;
+
+ /**
* Is the device (already) open and functional?
*
* This attribute may only be modified by the output thread.
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 13dba0d0b..3a9748d46 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -67,6 +67,24 @@ struct audio_output_plugin {
void (*finish)(void *data);
/**
+ * Enable the device. This may allocate resources, preparing
+ * for the device to be opened. Enabling a device cannot
+ * fail: if an error occurs during that, it should be reported
+ * by the open() method.
+ *
+ * @param error_r location to store the error occuring, or
+ * NULL to ignore errors
+ * @return true on success, false on error
+ */
+ bool (*enable)(void *data, GError **error_r);
+
+ /**
+ * Disables the device. It is closed before this method is
+ * called.
+ */
+ void (*disable)(void *data);
+
+ /**
* Really open the device.
*
* @param audio_format the audio format in which data is going
@@ -150,6 +168,22 @@ ao_plugin_finish(const struct audio_output_plugin *plugin, void *data)
}
static inline bool
+ao_plugin_enable(const struct audio_output_plugin *plugin, void *data,
+ GError **error_r)
+{
+ return plugin->enable != NULL
+ ? plugin->enable(data, error_r)
+ : true;
+}
+
+static inline void
+ao_plugin_disable(const struct audio_output_plugin *plugin, void *data)
+{
+ if (plugin->disable != NULL)
+ plugin->disable(data);
+}
+
+static inline bool
ao_plugin_open(const struct audio_output_plugin *plugin,
void *data, struct audio_format *audio_format,
GError **error)
diff --git a/src/output_thread.c b/src/output_thread.c
index 9eb2478b0..4bae2f162 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -42,6 +42,40 @@ static void ao_command_finished(struct audio_output *ao)
notify_signal(&audio_output_client_notify);
}
+static bool
+ao_enable(struct audio_output *ao)
+{
+ GError *error = NULL;
+
+ if (ao->really_enabled)
+ return true;
+
+ if (!ao_plugin_enable(ao->plugin, ao->data, &error)) {
+ g_warning("Failed to enable \"%s\" [%s]: %s\n",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ ao->really_enabled = true;
+ return true;
+}
+
+static void
+ao_close(struct audio_output *ao);
+
+static void
+ao_disable(struct audio_output *ao)
+{
+ if (ao->open)
+ ao_close(ao);
+
+ if (ao->really_enabled) {
+ ao->really_enabled = false;
+ ao_plugin_disable(ao->plugin, ao->data);
+ }
+}
+
static void
ao_open(struct audio_output *ao)
{
@@ -54,6 +88,12 @@ ao_open(struct audio_output *ao)
assert(ao->pipe != NULL);
assert(ao->chunk == NULL);
+ /* enable the device (just in case the last enable has failed) */
+
+ if (!ao_enable(ao))
+ /* still no luck */
+ return;
+
/* open the filter */
filter_audio_format = filter_open(ao->filter, &ao->in_audio_format,
@@ -321,6 +361,16 @@ static gpointer audio_output_task(gpointer arg)
case AO_COMMAND_NONE:
break;
+ case AO_COMMAND_ENABLE:
+ ao_enable(ao);
+ ao_command_finished(ao);
+ break;
+
+ case AO_COMMAND_DISABLE:
+ ao_disable(ao);
+ ao_command_finished(ao);
+ break;
+
case AO_COMMAND_OPEN:
ao_open(ao);
ao_command_finished(ao);