aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-10-23 10:55:52 +0200
committerMax Kellermann <max@duempel.org>2009-10-23 10:55:52 +0200
commite53ca368a5448291ca2783b8061727635084618f (patch)
tree596c18606cd386b580a23cc6d07cf121cca8db37 /src
parentc426a0bc5cc641ecd044c389f7180dad50a355bf (diff)
downloadmpd-e53ca368a5448291ca2783b8061727635084618f.tar.gz
mpd-e53ca368a5448291ca2783b8061727635084618f.tar.xz
mpd-e53ca368a5448291ca2783b8061727635084618f.zip
output_plugin: added methods enable() and disable()
With these methods, an output plugin can allocate some global resources only if it is actually enabled. The method enable() is called after daemonization, which allows for more sophisticated resource allocation during that method.
Diffstat (limited to 'src')
-rw-r--r--src/main.c4
-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
-rw-r--r--src/player_control.c6
-rw-r--r--src/player_control.h9
-rw-r--r--src/player_thread.c10
-rw-r--r--src/playlist_state.c6
15 files changed, 213 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c
index 4b61ab39e..dfd5d49d8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -374,6 +374,10 @@ int main(int argc, char *argv[])
config_global_check();
+ /* enable all audio outputs (if not already done by
+ playlist_state_restore() */
+ pc_update_audio();
+
/* run the main loop */
g_main_loop_run(main_loop);
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);
diff --git a/src/player_control.c b/src/player_control.c
index 25a0320c5..23ae136ac 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -100,6 +100,12 @@ pc_stop(void)
}
void
+pc_update_audio(void)
+{
+ player_command(PLAYER_COMMAND_UPDATE_AUDIO);
+}
+
+void
pc_kill(void)
{
assert(pc.thread != NULL);
diff --git a/src/player_control.h b/src/player_control.h
index e9a43e844..c0f1d4f07 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -39,6 +39,12 @@ enum player_command {
PLAYER_COMMAND_SEEK,
PLAYER_COMMAND_CLOSE_AUDIO,
+ /**
+ * At least one audio_output.enabled flag has been modified;
+ * commit those changes to the output threads.
+ */
+ PLAYER_COMMAND_UPDATE_AUDIO,
+
/** player_control.next_song has been updated */
PLAYER_COMMAND_QUEUE,
@@ -152,6 +158,9 @@ void
pc_stop(void);
void
+pc_update_audio(void);
+
+void
pc_enqueue_song(struct song *song);
/**
diff --git a/src/player_thread.c b/src/player_thread.c
index f68f61a04..1794ad404 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -350,6 +350,11 @@ static void player_process_command(struct player *player)
case PLAYER_COMMAND_CLOSE_AUDIO:
break;
+ case PLAYER_COMMAND_UPDATE_AUDIO:
+ audio_output_all_enable_disable();
+ player_command_finished();
+ break;
+
case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL);
assert(!player->queued);
@@ -805,6 +810,11 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
break;
+ case PLAYER_COMMAND_UPDATE_AUDIO:
+ audio_output_all_enable_disable();
+ player_command_finished();
+ break;
+
case PLAYER_COMMAND_EXIT:
dc_quit();
audio_output_all_close();
diff --git a/src/playlist_state.c b/src/playlist_state.c
index d97fb1369..b0cf961f7 100644
--- a/src/playlist_state.c
+++ b/src/playlist_state.c
@@ -187,6 +187,12 @@ playlist_state_restore(const char *line, FILE *fp, struct playlist *playlist)
if (!queue_valid_position(&playlist->queue, current))
current = 0;
+ /* enable all devices for the first time; this must be
+ called here, after the audio output states were
+ restored, before playback begins */
+ if (state != PLAYER_STATE_STOP)
+ pc_update_audio();
+
if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current;
else if (seek_time == 0)