diff options
Diffstat (limited to 'audio/filter/af_lavrresample.c')
-rw-r--r-- | audio/filter/af_lavrresample.c | 171 |
1 files changed, 141 insertions, 30 deletions
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index 5b26a0dce6..4372100ea0 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -43,6 +43,7 @@ #define avresample_available(x) 0 #define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \ swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples) +#define avresample_set_channel_mapping swr_set_channel_mapping #else #error "config.h broken" #endif @@ -50,6 +51,7 @@ #include "core/mp_msg.h" #include "core/subopt-helper.h" #include "audio/filter/af.h" +#include "audio/fmt-conversion.h" struct af_resample_opts { int filter_size; @@ -57,14 +59,24 @@ struct af_resample_opts { int linear; double cutoff; - int out_rate; int in_rate; + int in_format; + struct mp_chmap in_channels; + int out_rate; + int out_format; + struct mp_chmap out_channels; }; struct af_resample { + int allow_detach; struct AVAudioResampleContext *avrctx; + struct AVAudioResampleContext *avrctx_out; // for output channel reordering struct af_resample_opts ctx; // opts in the context struct af_resample_opts opts; // opts requested by the user + // At least libswresample keeps a pointer around for this: + int reorder_in[MP_NUM_CHANNELS]; + int reorder_out[MP_NUM_CHANNELS]; + uint8_t *reorder_buffer; }; #ifdef CONFIG_LIBAVRESAMPLE @@ -88,8 +100,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s, struct mp_audio *in, struct mp_audio *out) { - return s->ctx.out_rate != out->rate || - s->ctx.in_rate != in->rate || + return s->ctx.in_rate != in->rate || + s->ctx.in_format != in->format || + !mp_chmap_equals(&s->ctx.in_channels, &in->channels) || + s->ctx.out_rate != out->rate || + s->ctx.out_format != out->format || + !mp_chmap_equals(&s->ctx.out_channels, &out->channels) || s->ctx.filter_size != s->opts.filter_size || s->ctx.phase_shift != s->opts.phase_shift || s->ctx.linear != s->opts.linear || @@ -97,6 +113,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s, } +static bool test_conversion(int src_format, int dst_format) +{ + return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE && + af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE; +} + #define ctx_opt_set_int(a,b) av_opt_set_int(s->avrctx, (a), (b), 0) #define ctx_opt_set_dbl(a,b) av_opt_set_double(s->avrctx, (a), (b), 0) @@ -108,36 +130,70 @@ static int control(struct af_instance *af, int cmd, void *arg) switch (cmd) { case AF_CONTROL_REINIT: { - if ((out->rate == in->rate) || (out->rate == 0)) + struct mp_audio orig_in = *in; + + if (((out->rate == in->rate) || (out->rate == 0)) && + (out->format == in->format) && + (mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) && + s->allow_detach) return AF_DETACH; - out->nch = FFMIN(in->nch, AF_NCH); - out->format = AF_FORMAT_S16_NE; - out->bps = 2; - af->mul = (double) out->rate / in->rate; + if (out->rate == 0) + out->rate = in->rate; + + if (mp_chmap_is_empty(&out->channels)) + mp_audio_set_channels(out, &in->channels); + + enum AVSampleFormat in_samplefmt = af_to_avformat(in->format); + if (in_samplefmt == AV_SAMPLE_FMT_NONE) { + mp_audio_set_format(in, AF_FORMAT_FLOAT_NE); + in_samplefmt = af_to_avformat(in->format); + } + enum AVSampleFormat out_samplefmt = af_to_avformat(out->format); + if (out_samplefmt == AV_SAMPLE_FMT_NONE) { + mp_audio_set_format(out, in->format); + out_samplefmt = in_samplefmt; + } + + af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch); af->delay = out->nch * s->opts.filter_size / FFMIN(af->mul, 1); if (needs_lavrctx_reconfigure(s, in, out)) { - if (s->avrctx) - avresample_close(s->avrctx); + avresample_close(s->avrctx); + avresample_close(s->avrctx_out); s->ctx.out_rate = out->rate; s->ctx.in_rate = in->rate; + s->ctx.out_format = out->format; + s->ctx.in_format = in->format; + s->ctx.out_channels= out->channels; + s->ctx.in_channels = in->channels; s->ctx.filter_size = s->opts.filter_size; s->ctx.phase_shift = s->opts.phase_shift; s->ctx.linear = s->opts.linear; s->ctx.cutoff = s->opts.cutoff; - int ch_layout = av_get_default_channel_layout(out->nch); + struct mp_chmap map_in = in->channels; + struct mp_chmap map_out = out->channels; + + // Try not to do any remixing if at least one is "unknown". + if (mp_chmap_is_unknown(&map_in) || mp_chmap_is_unknown(&map_out)) { + mp_chmap_set_unknown(&map_in, map_in.num); + mp_chmap_set_unknown(&map_out, map_out.num); + } + + // unchecked: don't take any channel reordering into account + uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in); + uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out); - ctx_opt_set_int("in_channel_layout", ch_layout); - ctx_opt_set_int("out_channel_layout", ch_layout); + ctx_opt_set_int("in_channel_layout", in_ch_layout); + ctx_opt_set_int("out_channel_layout", out_ch_layout); ctx_opt_set_int("in_sample_rate", s->ctx.in_rate); ctx_opt_set_int("out_sample_rate", s->ctx.out_rate); - ctx_opt_set_int("in_sample_fmt", AV_SAMPLE_FMT_S16); - ctx_opt_set_int("out_sample_fmt", AV_SAMPLE_FMT_S16); + ctx_opt_set_int("in_sample_fmt", in_samplefmt); + ctx_opt_set_int("out_sample_fmt", out_samplefmt); ctx_opt_set_int("filter_size", s->ctx.filter_size); ctx_opt_set_int("phase_shift", s->ctx.phase_shift); @@ -145,20 +201,51 @@ static int control(struct af_instance *af, int cmd, void *arg) ctx_opt_set_dbl("cutoff", s->ctx.cutoff); - if (avresample_open(s->avrctx) < 0) { + struct mp_chmap in_lavc; + mp_chmap_from_lavc(&in_lavc, in_ch_layout); + mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc); + + struct mp_chmap out_lavc; + mp_chmap_from_lavc(&out_lavc, out_ch_layout); + mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out); + + // Same configuration; we just reorder. + av_opt_set_int(s->avrctx_out, "in_channel_layout", out_ch_layout, 0); + av_opt_set_int(s->avrctx_out, "out_channel_layout", out_ch_layout, 0); + av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmt, 0); + av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0); + av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0); + av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0); + + // API has weird requirements, quoting avresample.h: + // * This function can only be called when the allocated context is not open. + // * Also, the input channel layout must have already been set. + avresample_set_channel_mapping(s->avrctx, s->reorder_in); + avresample_set_channel_mapping(s->avrctx_out, s->reorder_out); + + if (avresample_open(s->avrctx) < 0 || + avresample_open(s->avrctx_out) < 0) + { mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot open " "Libavresample Context. \n"); return AF_ERROR; } } - int out_rate, test_output_res; - // hack to make af_test_output ignore the samplerate change - out_rate = out->rate; - out->rate = in->rate; - test_output_res = af_test_output(af, in); - out->rate = out_rate; - return test_output_res; + return ((in->format == orig_in.format) && + mp_chmap_equals(&in->channels, &orig_in.channels)) + ? AF_OK : AF_FALSE; + } + case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: { + if (af_to_avformat(*(int*)arg) == AV_SAMPLE_FMT_NONE) + return AF_FALSE; + + mp_audio_set_format(af->data, *(int*)arg); + return AF_OK; + } + case AF_CONTROL_CHANNELS | AF_CONTROL_SET: { + mp_audio_set_channels(af->data, (struct mp_chmap *)arg); + return AF_OK; } case AF_CONTROL_COMMAND_LINE: { s->opts.cutoff = 0.0; @@ -169,6 +256,7 @@ static int control(struct af_instance *af, int cmd, void *arg) {"phase_shift", OPT_ARG_INT, &s->opts.phase_shift, NULL}, {"linear", OPT_ARG_BOOL, &s->opts.linear, NULL}, {"cutoff", OPT_ARG_FLOAT, &s->opts.cutoff, NULL}, + {"detach", OPT_ARG_BOOL, &s->allow_detach, NULL}, {0} }; @@ -198,10 +286,21 @@ static void uninit(struct af_instance *af) struct af_resample *s = af->setup; if (s->avrctx) avresample_close(s->avrctx); + if (s->avrctx_out) + avresample_close(s->avrctx_out); talloc_free(af->setup); } } +static bool needs_reorder(int *reorder, int num_ch) +{ + for (int n = 0; n < num_ch; n++) { + if (reorder[n] != n) + return true; + } + return false; +} + static struct mp_audio *play(struct af_instance *af, struct mp_audio *data) { struct af_resample *s = af->setup; @@ -227,10 +326,18 @@ static struct mp_audio *play(struct af_instance *af, struct mp_audio *data) (uint8_t **) &out->audio, out_size, out_samples, (uint8_t **) &in->audio, in_size, in_samples); - out_size = out->bps * out_samples * out->nch; - in->audio = out->audio; - in->len = out_size; - in->rate = s->ctx.out_rate; + *data = *out; + + if (needs_reorder(s->reorder_out, out->nch)) { + if (talloc_get_size(s->reorder_buffer) < out_size) + s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size); + data->audio = s->reorder_buffer; + out_samples = avresample_convert(s->avrctx_out, + (uint8_t **) &data->audio, out_size, out_samples, + (uint8_t **) &out->audio, out_size, out_samples); + } + + data->len = out->bps * out_samples * out->nch; return data; } @@ -244,7 +351,7 @@ static int af_open(struct af_instance *af) af->mul = 1; af->data = talloc_zero(s, struct mp_audio); - af->data->rate = 44100; + af->data->rate = 0; int default_filter_size = 16; s->opts = (struct af_resample_opts) { @@ -254,10 +361,13 @@ static int af_open(struct af_instance *af) .phase_shift = 10, }; + s->allow_detach = 1; + s->avrctx = avresample_alloc_context(); + s->avrctx_out = avresample_alloc_context(); af->setup = s; - if (s->avrctx) { + if (s->avrctx && s->avrctx_out) { return AF_OK; } else { mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot initialize " @@ -273,5 +383,6 @@ struct af_info af_info_lavrresample = { "Stefano Pigozzi (based on Michael Niedermayer's lavcresample)", "", AF_FLAGS_REENTRANT, - af_open + af_open, + .test_conversion = test_conversion, }; |