diff options
-rw-r--r-- | audio/chmap.c | 70 | ||||
-rw-r--r-- | audio/chmap.h | 9 | ||||
-rw-r--r-- | audio/filter/af_lavrresample.c | 119 |
3 files changed, 142 insertions, 56 deletions
diff --git a/audio/chmap.c b/audio/chmap.c index aec6a9aa05..7e62dab74a 100644 --- a/audio/chmap.c +++ b/audio/chmap.c @@ -190,6 +190,33 @@ void mp_chmap_reorder_norm(struct mp_chmap *map) qsort(arr, map->num, 1, comp_uint8); } +// Remove silent (NA) channels, if any. +void mp_chmap_remove_na(struct mp_chmap *map) +{ + struct mp_chmap new = {0}; + for (int n = 0; n < map->num; n++) { + int sp = map->speaker[n]; + if (!(sp >= MP_SPEAKER_ID_NA0 && sp <= MP_SPEAKER_ID_NA_LAST)) + new.speaker[new.num++] = map->speaker[n]; + } + *map = new; +} + +// Add silent (NA) channels to map until map->num >= num. +void mp_chmap_fill_na(struct mp_chmap *map, int num) +{ + assert(num <= MP_NUM_CHANNELS); + while (map->num < num) { + int sp = map->num ? map->speaker[map->num - 1] : -1; + if (sp >= MP_SPEAKER_ID_NA0 && sp < MP_SPEAKER_ID_NA_LAST) { + sp += 1; + } else { + sp = MP_SPEAKER_ID_NA0; + } + map->speaker[map->num++] = sp; + } +} + // Set *dst to a standard layout with the given number of channels. // If the number of channels is invalid, an invalid map is set, and // mp_chmap_is_valid(dst) will return false. @@ -279,7 +306,7 @@ uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src) mp_chmap_from_channels(&t, t.num); uint64_t mask = 0; for (int n = 0; n < t.num; n++) { - if (t.speaker[n] < 64) + if (t.speaker[n] < 64) // ignore MP_SPEAKER_ID_NA* etc. mask |= 1ULL << t.speaker[n]; } return mask; @@ -342,33 +369,35 @@ void mp_chmap_reorder_to_lavc(struct mp_chmap *map) // Get reordering array for from->to reordering. from->to must have the same set // of speakers (i.e. same number and speaker IDs, just different order). Then, -// for each speaker n, dst[n] will be set such that: -// to->speaker[dst[n]] = from->speaker[n] -// (dst[n] gives the source channel for destination channel n) -void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from, +// for each speaker n, src[n] will be set such that: +// to->speaker[n] = from->speaker[src[n]] +// (src[n] gives the source channel for destination channel n) +// If *from and *to don't contain the same set of speakers, then the above +// invariant is not guaranteed. Instead, src[n] can be set to -1 if the channel +// at to->speaker[n] is unmapped. +void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from, const struct mp_chmap *to) { - assert(from->num == to->num); + for (int n = 0; n < MP_NUM_CHANNELS; n++) + src[n] = -1; + if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) { - for (int n = 0; n < from->num; n++) - dst[n] = n; + for (int n = 0; n < to->num; n++) + src[n] = n < from->num ? n : -1; return; } - // Same set of speakers required - assert(mp_chmap_equals_reordered(from, to)); + for (int n = 0; n < from->num; n++) { - int src = from->speaker[n]; - dst[n] = -1; for (int i = 0; i < to->num; i++) { - if (src == to->speaker[i]) { - dst[n] = i; + if (to->speaker[n] == from->speaker[i]) { + src[n] = i; break; } } - assert(dst[n] != -1); } - for (int n = 0; n < from->num; n++) - assert(to->speaker[dst[n]] == from->speaker[n]); + + for (int n = 0; n < to->num; n++) + assert(to->speaker[n] == src[n] < 0 ? -1 : from->speaker[src[n]]); } // Performs the difference between a and b, and store it in diff. If b has @@ -411,7 +440,12 @@ char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL; char sp_buf[10]; if (!s) { - snprintf(sp_buf, sizeof(sp_buf), "sp%d", sp); + const char *prefix = "sp"; + if (sp >= MP_SPEAKER_ID_NA0 && sp <= MP_SPEAKER_ID_NA_LAST) { + sp -= MP_SPEAKER_ID_NA0; + prefix = "na"; + } + snprintf(sp_buf, sizeof(sp_buf), "%s%d", prefix, sp); s = sp_buf; } mp_snprintf_cat(buf, buf_size, "%s%s", n > 0 ? "-" : "", s); diff --git a/audio/chmap.h b/audio/chmap.h index 1ae4fb0697..296b2a1712 100644 --- a/audio/chmap.h +++ b/audio/chmap.h @@ -61,6 +61,11 @@ enum mp_speaker_id { MP_SPEAKER_ID_UNKNOWN0 = 64, MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1, + // "Silent" channels. These are sometimes used to insert padding for + // unused channels. + MP_SPEAKER_ID_NA0, + MP_SPEAKER_ID_NA_LAST = MP_SPEAKER_ID_NA0 + MP_NUM_CHANNELS - 1, + // Including the unassigned IDs in between. This is not a valid ID anymore, // but is still within uint8_t. MP_SPEAKER_ID_COUNT, @@ -101,6 +106,8 @@ bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap * bool mp_chmap_is_stereo(const struct mp_chmap *src); void mp_chmap_reorder_norm(struct mp_chmap *map); +void mp_chmap_remove_na(struct mp_chmap *map); +void mp_chmap_fill_na(struct mp_chmap *map, int num); void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels); void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels); @@ -116,7 +123,7 @@ void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src); bool mp_chmap_is_lavc(const struct mp_chmap *src); void mp_chmap_reorder_to_lavc(struct mp_chmap *map); -void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from, +void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from, const struct mp_chmap *to); void mp_chmap_diff(const struct mp_chmap *a, const struct mp_chmap *b, 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; |