From 433402b56cdc2a32a6cb45d13a234b9617a3c9f9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 12 Jun 2015 17:53:23 +0200 Subject: audio: fill NA channels with silence Until now, we didn't do this, because it required some effort, and didn't seem to be necessary. It probably still isn't, but it sounds like a good idea not to output arbitrary data on these channels. The situation is complicated by the fact that just adding new channels to a planar frame would require messing with buffers. So we would have to allocate new buffers and add them to the frame. We could have to maintain an extra buffer pool for this. Avoid this by being "clever", and just allocate a frame with enough channels in the first place. libav/swresample won't know about these channels and won't write to them, but we can grab them in reorder_planes() and use them for the NA channels. --- audio/filter/af_lavrresample.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index 869f5840ab..4683025a7e 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -81,6 +81,7 @@ struct af_resample { bool avrctx_ok; struct AVAudioResampleContext *avrctx; struct mp_audio avrctx_fmt; // output format of avrctx + struct mp_audio pool_fmt; // format used to allocate frames for avrctx output 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 @@ -273,6 +274,14 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, mp_audio_set_channels(&s->avrctx_fmt, &out_lavc); mp_audio_set_format(&s->avrctx_fmt, af_from_avformat(out_samplefmtp)); + // If there are NA channels, the final output will have more channels than + // the avrctx output. Also, avrctx will output planar (out_samplefmtp was + // not overwritten). Allocate the output frame with more channels, so the + // NA channels can be trivially added. + s->pool_fmt = s->avrctx_fmt; + if (map_out.num > out_lavc.num) + mp_audio_set_channels(&s->pool_fmt, &map_out); + // 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); @@ -390,22 +399,28 @@ static void uninit(struct af_instance *af) avresample_free(&s->avrctx_out); } +// This relies on the tricky way mpa was allocated. static void reorder_planes(struct mp_audio *mpa, int *reorder, struct mp_chmap *newmap) { struct mp_audio prev = *mpa; + mp_audio_set_channels(mpa, newmap); + + // The trailing planes were never written by avrctx, they're the NA channels. + int next_na = prev.num_planes; - for (int n = 0; n < newmap->num; n++) { + for (int n = 0; n < mpa->num_planes; n++) { int src = reorder[n]; - if (src < 0) { - src = 0; // Use the first plane for padding channels. - mpa->readonly = true; + assert(src >= -1 && src < prev.num_planes); + if (src >= 0) { + mpa->planes[n] = prev.planes[src]; + } else { + assert(next_na < mpa->num_planes); + mpa->planes[n] = prev.planes[next_na++]; + af_fill_silence(mpa->planes[n], mpa->sstride * mpa->samples, + mpa->format); } - 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) @@ -414,7 +429,7 @@ static int filter(struct af_instance *af, struct mp_audio *in) int samples = get_out_samples(s, in ? in->samples : 0); - struct mp_audio out_format = s->avrctx_fmt; + struct mp_audio out_format = s->pool_fmt; struct mp_audio *out = mp_audio_pool_get(af->out_pool, &out_format, samples); if (!out) goto error; @@ -427,9 +442,12 @@ static int filter(struct af_instance *af, struct mp_audio *in) goto error; } + struct mp_audio real_out = *out; + mp_audio_copy_config(out, &s->avrctx_fmt); + 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); + assert(AF_FORMAT_IS_PLANAR(out->format) && out->format == real_out.format); + reorder_planes(out, s->reorder_out, &s->pool_fmt.channels); if (!mp_audio_config_equals(out, af->data)) { struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, af->data, out->samples); -- cgit v1.2.3