aboutsummaryrefslogtreecommitdiff
path: root/src/output_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/output_thread.c')
-rw-r--r--src/output_thread.c148
1 files changed, 68 insertions, 80 deletions
diff --git a/src/output_thread.c b/src/output_thread.c
index fde9050f..b9a4f3ea 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -38,6 +38,10 @@
#include <stdlib.h>
#include <errno.h>
+#include <libavutil/common.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "output"
@@ -315,120 +319,101 @@ ao_wait(struct audio_output *ao)
}
}
-static const char *
-ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
- struct filter *replay_gain_filter,
- unsigned *replay_gain_serial_p,
- size_t *length_r)
+static AVFrame *ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
+ struct filter *replay_gain_filter,
+ unsigned *replay_gain_serial_p)
{
+ AVFrame *frame = NULL;
+
assert(chunk != NULL);
assert(!music_chunk_is_empty(chunk));
assert(music_chunk_check_format(chunk, &ao->in_audio_format));
- const char *data = chunk->frame ? chunk->frame->data[0] : NULL;
- size_t length = data ? av_samples_get_buffer_size(NULL, ao->in_audio_format.channels,
- chunk->frame->nb_samples,
- chunk->frame->format, 1) : 0;
+ frame = av_frame_clone(chunk->frame);
+ if (!frame)
+ return NULL;
- if (length > 0 && replay_gain_filter != NULL) {
+ if (replay_gain_filter) {
if (chunk->replay_gain_serial != *replay_gain_serial_p) {
replay_gain_filter_set_info(replay_gain_filter,
- chunk->replay_gain_serial != 0
- ? &chunk->replay_gain_info
- : NULL);
+ chunk->replay_gain_serial != 0
+ ? &chunk->replay_gain_info
+ : NULL);
*replay_gain_serial_p = chunk->replay_gain_serial;
}
-
- GError *error = NULL;
- data = filter_filter(replay_gain_filter, data, length,
- &length, &error);
- if (data == NULL) {
- g_warning("\"%s\" [%s] failed to filter: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
+ frame = filter_filter(replay_gain_filter, frame);
+ if (!frame) {
+ g_warning("\"%s\" [%s] failed to apply replay gain",
+ ao->name, ao->plugin->name);
return NULL;
}
}
- *length_r = length;
- return data;
+ return frame;
}
-static const char *
-ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
- size_t *length_r)
+static AVFrame *ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk)
{
- GError *error = NULL;
+ AVFrame *frame;
+ int ret;
- size_t length;
- const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
- &ao->replay_gain_serial, &length);
- if (data == NULL)
+ frame = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
+ &ao->replay_gain_serial);
+ if (!frame)
return NULL;
- if (length == 0) {
- /* empty chunk, nothing to do */
- *length_r = 0;
- return data;
- }
-
/* cross-fade */
-
- if (chunk->other != NULL) {
- size_t other_length;
- const char *other_data =
- ao_chunk_data(ao, chunk->other,
- ao->other_replay_gain_filter,
- &ao->other_replay_gain_serial,
- &other_length);
- if (other_data == NULL)
+ if (chunk->other) {
+ AVFrame *other = ao_chunk_data(ao, chunk->other,
+ ao->other_replay_gain_filter,
+ &ao->other_replay_gain_serial);
+ if (!other) {
+ av_frame_free(&frame);
return NULL;
-
- if (other_length == 0) {
- *length_r = 0;
- return data;
}
/* if the "other" chunk is longer, then that trailer
is used as-is, without mixing; it is part of the
"next" song being faded in, and if there's a rest,
it means cross-fading ends here */
+ ret = av_frame_make_writable(other);
+ if (ret < 0) {
+ av_frame_free(&frame);
+ av_frame_free(&other);
+ return NULL;
+ }
- if (length > other_length)
- length = other_length;
-
- char *dest = pcm_buffer_get(&ao->cross_fade_buffer,
- other_length);
- memcpy(dest, other_data, other_length);
- if (!pcm_mix(dest, data, length, ao->in_audio_format.format,
- 1.0 - chunk->mix_ratio)) {
+ ret = pcm_mix(other->data[0], frame->data[0],
+ FFMIN(frame->linesize[0], other->linesize[0]),
+ ao->in_audio_format.format,
+ 1.0 - chunk->mix_ratio);
+ av_frame_free(&frame);
+ if (!ret) {
g_warning("Cannot cross-fade format %s",
- sample_format_to_string(ao->in_audio_format.format));
+ sample_format_to_string(ao->in_audio_format.format));
+ av_frame_free(&other);
return NULL;
}
- data = dest;
- length = other_length;
+ frame = other;
}
/* apply filter chain */
-
- data = filter_filter(ao->filter, data, length, &length, &error);
- if (data == NULL) {
- g_warning("\"%s\" [%s] failed to filter: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
+ frame = filter_filter(ao->filter, frame);
+ if (!frame) {
+ g_warning("\"%s\" [%s] failed to filter\n",
+ ao->name, ao->plugin->name);
return NULL;
}
- *length_r = length;
- return data;
+ return frame;
}
-static bool
-ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
+static bool ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
{
- GError *error = NULL;
+ AVFrame *frame;
+ const uint8_t *data;
+ size_t size;
assert(ao != NULL);
assert(ao->filter != NULL);
@@ -438,14 +423,11 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
ao_plugin_send_tag(ao, chunk->tag);
g_mutex_lock(ao->mutex);
}
+ if (!chunk->frame)
+ return true;
- size_t size;
-#if GCC_CHECK_VERSION(4,7)
- /* workaround -Wmaybe-uninitialized false positive */
- size = 0;
-#endif
- const char *data = ao_filter_chunk(ao, chunk, &size);
- if (data == NULL) {
+ frame = ao_filter_chunk(ao, chunk);
+ if (!frame) {
ao_close(ao, false);
/* don't automatically reopen this device for 10
@@ -454,7 +436,11 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
return false;
}
+ data = frame->data[0];
+ size = frame->nb_samples * ao->out_audio_format.channels *
+ av_get_bytes_per_sample(frame->format);
while (size > 0 && ao->command == AO_COMMAND_NONE) {
+ GError *error = NULL;
size_t nbytes;
if (!ao_wait(ao))
@@ -466,7 +452,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
if (nbytes == 0) {
/* play()==0 means failure */
g_warning("\"%s\" [%s] failed to play: %s",
- ao->name, ao->plugin->name, error->message);
+ ao->name, ao->plugin->name, error->message);
g_error_free(error);
ao_close(ao, false);
@@ -475,6 +461,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
10 seconds */
assert(ao->fail_timer == NULL);
ao->fail_timer = g_timer_new();
+ av_frame_free(&frame);
return false;
}
@@ -486,6 +473,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
size -= nbytes;
}
+ av_frame_free(&frame);
return true;
}