summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/chmap_sel.c86
-rw-r--r--test/chmap_sel.c8
2 files changed, 34 insertions, 60 deletions
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c
index 272e3e4baf..3d93a11a5c 100644
--- a/audio/chmap_sel.c
+++ b/audio/chmap_sel.c
@@ -199,55 +199,29 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
return false;
}
-enum {
- UPMIX_IDX,
- FALLBACK_UPMIX_IDX,
- DOWNMIX_IDX,
- FALLBACK_DOWNMIX_IDX,
- REMIX_COUNT
-};
-
-static bool test_fallbacks(struct mp_chmap *a, struct mp_chmap *b,
- int best_diffs[], struct mp_chmap best[])
+// 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)
{
- struct mp_chmap diff1, diff2;
+ int old_lost = mp_chmap_diffn(req, old); // num. channels only in req
+ int new_lost = mp_chmap_diffn(req, new);
- mp_chmap_diff(a, b, &diff1);
- if (mp_chmap_contains(a, b) && best_diffs[UPMIX_IDX] > diff1.num) {
- best[UPMIX_IDX] = *a;
- best_diffs[UPMIX_IDX] = diff1.num;
+ // Initial case
+ if (!old->num)
return true;
- }
- mp_chmap_diff(b, a, &diff2);
- if (mp_chmap_contains(b, a) && best_diffs[DOWNMIX_IDX] > diff2.num) {
- best[DOWNMIX_IDX] = *a;
- best_diffs[DOWNMIX_IDX] = diff2.num;
- return true;
- }
+ // Imperfect upmix (no real superset) - minimize lost channels
+ if (new_lost != old_lost)
+ return new_lost < old_lost;
- if (diff1.num > 0 && best_diffs[FALLBACK_UPMIX_IDX] > diff1.num) {
- best[FALLBACK_UPMIX_IDX] = *a;
- best_diffs[FALLBACK_UPMIX_IDX] = diff1.num;
- return true;
- }
-
- if (diff2.num > 0 && best_diffs[FALLBACK_DOWNMIX_IDX] > diff2.num) {
- best[FALLBACK_DOWNMIX_IDX] = *a;
- best_diffs[FALLBACK_DOWNMIX_IDX] = diff2.num;
- return true;
- }
-
- return false;
+ // 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.
+ return new->num < old->num;
}
-// Determine which channel map to fallback to given a source channel map. It
-// uses the following heuristic:
-// 1) If mono is required always prefer stereo to a multichannel upmix.
-// 2) Search for an upmix that is an exact superset of the required chmap.
-// 3) Search for a downmix that is the exact subset of the required chmap.
-// 4) Search for either an upmix or downmix that is the closest (minimum
-// difference of speakers) to the required chmap.
+// Determine which channel map to fallback to given a source channel map.
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
{
// special case: if possible always fallback mono to stereo (instead of
@@ -259,10 +233,7 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
return true;
}
- struct mp_chmap best[REMIX_COUNT] = {{0}};
- int best_diffs[REMIX_COUNT];
- for (int n = 0; n < REMIX_COUNT; n++)
- best_diffs[n] = INT_MAX;
+ struct mp_chmap best = {0};
for (int n = 0; n < s->num_chmaps; n++) {
struct mp_chmap e = s->chmaps[n];
@@ -270,25 +241,22 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
if (mp_chmap_is_unknown(&e))
continue;
- if (test_fallbacks(&e, map, best_diffs, best))
- continue;
-
// in case we didn't match any fallback retry after replacing speakers
- for (int i = 0; i < MP_ARRAY_SIZE(speaker_replacements); i++) {
- struct mp_chmap t = e;
- struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
- if (replace_speakers(&t, r)) {
- if (test_fallbacks(&t, map, best_diffs, best))
+ for (int i = -1; i < (int)MP_ARRAY_SIZE(speaker_replacements); i++) {
+ struct mp_chmap t = *map;
+ if (i >= 0) {
+ struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
+ if (!replace_speakers(&t, r))
continue;
}
+ if (mp_chmap_is_better(&t, &best, &e))
+ best = e;
}
}
- for (int i = 0; i < MP_ARRAY_SIZE(best); i++) {
- if (best_diffs[i] < INT_MAX) {
- *map = best[i];
- return true;
- }
+ if (best.num) {
+ *map = best;
+ return true;
}
return false;
diff --git a/test/chmap_sel.c b/test/chmap_sel.c
index b014e954ea..914e00368f 100644
--- a/test/chmap_sel.c
+++ b/test/chmap_sel.c
@@ -41,7 +41,7 @@ static void test_mp_chmap_sel_fallback_incompatible(void **state) {
}
static void test_mp_chmap_sel_fallback_prefer_compatible(void **state) {
- test_sel("7.1(wide-side)", "7.1", LAYOUTS("7.1", "5.1(side)"));
+ test_sel("7.1(wide-side)", "5.1(side)", LAYOUTS("7.1", "5.1(side)"));
}
static void test_mp_chmap_sel_fallback_prefer_closest_upmix(void **state) {
@@ -68,6 +68,11 @@ static void test_mp_chmap_sel_fallback_no_downmix(void **state) {
test_sel("5.1(side)", "7.1(rear)", LAYOUTS("stereo", "7.1(rear)"));
}
+static void test_mp_chmap_sel_fallback_minimal_downmix(void **state) {
+ test_sel("7.1", "fl-fr-lfe-fc-bl-br-flc-frc",
+ LAYOUTS("fl-fr-lfe-fc-bl-br-flc-frc", "3.0(back)"));
+}
+
static void test_mp_chmap_sel_fallback_reject_unknown(void **state) {
struct mp_chmap a;
struct mp_chmap b;
@@ -94,6 +99,7 @@ int main(void) {
unit_test(test_mp_chmap_sel_fallback_mono_to_stereo),
unit_test(test_mp_chmap_sel_fallback_stereo_to_stereo),
unit_test(test_mp_chmap_sel_fallback_no_downmix),
+ unit_test(test_mp_chmap_sel_fallback_minimal_downmix),
unit_test(test_mp_chmap_sel_fallback_reject_unknown),
};
return run_tests(tests);