aboutsummaryrefslogtreecommitdiffstats
path: root/src/output_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/output_thread.c')
-rw-r--r--src/output_thread.c204
1 files changed, 149 insertions, 55 deletions
diff --git a/src/output_thread.c b/src/output_thread.c
index 9ca844623..11dea0845 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -23,6 +23,8 @@
#include "chunk.h"
#include "pipe.h"
#include "player_control.h"
+#include "filter_plugin.h"
+#include "filter/convert_filter_plugin.h"
#include <glib.h>
@@ -41,6 +43,73 @@ static void ao_command_finished(struct audio_output *ao)
}
static void
+ao_open(struct audio_output *ao)
+{
+ bool success;
+ GError *error = NULL;
+ const struct audio_format *filter_audio_format;
+
+ assert(!ao->open);
+ assert(ao->fail_timer == NULL);
+ assert(ao->pipe != NULL);
+ assert(ao->chunk == NULL);
+
+ /* open the filter */
+
+ filter_audio_format = filter_open(ao->filter, &ao->in_audio_format,
+ &error);
+ if (filter_audio_format == NULL) {
+ g_warning("Failed to open filter for \"%s\" [%s]: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ ao->fail_timer = g_timer_new();
+ return;
+ }
+
+ if (!ao->config_audio_format)
+ ao->out_audio_format = *filter_audio_format;
+
+ success = ao_plugin_open(ao->plugin, ao->data,
+ &ao->out_audio_format,
+ &error);
+
+ assert(!ao->open);
+
+ if (!success) {
+ g_warning("Failed to open \"%s\" [%s]: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ filter_close(ao->filter);
+ ao->fail_timer = g_timer_new();
+ return;
+ }
+
+ convert_filter_set(ao->convert_filter, &ao->out_audio_format);
+
+ g_mutex_lock(ao->mutex);
+ ao->open = true;
+ g_mutex_unlock(ao->mutex);
+
+ g_debug("opened plugin=%s name=\"%s\" "
+ "audio_format=%u:%u:%u:%u",
+ ao->plugin->name, ao->name,
+ ao->out_audio_format.sample_rate,
+ ao->out_audio_format.bits,
+ ao->out_audio_format.channels,
+ ao->out_audio_format.reverse_endian);
+
+ if (!audio_format_equals(&ao->in_audio_format,
+ &ao->out_audio_format))
+ g_debug("converting from %u:%u:%u:%u",
+ ao->in_audio_format.sample_rate,
+ ao->in_audio_format.bits,
+ ao->in_audio_format.channels,
+ ao->in_audio_format.reverse_endian);
+}
+
+static void
ao_close(struct audio_output *ao)
{
assert(ao->open);
@@ -53,11 +122,69 @@ ao_close(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
ao_plugin_close(ao->plugin, ao->data);
- pcm_convert_deinit(&ao->convert_state);
+ filter_close(ao->filter);
g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
}
+static void
+ao_reopen_filter(struct audio_output *ao)
+{
+ const struct audio_format *filter_audio_format;
+ GError *error = NULL;
+
+ filter_close(ao->filter);
+ filter_audio_format = filter_open(ao->filter, &ao->in_audio_format,
+ &error);
+ if (filter_audio_format == NULL) {
+ g_warning("Failed to open filter for \"%s\" [%s]: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ /* this is a little code duplication fro ao_close(),
+ but we cannot call this function because we must
+ not call filter_close(ao->filter) again */
+
+ ao->pipe = NULL;
+
+ g_mutex_lock(ao->mutex);
+ ao->chunk = NULL;
+ ao->open = false;
+ g_mutex_unlock(ao->mutex);
+
+ ao_plugin_close(ao->plugin, ao->data);
+
+ ao->fail_timer = g_timer_new();
+ return;
+ }
+
+ convert_filter_set(ao->convert_filter, &ao->out_audio_format);
+}
+
+static void
+ao_reopen(struct audio_output *ao)
+{
+ if (!ao->config_audio_format) {
+ if (ao->open) {
+ const struct music_pipe *mp = ao->pipe;
+ ao_close(ao);
+ ao->pipe = mp;
+ }
+
+ /* no audio format is configured: copy in->out, let
+ the output's open() method determine the effective
+ out_audio_format */
+ ao->out_audio_format = ao->in_audio_format;
+ }
+
+ if (ao->open)
+ /* the audio format has changed, and all filters have
+ to be reconfigured */
+ ao_reopen_filter(ao);
+ else
+ ao_open(ao);
+}
+
static bool
ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
{
@@ -65,6 +192,8 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
size_t size = chunk->length;
GError *error = NULL;
+ assert(ao != NULL);
+ assert(ao->filter != NULL);
assert(!music_chunk_is_empty(chunk));
assert(music_chunk_check_format(chunk, &ao->in_audio_format));
assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);
@@ -75,18 +204,19 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
if (size == 0)
return true;
- if (!audio_format_equals(&ao->in_audio_format,
- &ao->out_audio_format)) {
- data = pcm_convert(&ao->convert_state,
- &ao->in_audio_format, data, size,
- &ao->out_audio_format, &size);
-
- /* under certain circumstances, pcm_convert() may
- return an empty buffer - this condition should be
- investigated further, but for now, do this check as
- a workaround: */
- if (data == NULL)
- return true;
+ data = filter_filter(ao->filter, data, size, &size, &error);
+ if (data == NULL) {
+ g_warning("\"%s\" [%s] failed to filter: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ ao_plugin_cancel(ao->plugin, ao->data);
+ ao_close(ao);
+
+ /* don't automatically reopen this device for 10
+ seconds */
+ ao->fail_timer = g_timer_new();
+ return false;
}
while (size > 0 && ao->command == AO_COMMAND_NONE) {
@@ -182,8 +312,6 @@ static void ao_pause(struct audio_output *ao)
static gpointer audio_output_task(gpointer arg)
{
struct audio_output *ao = arg;
- bool ret;
- GError *error;
while (1) {
switch (ao->command) {
@@ -191,47 +319,12 @@ static gpointer audio_output_task(gpointer arg)
break;
case AO_COMMAND_OPEN:
- assert(!ao->open);
- assert(ao->fail_timer == NULL);
- assert(ao->pipe != NULL);
- assert(ao->chunk == NULL);
-
- error = NULL;
- ret = ao_plugin_open(ao->plugin, ao->data,
- &ao->out_audio_format,
- &error);
-
- assert(!ao->open);
- if (ret) {
- pcm_convert_init(&ao->convert_state);
-
- g_mutex_lock(ao->mutex);
- ao->open = true;
- g_mutex_unlock(ao->mutex);
-
- g_debug("opened plugin=%s name=\"%s\" "
- "audio_format=%u:%u:%u",
- ao->plugin->name,
- ao->name,
- ao->out_audio_format.sample_rate,
- ao->out_audio_format.bits,
- ao->out_audio_format.channels);
-
- if (!audio_format_equals(&ao->in_audio_format,
- &ao->out_audio_format))
- g_debug("converting from %u:%u:%u",
- ao->in_audio_format.sample_rate,
- ao->in_audio_format.bits,
- ao->in_audio_format.channels);
- } else {
- g_warning("Failed to open \"%s\" [%s]: %s",
- ao->name, ao->plugin->name,
- error->message);
- g_error_free(error);
-
- ao->fail_timer = g_timer_new();
- }
+ ao_open(ao);
+ ao_command_finished(ao);
+ break;
+ case AO_COMMAND_REOPEN:
+ ao_reopen(ao);
ao_command_finished(ao);
break;
@@ -244,6 +337,7 @@ static gpointer audio_output_task(gpointer arg)
ao_plugin_cancel(ao->plugin, ao->data);
ao_close(ao);
+ filter_close(ao->filter);
ao_command_finished(ao);
break;