Commit cce879874c for ffmpeg
commit cce879874ceadb9dc0ca41356b3b9c8974049955
Author: Frank Plowman <post@frankplowman.com>
Date: Sat Sep 5 14:24:10 2026 +0100
lavc/vvc: Use correct wrapping arithmetic for MVs
In the VVC spec, MVs are computed using 18-bit signed arithmetic with
normative wrapping. In H.266(V3), the wrapping behaviour is defined
in Eqs. (465)--(468) for block-wide MVs, Eqs. (658)--(661) for affine
CPMVs and (1086)--(1089) for IBC BVs. This wrapping was only
partially implemented in the VVC decoder before this patch.
Specifically, only eqs. (1087) and (1089) were implemented. This patch
implements the full wrapping as its own function `ff_vvc_wrap_mv`, and
uses it for all sorts of MVs.
Fixes mismatches and signed integer overflows in bitstreams with
unusually large MVs.
Signed-off-by: Frank Plowman <post@frankplowman.com>
diff --git a/libavcodec/vvc/ctu.c b/libavcodec/vvc/ctu.c
index 56b92ff37a..a69fc460c7 100644
--- a/libavcodec/vvc/ctu.c
+++ b/libavcodec/vvc/ctu.c
@@ -1616,6 +1616,7 @@ static void mvp_add_difference(MotionInfo *mi, const int num_cp_mv,
const Mv *mvd = &mvds[i][j];
mi->mv[i][j].x += mvd->x * (1 << amvr_shift);
mi->mv[i][j].y += mvd->y * (1 << amvr_shift);
+ ff_vvc_wrap_mv(&mi->mv[i][j]);
}
}
}
diff --git a/libavcodec/vvc/mvs.c b/libavcodec/vvc/mvs.c
index 955fe4b9fd..fe32b96555 100644
--- a/libavcodec/vvc/mvs.c
+++ b/libavcodec/vvc/mvs.c
@@ -1689,15 +1689,13 @@ static int ibc_history_candidates(const VVCLocalContext *lc,
return 0;
}
-#define MV_BITS 18
-#define IBC_SHIFT(v) ((v) >= (1 << (MV_BITS - 1)) ? ((v) - (1 << MV_BITS)) : (v))
-
static inline void ibc_add_mvp(Mv *mv, Mv *mvp, const int amvr_shift)
{
ff_vvc_round_mv(mv, amvr_shift, 0);
ff_vvc_round_mv(mvp, amvr_shift, amvr_shift);
- mv->x = IBC_SHIFT(mv->x + mvp->x);
- mv->y = IBC_SHIFT(mv->y + mvp->y);
+ mv->x += mvp->x;
+ mv->y += mvp->y;
+ ff_vvc_wrap_mv(mv);
}
static void ibc_merge_candidates(VVCLocalContext *lc, const int merge_idx, Mv *mv)
@@ -1897,10 +1895,22 @@ void ff_vvc_round_mv(Mv *mv, const int lshift, const int rshift)
}
}
+#define MV_BITS 18
+#define MV_MIN (-(1 << (MV_BITS - 1)))
+#define MV_MAX ((1 << (MV_BITS - 1)) - 1)
+
void ff_vvc_clip_mv(Mv *mv)
{
- mv->x = av_clip(mv->x, -(1 << 17), (1 << 17) - 1);
- mv->y = av_clip(mv->y, -(1 << 17), (1 << 17) - 1);
+ mv->x = av_clip(mv->x, MV_MIN, MV_MAX);
+ mv->y = av_clip(mv->y, MV_MIN, MV_MAX);
+}
+
+#define MV_WRAP(v) ((((v) & ((1 << MV_BITS) - 1)) ^ (1 << (MV_BITS - 1))) + MV_MIN)
+
+void ff_vvc_wrap_mv(Mv *mv)
+{
+ mv->x = MV_WRAP(mv->x);
+ mv->y = MV_WRAP(mv->y);
}
//8.5.2.1 Derivation process for motion vector components and reference indices
diff --git a/libavcodec/vvc/mvs.h b/libavcodec/vvc/mvs.h
index 7150c0b8cf..056f7c1ab3 100644
--- a/libavcodec/vvc/mvs.h
+++ b/libavcodec/vvc/mvs.h
@@ -27,6 +27,7 @@
void ff_vvc_round_mv(Mv *mv, int lshift, int rshift);
void ff_vvc_clip_mv(Mv *mv);
+void ff_vvc_wrap_mv(Mv *mv);
void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb);
void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, int merge_idx, int ciip_flag, MvField *mv);
void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv);