From 7678c48a72c9750e52161fe023521c794068a72b Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 23 Apr 2013 10:59:41 +0200 Subject: Switch filters to AVFrame. Remove route filter for now, it should be replaced with lavfi later. --- src/output_thread.c | 148 ++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 80 deletions(-) (limited to 'src/output_thread.c') 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 #include +#include +#include +#include + #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; } -- cgit v1.2.3