PR #25246 opened by SurbhiAgarwal1
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25246
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25246.patch

Fixes #25234

## Summary

For input formats deeper than 8 bits, `prec[c]` points directly to 
`ref->data[c]`. Previously, `get_wsse()` hard-coded the stride of `rec` to 
`s->plane_width[c]`, ignoring `ref->linesize[c]`. When the reference frame's 
linesize carries padding (`linesize != 2 * plane_width`), rows after the first 
were read at incorrect offsets, resulting in near-zero XPSNR values (< 1 dB).

This patch passes the per-plane stride of the reference frame into 
`get_wsse()`: `plane_width[c]` for packed 8-bit buffers and `ref->linesize[c] / 
bpp` for the high-bit-depth zero-copy path.

Additionally, fixes a bug in `calc_squared_error_and_weight()` where the return 
value of `highds()` was discarded for `b_val > 1 && w_act <= 12`, leaving 
`sa_act` at 0 on narrow edge blocks.


>From 32df5f5d92356d5d952cdd9a415c70bcdb17e162 Mon Sep 17 00:00:00 2001
From: Surbhi Agarwal <[email protected]>
Date: Tue, 20 Oct 2026 09:42:17 +0530
Subject: [PATCH 1/2] avfilter/vf_xpsnr: use reference frame linesize for
 high-bit-depth stride

For input formats deeper than 8 bits, prec[c] points directly to
ref->data[c]. Previously, get_wsse() hard-coded the stride of rec
to s->plane_width[c], ignoring ref->linesize[c]. When the reference
frame linesize carries padding (linesize != 2 * plane_width), rows
after the first were read at incorrect offsets.

Pass the per-plane stride of the reference frame to get_wsse():
plane_width[c] for packed 8-bit buffers and ref->linesize[c] / bpp
for high-bit-depth inputs.

Fixes: #25234
Signed-off-by: Surbhi Agarwal <[email protected]>
---
 libavfilter/vf_xpsnr.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/libavfilter/vf_xpsnr.c b/libavfilter/vf_xpsnr.c
index 89f4f279ba4..017a07ead46 100644
--- a/libavfilter/vf_xpsnr.c
+++ b/libavfilter/vf_xpsnr.c
@@ -267,7 +267,7 @@ static inline double get_avg_xpsnr (const double 
sqrt_wsse_val,  const double su
 }
 
 static int get_wsse(AVFilterContext *ctx, int16_t **org, int16_t *org_m1,
-                    int16_t *org_m2, int16_t **rec, uint64_t *const wsse64)
+                    int16_t *org_m2, int16_t **rec, const int *stride_rec, 
uint64_t *const wsse64)
 {
     XPSNRContext *const  s = ctx->priv;
     const uint32_t       w = s->plane_width [0]; /* luma image width in pixels 
*/
@@ -298,7 +298,7 @@ static int get_wsse(AVFilterContext *ctx, int16_t **org, 
int16_t *org_m1,
         const int16_t *p_org = org[0];
         const uint32_t s_org = stride_org[0] / s->bpp;
         const int16_t *p_rec = rec[0];
-        const uint32_t s_rec = s->plane_width[0];
+        const uint32_t s_rec = stride_rec[0];
         double     wsse_luma = 0.0;
 
         for (y = 0; y < h; y += b) { /* calculate block SSE and perceptual 
weights */
@@ -349,7 +349,7 @@ static int get_wsse(AVFilterContext *ctx, int16_t **org, 
int16_t *org_m1,
         const int16_t *p_org = org[c];
         const uint32_t s_org = stride_org[c] / s->bpp;
         const int16_t *p_rec = rec[c];
-        const uint32_t s_rec = s->plane_width[c];
+        const uint32_t s_rec = stride_rec[c];
         const uint32_t w_pln = s->plane_width[c];
         const uint32_t h_pln = s->plane_height[c];
 
@@ -405,6 +405,7 @@ static int do_xpsnr(FFFrameSync *fs)
     AVFrame *master, *ref = NULL;
     int16_t *porg   [3];
     int16_t *prec   [3];
+    int stride_rec  [3];
     uint64_t wsse64 [3] = {0, 0, 0};
     double cur_xpsnr[3] = {INFINITY, INFINITY, INFINITY};
     int c, ret_value, stride_org_bpp;
@@ -445,6 +446,7 @@ static int do_xpsnr(FFFrameSync *fs)
 
             porg[c] = s->buf_org[c];
             prec[c] = s->buf_rec[c];
+            stride_rec[c] = s->plane_width[c];
 
             for (int y = 0; y < s->plane_height[c]; y++) {
                 for (int x = 0; x < s->plane_width[c]; x++) {
@@ -457,11 +459,12 @@ static int do_xpsnr(FFFrameSync *fs)
         for (c = 0; c < s->num_comps; c++) {
             porg[c] = (int16_t *) master->data[c];
             prec[c] = (int16_t *)    ref->data[c];
+            stride_rec[c] = ref->linesize[c] / s->bpp;
         }
     }
 
     /* extended perceptually weighted peak signal-to-noise ratio (XPSNR) value 
*/
-    ret_value = get_wsse(ctx, porg, s->buf_org_m1, s->buf_org_m2, prec, 
wsse64);
+    ret_value = get_wsse(ctx, porg, s->buf_org_m1, s->buf_org_m2, prec, 
stride_rec, wsse64);
     if ( ret_value < 0 )
         return ret_value; /* an error here means something went wrong earlier! 
*/
 
-- 
2.54.0


>From dadabe9ab4384db12bd530548c2aafebeba9d99b Mon Sep 17 00:00:00 2001
From: Surbhi Agarwal <[email protected]>
Date: Tue, 20 Oct 2026 09:43:13 +0530
Subject: [PATCH 2/2] avfilter/vf_xpsnr: fix discarded return value of highds()

When b_val > 1 and w_act <= 12, the return value of highds() was
discarded, leaving sa_act at 0 for edge blocks.

Signed-off-by: Surbhi Agarwal <[email protected]>
---
 libavfilter/vf_xpsnr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavfilter/vf_xpsnr.c b/libavfilter/vf_xpsnr.c
index 017a07ead46..71e1f2fb7dd 100644
--- a/libavfilter/vf_xpsnr.c
+++ b/libavfilter/vf_xpsnr.c
@@ -194,7 +194,7 @@ static inline double calc_squared_error_and_weight 
(XPSNRContext const *s,
         if (w_act > 12)
             sa_act = s->dsp.highds_func(x_act, y_act, w_act, h_act, o_m0, o);
         else
-            highds(x_act, y_act, w_act, h_act, o_m0, o);
+            sa_act = highds(x_act, y_act, w_act, h_act, o_m0, o);
     } else { /* <=HD highpass without downsampling */
         for (int y = y_act; y < h_act; y++) {
             for (int x = x_act; x < w_act; x++) {
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to