diff options
-rw-r--r-- | audio/chmap_sel.c | 97 | ||||
-rw-r--r-- | test/chmap_sel.c | 10 |
2 files changed, 71 insertions, 36 deletions
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c index d1f6f2eee0..b0b477cb45 100644 --- a/audio/chmap_sel.c +++ b/audio/chmap_sel.c @@ -200,25 +200,72 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map) return false; } +// Like mp_chmap_diffn(), but find the minimum difference with all possible +// speaker replacements considered. +static int mp_chmap_diffn_r(const struct mp_chmap *a, const struct mp_chmap *b) +{ + int mindiff = INT_MAX; + + for (int i = -1; i < (int)MP_ARRAY_SIZE(speaker_replacements); i++) { + struct mp_chmap ar = *a; + if (i >= 0) { + struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i]; + if (!replace_speakers(&ar, r)) + continue; + } + int d = mp_chmap_diffn(&ar, b); + if (d < mindiff) + mindiff = d; + } + + return mindiff; +} + // Decide whether we should prefer old or new for the requested layout. // Return true if new should be used, false if old should be used. // If old is empty, always return new (initial case). static bool mp_chmap_is_better(struct mp_chmap *req, struct mp_chmap *old, struct mp_chmap *new) { - int old_lost = mp_chmap_diffn(req, old); // num. channels only in req - int new_lost = mp_chmap_diffn(req, new); - // Initial case if (!old->num) return true; + // Exact pick - this also ensures that the best layout is chosen if the + // layouts are the same, but with different order of channels. + if (mp_chmap_equals(req, old)) + return false; + if (mp_chmap_equals(req, new)) + return true; + + int old_lost_r = mp_chmap_diffn_r(req, old); // num. channels only in req + int new_lost_r = mp_chmap_diffn_r(req, new); + // Imperfect upmix (no real superset) - minimize lost channels + if (new_lost_r != old_lost_r) + return new_lost_r < old_lost_r; + + int old_lost = mp_chmap_diffn(req, old); + int new_lost = mp_chmap_diffn(req, new); + + // If the situation is equal with replaced speakers, but one of them loses + // less if no replacements are performed, pick the better one, even if it + // means an upmix. This prefers exact supersets over inexact equivalents. if (new_lost != old_lost) return new_lost < old_lost; + struct mp_chmap old_p = *old, new_p = *new; + mp_chmap_remove_na(&old_p); + mp_chmap_remove_na(&new_p); + // Some kind of upmix. If it's perfect, prefer the smaller one. Even if not, // both have equal loss, so also prefer the smaller one. + // Drop padding channels (NA) for the sake of this check, as the number of + // padding channels isn't really meaningful. + if (new_p.num != old_p.num) + return new_p.num < old_p.num; + + // Again, with physical channels (minimizes number of NA channels). return new->num < old->num; } @@ -234,45 +281,23 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map) return true; } - struct mp_chmap best_of_best = {0}; + // Add layouts and replaced layouts. Layouts with replacements applied + // are considered equivalent to the original, but the ori - for (int i = -1; i < (int)MP_ARRAY_SIZE(speaker_replacements); i++) { - struct mp_chmap best = {0}; - struct mp_chmap t = *map; + struct mp_chmap best = {0}; - if (i >= 0) { - struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i]; - if (!replace_speakers(&t, r)) - continue; - } - - for (int n = 0; n < s->num_chmaps; n++) { - struct mp_chmap e = s->chmaps[n]; - - if (mp_chmap_is_unknown(&e)) - continue; + for (int n = 0; n < s->num_chmaps; n++) { + struct mp_chmap e = s->chmaps[n]; - if (mp_chmap_is_better(&t, &best, &e)) - best = e; - } + if (mp_chmap_is_unknown(&e)) + continue; - if (best.num) { - if (best_of_best.num) { - // If best (without replacements) is not worse, but is actually - // better with replacements applied, pick it. - int bbest_lost = mp_chmap_diffn(map, &best_of_best); - int best_lost = mp_chmap_diffn(map, &best); - int repl_lost = mp_chmap_diffn(&t, &best); - if (best_lost <= bbest_lost && repl_lost < bbest_lost) - best_of_best = best; - } else { - best_of_best = best; - } - } + if (mp_chmap_is_better(map, &best, &e)) + best = e; } - if (best_of_best.num) { - *map = best_of_best; + if (best.num) { + *map = best; return true; } diff --git a/test/chmap_sel.c b/test/chmap_sel.c index f4299e1146..0301045e23 100644 --- a/test/chmap_sel.c +++ b/test/chmap_sel.c @@ -99,6 +99,15 @@ static void test_mp_chmap_sel_fallback_more_replacements(void **state) { LAYOUTS("fl-fr-bl-br-na-na-na-na", "quad(side)", "stereo")); test_sel("quad", "fl-fr-bl-br-na-na-na-na", LAYOUTS("stereo", "quad(side)", "fl-fr-bl-br-na-na-na-na")); + test_sel("fl-fr-fc-lfe-sl-sr", "fl-fr-lfe-fc-bl-br-na-na", + LAYOUTS("fl-fr-lfe-fc-bl-br-na-na", "fl-fr-lfe-fc-bl-br-sdl-sdr")); + test_sel("fl-fr-fc-lfe-sl-sr", "fl-fr-lfe-fc-bl-br-na-na", + LAYOUTS("fl-fr-lfe-fc-bl-br-sdl-sdr", "fl-fr-lfe-fc-bl-br-na-na")); +} + +static void test_mp_chmap_sel_fallback_na_channels(void **state) { + test_sel("na-fl-fr", "na-fl-fr", LAYOUTS("na-fl-fr-na", "fl-na-fr", "na-fl-fr", + "fl-fr-na-na", "na-na-fl-fr")); } int main(void) { @@ -116,6 +125,7 @@ int main(void) { cmocka_unit_test(test_mp_chmap_sel_fallback_minimal_downmix), cmocka_unit_test(test_mp_chmap_sel_fallback_reject_unknown), cmocka_unit_test(test_mp_chmap_sel_fallback_more_replacements), + cmocka_unit_test(test_mp_chmap_sel_fallback_na_channels), }; return cmocka_run_group_tests(tests, NULL, NULL); } |