diff options
Diffstat (limited to 'audio/filter')
-rw-r--r-- | audio/filter/af_lavrresample.c | 119 |
1 files changed, 82 insertions, 37 deletions
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index 412846a78e..ba67eb1c0c 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -81,6 +81,7 @@ struct af_resample { struct mp_audio *pending; bool avrctx_ok; struct AVAudioResampleContext *avrctx; + struct mp_audio avrctx_fmt; // output format of 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 @@ -163,6 +164,22 @@ static bool test_conversion(int src_format, int dst_format) af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE; } +// mp_chmap_get_reorder() performs: +// to->speaker[n] = from->speaker[src[n]] +// but libavresample does: +// to->speaker[dst[n]] = from->speaker[n] +static void transpose_order(int *map, int num) +{ + int nmap[MP_NUM_CHANNELS] = {0}; + for (int n = 0; n < num; n++) { + for (int i = 0; i < num; i++) { + if (map[n] == i) + nmap[i] = n; + } + } + memcpy(map, nmap, sizeof(nmap)); +} + static int configure_lavrr(struct af_instance *af, struct mp_audio *in, struct mp_audio *out) { @@ -172,8 +189,12 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, enum AVSampleFormat in_samplefmt = af_to_avformat(in->format); enum AVSampleFormat out_samplefmt = af_to_avformat(out->format); + enum AVSampleFormat out_samplefmtp = + af_to_avformat(af_fmt_to_planar(out->format)); - if (in_samplefmt == AV_SAMPLE_FMT_NONE || out_samplefmt == AV_SAMPLE_FMT_NONE) + if (in_samplefmt == AV_SAMPLE_FMT_NONE || + out_samplefmt == AV_SAMPLE_FMT_NONE || + out_samplefmtp == AV_SAMPLE_FMT_NONE) return AF_ERROR; avresample_close(s->avrctx); @@ -220,27 +241,52 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in); uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out); - av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0); - av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0); - - av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0); - av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0); - - av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0); - av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmt, 0); - struct mp_chmap in_lavc; mp_chmap_from_lavc(&in_lavc, in_ch_layout); + if (in_lavc.num != map_in.num) { + // For handling NA channels, we would have to add a planarization step. + MP_FATAL(af, "Unsupported channel remapping.\n"); + return AF_ERROR; + } + mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc); + transpose_order(s->reorder_in, map_in.num); 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); + if (mp_chmap_equals(&out_lavc, &map_out)) { + // No intermediate step required - output new format directly. + out_samplefmtp = out_samplefmt; + } else { + // Verify that we really just reorder and/or insert NA channels. + struct mp_chmap withna = out_lavc; + mp_chmap_fill_na(&withna, map_out.num); + if (withna.num != map_out.num) + return AF_ERROR; + 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); + s->avrctx_fmt = *out; + mp_audio_set_channels(&s->avrctx_fmt, &out_lavc); + mp_audio_set_format(&s->avrctx_fmt, af_from_avformat(out_samplefmtp)); + + // Real conversion; output is input to avrctx_out. + av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0); + av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0); + av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0); + av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0); + av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0); + av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmtp, 0); + + // Just needs the correct number of channels. + int fake_out_ch_layout = av_get_default_channel_layout(map_out.num); + if (!fake_out_ch_layout) + return AF_ERROR; + + // Deplanarize if needed. + av_opt_set_int(s->avrctx_out, "in_channel_layout", fake_out_ch_layout, 0); + av_opt_set_int(s->avrctx_out, "out_channel_layout", fake_out_ch_layout, 0); + av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmtp, 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); @@ -249,7 +295,6 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, // * 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) @@ -354,22 +399,22 @@ static void uninit(struct af_instance *af) talloc_free(s->pending); } -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 void reorder_planes(struct mp_audio *mpa, int *reorder) +static void reorder_planes(struct mp_audio *mpa, int *reorder, + struct mp_chmap *newmap) { struct mp_audio prev = *mpa; - for (int n = 0; n < mpa->num_planes; n++) { - assert(reorder[n] >= 0 && reorder[n] < mpa->num_planes); - mpa->planes[n] = prev.planes[reorder[n]]; + + for (int n = 0; n < newmap->num; n++) { + int src = reorder[n]; + if (src < 0) { + src = 0; // Use the first plane for padding channels. + mpa->readonly = true; + } + assert(src < mpa->num_planes); + mpa->planes[n] = prev.planes[src]; } + + mp_audio_set_channels(mpa, newmap); } static int filter(struct af_instance *af, struct mp_audio *in) @@ -388,14 +433,14 @@ static int filter(struct af_instance *af, struct mp_audio *in) int samples = avresample_available(s->avrctx) + av_rescale_rnd(get_delay(s) + (in ? in->samples : 0), s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP); - struct mp_audio *out = mp_audio_pool_get(af->out_pool, af->data, samples); + + struct mp_audio out_format = s->avrctx_fmt; + struct mp_audio *out = mp_audio_pool_get(af->out_pool, &out_format, samples); if (!out) goto error; if (in) mp_audio_copy_attributes(out, in); - mp_audio_realloc_min(out, out->samples); - af->delay = get_delay(s) / (double)s->ctx.in_rate; if (out->samples) { @@ -404,11 +449,11 @@ static int filter(struct af_instance *af, struct mp_audio *in) goto error; } - if (needs_reorder(s->reorder_out, out->nch)) { - if (AF_FORMAT_IS_PLANAR(out->format)) { - reorder_planes(out, s->reorder_out); - } else if (out->samples) { - struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, out, + if (out->samples && !mp_audio_config_equals(out, af->data)) { + assert(AF_FORMAT_IS_PLANAR(out->format)); + reorder_planes(out, s->reorder_out, &af->data->channels); + if (!mp_audio_config_equals(out, af->data)) { + struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, af->data, out->samples); if (!new) goto error; |