PR #24038 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24038 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24038.patch
>From 9a41feea6e33a77f6aa1e80467a485e17c60d3af Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Thu, 6 Aug 2026 22:57:27 -0300 Subject: [PATCH] swresample/rematrix: handle missing channels from 22.2 layout Signed-off-by: James Almer <[email protected]> --- libswresample/rematrix.c | 137 ++++++++++++++++++++++++++-- libswresample/swresample_internal.h | 2 + libswresample/tests/rematrix.c | 31 +++++++ 3 files changed, 160 insertions(+), 10 deletions(-) diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index 269849a427..c92d2f4523 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -60,7 +60,13 @@ #define TOP_BACK_LEFT 15 #define TOP_BACK_CENTER 16 #define TOP_BACK_RIGHT 17 -#define NUM_NAMED_CHANNELS 18 +#define LOW_FREQUENCY_2 35 +#define TOP_SIDE_LEFT 36 +#define TOP_SIDE_RIGHT 37 +#define BOTTOM_FRONT_CENTER 38 +#define BOTTOM_FRONT_LEFT 39 +#define BOTTOM_FRONT_RIGHT 40 +#define NUM_NAMED_CHANNELS 41 int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride) { @@ -129,6 +135,10 @@ static int sane_layout(AVChannelLayout *ch_layout) { return 0; if(!even(av_channel_layout_subset(ch_layout, (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT)))) return 0; + if(!even(av_channel_layout_subset(ch_layout, (AV_CH_TOP_SIDE_LEFT | AV_CH_TOP_SIDE_RIGHT)))) + return 0; + if(!even(av_channel_layout_subset(ch_layout, (AV_CH_BOTTOM_FRONT_LEFT | AV_CH_BOTTOM_FRONT_RIGHT)))) + return 0; return 1; } @@ -299,6 +309,19 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay av_assert0(0); } + if (unaccounted & AV_CH_TOP_FRONT_CENTER) { + if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_FRONT_LEFT) >= 0) { + matrix[TOP_FRONT_LEFT ][TOP_FRONT_CENTER]+= M_SQRT1_2; + matrix[TOP_FRONT_RIGHT][TOP_FRONT_CENTER]+= M_SQRT1_2; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][TOP_FRONT_CENTER] += 1.0; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][TOP_FRONT_CENTER] += center_mix_level; + matrix[FRONT_RIGHT][TOP_FRONT_CENTER] += center_mix_level; + } else + av_assert0(0); + } + if (unaccounted & AV_CH_TOP_BACK_LEFT) { if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_BACK_CENTER) >= 0) { matrix[TOP_BACK_CENTER][TOP_BACK_LEFT ] += M_SQRT1_2; @@ -343,6 +366,96 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay av_assert0(0); } + + if (unaccounted & AV_CH_TOP_SIDE_LEFT) { + if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) == + (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) { + matrix[TOP_FRONT_LEFT ][TOP_SIDE_LEFT ] += SQRT2_3; + matrix[TOP_FRONT_RIGHT][TOP_SIDE_RIGHT] += SQRT2_3; + matrix[TOP_BACK_CENTER][TOP_SIDE_LEFT ] += SQRT1_3; + matrix[TOP_BACK_CENTER][TOP_SIDE_RIGHT] += SQRT1_3; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_BACK_LEFT) >= 0) { + matrix[TOP_BACK_LEFT ][TOP_SIDE_LEFT ] += M_SQRT1_2; + matrix[TOP_BACK_RIGHT][TOP_SIDE_RIGHT] += M_SQRT1_2; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_FRONT_LEFT) >= 0) { + matrix[TOP_FRONT_LEFT ][TOP_SIDE_LEFT ] += M_SQRT1_2; + matrix[TOP_FRONT_RIGHT][TOP_SIDE_RIGHT] += M_SQRT1_2; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_SIDE_LEFT) >= 0) { + matrix[SIDE_LEFT ][TOP_SIDE_LEFT ] += 1.0; + matrix[SIDE_RIGHT][TOP_SIDE_RIGHT] += 1.0; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BACK_LEFT) >= 0) { + matrix[BACK_LEFT ][TOP_SIDE_LEFT ] += 1.0; + matrix[BACK_RIGHT][TOP_SIDE_RIGHT] += 1.0; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][TOP_SIDE_LEFT ] += surround_mix_level; + matrix[FRONT_RIGHT][TOP_SIDE_RIGHT] += surround_mix_level; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][TOP_SIDE_LEFT ] += surround_mix_level*M_SQRT1_2; + matrix[FRONT_CENTER][TOP_SIDE_RIGHT] += surround_mix_level*M_SQRT1_2; + } else + av_assert0(0); + } + + if (unaccounted & AV_CH_TOP_CENTER) { + if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_LEFT)) == + (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_LEFT)) { + matrix[TOP_FRONT_LEFT ][TOP_CENTER] += 0.5; + matrix[TOP_FRONT_RIGHT][TOP_CENTER] += 0.5; + matrix[TOP_BACK_LEFT ][TOP_CENTER] += 0.5; + matrix[TOP_BACK_RIGHT][TOP_CENTER] += 0.5; + } else if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) == + (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) { + matrix[TOP_FRONT_LEFT ][TOP_CENTER] += SQRT1_3; + matrix[TOP_FRONT_RIGHT][TOP_CENTER] += SQRT1_3; + matrix[TOP_BACK_CENTER][TOP_CENTER] += SQRT1_3; + } else if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_BACK_LEFT)) == + (AV_CH_TOP_FRONT_LEFT|AV_CH_BACK_LEFT)) { + matrix[TOP_FRONT_LEFT ][TOP_CENTER] += 0.5; + matrix[TOP_FRONT_RIGHT][TOP_CENTER] += 0.5; + matrix[BACK_LEFT ][TOP_CENTER] += 0.5; + matrix[BACK_RIGHT][TOP_CENTER] += 0.5; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][TOP_CENTER] += 0.5; + matrix[FRONT_RIGHT][TOP_CENTER] += 0.5; + if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BACK_LEFT) >= 0) { + matrix[BACK_LEFT ][TOP_CENTER] += 0.5; + matrix[BACK_RIGHT][TOP_CENTER] += 0.5; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_SIDE_LEFT) >= 0) { + matrix[SIDE_LEFT ][TOP_CENTER] += 0.5; + matrix[SIDE_RIGHT][TOP_CENTER] += 0.5; + } + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][TOP_CENTER] += 0.5; + } else + av_assert0(0); + } + + if (unaccounted & AV_CH_BOTTOM_FRONT_CENTER) { + if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][BOTTOM_FRONT_CENTER] += 1.0; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][BOTTOM_FRONT_CENTER] += center_mix_level; + matrix[FRONT_RIGHT][BOTTOM_FRONT_CENTER] += center_mix_level; + } else + av_assert0(0); + } + + if (unaccounted & AV_CH_BOTTOM_FRONT_LEFT) { + if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BOTTOM_FRONT_CENTER) >= 0) { + matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_LEFT ] += M_SQRT1_2; + matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_RIGHT] += M_SQRT1_2; + if (av_channel_layout_index_from_channel(in_ch_layout, AV_CHAN_BOTTOM_FRONT_CENTER) >= 0) + matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_CENTER] = center_mix_level * sqrt(2); + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][BOTTOM_FRONT_LEFT ] += 1.0; + matrix[FRONT_RIGHT][BOTTOM_FRONT_RIGHT] += 1.0; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][BOTTOM_FRONT_LEFT ] += 1.0; + matrix[FRONT_CENTER][BOTTOM_FRONT_RIGHT] += 1.0; + } else + av_assert0(0); + } + /* mix LFE into front left/right or center */ if (unaccounted & AV_CH_LOW_FREQUENCY) { if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { @@ -354,6 +467,19 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay av_assert0(0); } + /* mix LFE2 into LFE, front left/right or center */ + if (unaccounted & AV_CH_LOW_FREQUENCY_2) { + if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_LOW_FREQUENCY) >= 0) { + matrix[LOW_FREQUENCY][LOW_FREQUENCY_2] += M_SQRT1_2; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) { + matrix[FRONT_CENTER][LOW_FREQUENCY_2] += lfe_mix_level; + } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) { + matrix[FRONT_LEFT ][LOW_FREQUENCY_2] += lfe_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][LOW_FREQUENCY_2] += lfe_mix_level * M_SQRT1_2; + } else + av_assert0(0); + } + for (i = 0; i < 64; i++) { double sum=0; @@ -413,15 +539,6 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL av_channel_layout_uninit(&in_ch_layout); in_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; } - if (!av_channel_layout_compare(&in_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_22POINT2) && - av_channel_layout_compare(&out_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_22POINT2)) { - av_channel_layout_from_mask(&in_ch_layout, (AV_CH_LAYOUT_7POINT1_WIDE_BACK|AV_CH_BACK_CENTER)); - av_channel_layout_describe(&in_ch_layout, buf, sizeof(buf)); - av_log(log_context, AV_LOG_WARNING, - "Full-on remixing from 22.2 has not yet been implemented! " - "Processing the input as '%s'\n", - buf); - } if(!av_channel_layout_check(&in_ch_layout)) { av_log(log_context, AV_LOG_ERROR, "Input channel layout is invalid\n"); diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h index ca2e0d7534..0cde2760bb 100644 --- a/libswresample/swresample_internal.h +++ b/libswresample/swresample_internal.h @@ -27,6 +27,8 @@ #define SWR_CH_MAX 64 +#define SQRT1_3 0.57735026918962576451 /* sqrt(1/3) */ +#define SQRT2_3 0.81649658092772603273 /* sqrt(2/3) */ #define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ #define NS_TAPS 20 diff --git a/libswresample/tests/rematrix.c b/libswresample/tests/rematrix.c index 4caf6d9678..4691823b82 100644 --- a/libswresample/tests/rematrix.c +++ b/libswresample/tests/rematrix.c @@ -97,12 +97,19 @@ int main(void) const AVChannelLayout surround_2 = AV_CHANNEL_LAYOUT_5POINT1POINT2; const AVChannelLayout surround_4 = AV_CHANNEL_LAYOUT_5POINT1POINT4_BACK; const AVChannelLayout surround_tbc = AV_CHANNEL_LAYOUT_7POINT2POINT3; + const AVChannelLayout surround_22_2 = AV_CHANNEL_LAYOUT_22POINT2; int ret = 0; ret |= check_coefficient(&surround_2, &stereo, AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_FRONT_LEFT, 1.0); ret |= check_coefficient(&surround_2, &stereo, AV_CHAN_TOP_FRONT_RIGHT, AV_CHAN_FRONT_RIGHT, 1.0); + ret |= check_coefficient(&surround_22_2, &surround_2, + AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_TOP_FRONT_LEFT, + M_SQRT1_2); + ret |= check_coefficient(&surround_22_2, &surround_2, + AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_TOP_FRONT_RIGHT, + M_SQRT1_2); ret |= check_coefficient(&surround_4, &surround_2, AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_FRONT_LEFT, M_SQRT1_2); @@ -165,6 +172,30 @@ int main(void) AV_CHAN_TOP_BACK_CENTER, AV_CHAN_FRONT_RIGHT, 0.5); ret |= check_coefficient(&surround_tbc, &mono, AV_CHAN_TOP_BACK_CENTER, AV_CHAN_FRONT_CENTER, 0.5); + ret |= check_coefficient(&surround_22_2, &surround_2, + AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_FRONT_LEFT, + M_SQRT1_2); + ret |= check_coefficient(&surround_22_2, &surround_2, + AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_TOP_FRONT_RIGHT, + M_SQRT1_2); + ret |= check_coefficient(&surround_22_2, &surround_tbc, + AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_BACK_CENTER, + 1.0/sqrt(3)); + ret |= check_coefficient(&surround_22_2, &surround_tbc, + AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_TOP_BACK_CENTER, + 1.0/sqrt(3)); + ret |= check_coefficient(&surround_22_2, &stereo, + AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_FRONT_LEFT, + M_SQRT1_2); + ret |= check_coefficient(&surround_22_2, &stereo, + AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_FRONT_RIGHT, + M_SQRT1_2); + ret |= check_coefficient_with_slev(&surround_22_2, &stereo, + AV_CHAN_TOP_SIDE_LEFT, + AV_CHAN_FRONT_LEFT, 0.5, 0.5); + ret |= check_coefficient_with_slev(&surround_22_2, &stereo, + AV_CHAN_TOP_SIDE_RIGHT, + AV_CHAN_FRONT_RIGHT, 0.5, 0.5); return ret; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
