summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/chmap.c70
-rw-r--r--audio/chmap.h9
-rw-r--r--audio/filter/af_lavrresample.c119
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;