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.
This commit is contained in:
Max Kellermann 2009-10-23 10:55:52 +02:00
parent c426a0bc5c
commit e53ca368a5
15 changed files with 213 additions and 1 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -65,6 +65,13 @@ audio_output_get(unsigned i);
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.
*

View File

@ -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;

View File

@ -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);

View File

@ -40,6 +40,18 @@ bool
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.
*

View File

@ -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;

View File

@ -27,6 +27,8 @@
enum audio_output_command {
AO_COMMAND_NONE = 0,
AO_COMMAND_ENABLE,
AO_COMMAND_DISABLE,
AO_COMMAND_OPEN,
/**
@ -70,6 +72,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?
*

View File

@ -66,6 +66,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.
*
@ -149,6 +167,22 @@ ao_plugin_finish(const struct audio_output_plugin *plugin, void *data)
plugin->finish(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,

View File

@ -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);

View File

@ -99,6 +99,12 @@ pc_stop(void)
idle_add(IDLE_PLAYER);
}
void
pc_update_audio(void)
{
player_command(PLAYER_COMMAND_UPDATE_AUDIO);
}
void
pc_kill(void)
{

View File

@ -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,
@ -151,6 +157,9 @@ pc_get_error(void);
void
pc_stop(void);
void
pc_update_audio(void);
void
pc_enqueue_song(struct song *song);

View File

@ -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();

View File

@ -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)