PR #25377 opened by Daniel Campos Ramos (danielcamposramos) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25377 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25377.patch
avfilter/stereo3d: correct eye order for interleaved inputs `stereo3d=icr:sbsl` exchanges the eyes on master 179d0834934. With an analytic RGB24 input at 128 by 48, every one of the 6,144 output pixels is in the wrong eye. The input has a red left view, a blue right view and a different white box in each, with the right view in the even columns. The column path swaps the output offsets and also chooses source columns from the input and output order. The first patch chooses each source column from the input order and lets the output layout place that eye. It also corrects column reversal and right eye first alternating output. The family checks found that both mono outputs from right eye first rows select the other eye, which the second patch fixes by keeping the proper row offset. Both patches have an analytic FATE regression that fails before its fix. The tests use frame MD5 because the exchanged images happen to have the same Adler checksum. GCC and clang each pass all 24 stereo3d FATE tests and 516 independent conversion checks at one and four filter threads. The checks cover both orders of every supported input in RGB24, plus the interleaved family in planar RGB, 16 bit planar RGB, RGBA, 48 bit RGB and 64 bit RGBA. Checkerboard outputs from column inputs are checked in both orders; checkerboard input is not supported by this filter. The fixed icr conversion matches all 6,144 expected pixels exactly. The tests generate their inputs without using stereo3d and need no new sample download. AI partners helped produce this work. Fixes #25376 >From 11b58c219fe5ebacb72320efd708cde1bb7d1658 Mon Sep 17 00:00:00 2001 From: Daniel Campos Ramos <[email protected]> Date: Fri, 23 Oct 2026 00:10:20 -0300 Subject: [PATCH 1/2] avfilter/stereo3d: preserve eye order for interleaved columns Select the source column from the input eye order and leave the output placement to the selected output layout. Swapping the configured offsets as well as the source columns reversed icr packed output. Depending on the output enum parity also reversed right-first alternating output. Use the actual input order when interleaving columns again, so icl:icr exchanges the eyes instead of copying the original column order. Add an analytic RGB FATE regression with different markers in each eye. Use frame MD5 because the exchanged views have the same Adler checksum. The reference is generated from independently packed left and right images, and fails on unpatched master. Signed-off-by: Daniel Campos Ramos <[email protected]> Assisted-by: gpt-6-astra via Codex --- libavfilter/vf_stereo3d.c | 19 +++++-------------- tests/fate/filter-video.mak | 9 +++++++++ tests/filtergraphs/stereo3d-icr | 8 ++++++++ tests/ref/fate/filter-stereo3d-icr-sbsl | 10 ++++++++++ 4 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 tests/filtergraphs/stereo3d-icr create mode 100644 tests/ref/fate/filter-stereo3d-icr-sbsl diff --git a/libavfilter/vf_stereo3d.c b/libavfilter/vf_stereo3d.c index 15d6f31c11f..b53eb5ae310 100644 --- a/libavfilter/vf_stereo3d.c +++ b/libavfilter/vf_stereo3d.c @@ -576,17 +576,6 @@ static int config_output(AVFilterLink *outlink) return AVERROR(EINVAL); } - if (s->in.format == INTERLEAVE_COLS_LR || s->in.format == INTERLEAVE_COLS_RL) { - if ((s->in.format & 1) != (s->out.format & 1)) { - FFSWAP(int, s->in.row_left, s->in.row_right); - FFSWAP(int, s->in.off_lstep, s->in.off_rstep); - FFSWAP(int, s->in.off_left, s->in.off_right); - FFSWAP(int, s->out.row_left, s->out.row_right); - FFSWAP(int, s->out.off_lstep, s->out.off_rstep); - FFSWAP(int, s->out.off_left, s->out.off_right); - } - } - outlink->w = s->out.width; outlink->h = s->out.height; ol->frame_rate = fps; @@ -860,7 +849,7 @@ copy: if (s->in.format == INTERLEAVE_COLS_LR || s->in.format == INTERLEAVE_COLS_RL) { for (i = 0; i < s->nb_planes; i++) { - int d = (s->in.format & 1) != (s->out.format & 1); + int d = s->in.format == INTERLEAVE_COLS_RL; interleave_cols_to_any(s, out_off_left, i, ileft, oleft, d); interleave_cols_to_any(s, out_off_right, i, iright, oright, !d); @@ -1021,12 +1010,14 @@ copy: for (i = 0; i < s->nb_planes; i++) { const int d = (s->in.format == INTERLEAVE_COLS_LR || s->in.format == INTERLEAVE_COLS_RL); const int m = 1 + d; + const int l = s->in.format == INTERLEAVE_COLS_RL; + const int r = s->in.format == INTERLEAVE_COLS_LR; int x, y; for (y = 0; y < s->pheight[i]; y++) { uint8_t *dst = out->data[i] + out->linesize[i] * y; - uint8_t *left = ileft->data[i] + ileft->linesize[i] * y * s->in.row_step + s->in_off_left[i] + d * s->pixstep[i]; - uint8_t *right = iright->data[i] + iright->linesize[i] * y * s->in.row_step + s->in_off_right[i]; + uint8_t *left = ileft->data[i] + ileft->linesize[i] * y * s->in.row_step + s->in_off_left[i] + l * s->pixstep[i]; + uint8_t *right = iright->data[i] + iright->linesize[i] * y * s->in.row_step + s->in_off_right[i] + r * s->pixstep[i]; int p, b; if (s->out.format == INTERLEAVE_COLS_LR) diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index 4a065ec0320..9f9e6713ed1 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -733,6 +733,15 @@ FATE_STEREO3D-$(call FILTERDEMDEC, SCALE STEREO3D, IMAGE2, PGMYUV) += $(FATE_STE fate-filter-stereo3d: $(FATE_STEREO3D-yes) FATE_FILTER_VSYNTH-yes += $(FATE_STEREO3D-yes) +# Analytic right-first columns: red left view, blue right view, different white boxes. +FATE_STEREO3D_ICR-$(call ALLYES, NULLSRC_FILTER GEQ_FILTER FORMAT_FILTER SCALE_FILTER \ + STEREO3D_FILTER RAWVIDEO_ENCODER FRAMEMD5_MUXER \ + FILE_PROTOCOL PIPE_PROTOCOL) += fate-filter-stereo3d-icr-sbsl +fate-filter-stereo3d-icr-sbsl: tests/data/filtergraphs/stereo3d-icr +fate-filter-stereo3d-icr-sbsl: CMD = framemd5 -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/stereo3d-icr -frames:v 1 -pix_fmt rgb24 +FATE_FILTER-yes += $(FATE_STEREO3D_ICR-yes) +fate-filter-stereo3d: $(FATE_STEREO3D_ICR-yes) + FATE_FILTER_VSYNTH_VIDEO_FILTER-$(call ALLYES, SCALE_FILTER THUMBNAIL_FILTER) += fate-filter-thumbnail fate-filter-thumbnail: CMD = video_filter "scale,thumbnail=10" diff --git a/tests/filtergraphs/stereo3d-icr b/tests/filtergraphs/stereo3d-icr new file mode 100644 index 00000000000..d9d063aae7a --- /dev/null +++ b/tests/filtergraphs/stereo3d-icr @@ -0,0 +1,8 @@ +nullsrc=s=128x48:r=1, +format=gbrp, +geq=r='if(mod(X,2),255,255*between(X,64,79)*between(Y,24,31))': + g='if(mod(X,2),255*between(X,16,31)*between(Y,8,15),255*between(X,64,79)*between(Y,24,31))': + b='if(mod(X,2),255*between(X,16,31)*between(Y,8,15),255)', +scale, +format=rgb24, +stereo3d=icr:sbsl diff --git a/tests/ref/fate/filter-stereo3d-icr-sbsl b/tests/ref/fate/filter-stereo3d-icr-sbsl new file mode 100644 index 00000000000..0f830b7cc29 --- /dev/null +++ b/tests/ref/fate/filter-stereo3d-icr-sbsl @@ -0,0 +1,10 @@ +#format: frame checksums +#version: 2 +#hash: MD5 +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 128x48 +#sar 0: 1/1 +#stream#, dts, pts, duration, size, hash +0, 0, 0, 1, 18432, 04d7d564965d41f85a201062e55ee9de -- 2.54.0 >From d4be831f8196c887d06b7057b677e13fba978339 Mon Sep 17 00:00:00 2001 From: Daniel Campos Ramos <[email protected]> Date: Fri, 23 Oct 2026 00:13:40 -0300 Subject: [PATCH 2/2] avfilter/stereo3d: select the correct mono view from interleaved rows Keep the configured left row offset for mono-left output and copy the right row offset for mono-right output. Swapping offsets only for one input order selected the other eye for both irr mono outputs. Add a FATE regression that independently generates right-first rows, extracts both mono views and joins them in left-first order. It shares the analytic reference of the column test and fails before this change. Signed-off-by: Daniel Campos Ramos <[email protected]> Assisted-by: gpt-6-astra via Codex --- libavfilter/vf_stereo3d.c | 11 +++-------- tests/fate/filter-video.mak | 9 +++++++++ tests/filtergraphs/stereo3d-irr-mono | 11 +++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 tests/filtergraphs/stereo3d-irr-mono diff --git a/libavfilter/vf_stereo3d.c b/libavfilter/vf_stereo3d.c index b53eb5ae310..81eea9e1f27 100644 --- a/libavfilter/vf_stereo3d.c +++ b/libavfilter/vf_stereo3d.c @@ -549,16 +549,11 @@ static int config_output(AVFilterLink *outlink) s->out.off_lstep = 1; break; case MONO_R: - if (s->in.format != INTERLEAVE_COLS_LR) { - s->in.off_left = s->in.off_right; - s->in.row_left = s->in.row_right; - } - if (s->in.format == INTERLEAVE_ROWS_LR) - FFSWAP(int, s->in.off_lstep, s->in.off_rstep); + s->in.off_left = s->in.off_right; + s->in.row_left = s->in.row_right; + s->in.off_lstep = s->in.off_rstep; break; case MONO_L: - if (s->in.format == INTERLEAVE_ROWS_RL) - FFSWAP(int, s->in.off_lstep, s->in.off_rstep); break; case ALTERNATING_RL: case ALTERNATING_LR: diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index 9f9e6713ed1..6f9b9ac7e24 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -742,6 +742,15 @@ fate-filter-stereo3d-icr-sbsl: CMD = framemd5 -/filter_complex $(TARGET_PATH)/te FATE_FILTER-yes += $(FATE_STEREO3D_ICR-yes) fate-filter-stereo3d: $(FATE_STEREO3D_ICR-yes) +FATE_STEREO3D_IRR-$(call ALLYES, NULLSRC_FILTER GEQ_FILTER FORMAT_FILTER SCALE_FILTER \ + SPLIT_FILTER HSTACK_FILTER STEREO3D_FILTER \ + RAWVIDEO_ENCODER FRAMEMD5_MUXER FILE_PROTOCOL PIPE_PROTOCOL) += fate-filter-stereo3d-irr-mono +fate-filter-stereo3d-irr-mono: tests/data/filtergraphs/stereo3d-irr-mono +fate-filter-stereo3d-irr-mono: CMD = framemd5 -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/stereo3d-irr-mono -frames:v 1 -pix_fmt rgb24 +fate-filter-stereo3d-irr-mono: REF = $(SRC_PATH)/tests/ref/fate/filter-stereo3d-icr-sbsl +FATE_FILTER-yes += $(FATE_STEREO3D_IRR-yes) +fate-filter-stereo3d: $(FATE_STEREO3D_IRR-yes) + FATE_FILTER_VSYNTH_VIDEO_FILTER-$(call ALLYES, SCALE_FILTER THUMBNAIL_FILTER) += fate-filter-thumbnail fate-filter-thumbnail: CMD = video_filter "scale,thumbnail=10" diff --git a/tests/filtergraphs/stereo3d-irr-mono b/tests/filtergraphs/stereo3d-irr-mono new file mode 100644 index 00000000000..f2850cc1c0a --- /dev/null +++ b/tests/filtergraphs/stereo3d-irr-mono @@ -0,0 +1,11 @@ +nullsrc=s=64x96:r=1, +format=gbrp, +geq=r='if(mod(Y,2),255,255*between(X,32,39)*between(Y,48,63))': + g='if(mod(Y,2),255*between(X,8,15)*between(Y,16,31),255*between(X,32,39)*between(Y,48,63))': + b='if(mod(Y,2),255*between(X,8,15)*between(Y,16,31),255)', +scale, +format=rgb24, +split[left][right]; +[left]stereo3d=irr:ml[l]; +[right]stereo3d=irr:mr[r]; +[l][r]hstack -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
