Commit 21441b3400 for ffmpeg
commit 21441b3400342e1d4d2273f28bddd2368fd0cca4
Author: Charly Morgand-Poyac <charly.morgand-poyac@bbright.com>
Date: Sat Jul 25 19:37:30 2026 +0200
avcodec/hevc/filter: fix deblocking offsets taken from the wrong CTB
Equations 8-348, 8-350 and 8-383 take slice_beta_offset_div2 and
slice_tc_offset_div2 from the slice that contains the q0,0 sample of the
edge: the current CTB for a vertical edge, and for a horizontal one the CTB
that contains the sample group.
deblocking_filter_CTB() reused whatever the previous loop had left in
tc_offset and beta_offset instead. Vertical edges could take the left CTB's
offsets, and each horizontal chroma sample group mixed the offsets of both
CTBs.
This only changes the output when a CTB and its left neighbour come from
slices with different deblocking offsets.
Fixes: 65746bfbae3d1077656b48820f0c18c5703e34f7 (hevc_filter: run vertical and horizontal together)
Fixes: fa0506e4136b5b883205c82809a113f5edcd200b (hevc/rext: update boundary strength for chroma deblocking filter)
Signed-off-by: Charly Morgand-Poyac <charly.morgand-poyac@bbright.com>
diff --git a/libavcodec/hevc/filter.c b/libavcodec/hevc/filter.c
index aee73df034..a4f1b24db7 100644
--- a/libavcodec/hevc/filter.c
+++ b/libavcodec/hevc/filter.c
@@ -540,14 +540,13 @@ static void deblocking_filter_CTB(const HEVCContext *s, const HEVCLayerContext *
if (y_end > sps->height)
y_end = sps->height;
- tc_offset = cur_tc_offset;
- beta_offset = cur_beta_offset;
-
x_end2 = x_end;
if (x_end2 != sps->width)
x_end2 -= 8;
for (y = y0; y < y_end; y += 8) {
// vertical filtering luma
+ tc_offset = cur_tc_offset;
+ beta_offset = cur_beta_offset;
for (x = x0 ? x0 : 8; x < x_end; x += 8) {
const int bs0 = l->vertical_bs[(x + y * l->bs_width) >> 2];
const int bs1 = l->vertical_bs[(x + (y + 4) * l->bs_width) >> 2];
@@ -622,8 +621,8 @@ static void deblocking_filter_CTB(const HEVCContext *s, const HEVCLayerContext *
const int qp1 = (get_qPy(sps, l->qp_y_tab, x - 1, y + (4 * v)) +
get_qPy(sps, l->qp_y_tab, x, y + (4 * v)) + 1) >> 1;
- c_tc[0] = (bs0 == 2) ? chroma_tc(pps, sps, qp0, chroma, tc_offset) : 0;
- c_tc[1] = (bs1 == 2) ? chroma_tc(pps, sps, qp1, chroma, tc_offset) : 0;
+ c_tc[0] = (bs0 == 2) ? chroma_tc(pps, sps, qp0, chroma, cur_tc_offset) : 0;
+ c_tc[1] = (bs1 == 2) ? chroma_tc(pps, sps, qp1, chroma, cur_tc_offset) : 0;
src = &data[chroma][(y >> sps->vshift[chroma]) * linesize[chroma] + ((x >> sps->hshift[chroma]) << sps->pixel_shift)];
if (pcmf) {
no_p[0] = get_pcm(sps, l->is_pcm, x - 1, y);
@@ -642,7 +641,6 @@ static void deblocking_filter_CTB(const HEVCContext *s, const HEVCLayerContext *
continue;
// horizontal filtering chroma
- tc_offset = x0 ? left_tc_offset : cur_tc_offset;
x_end2 = x_end;
if (x_end != sps->width)
x_end2 = x_end - 8 * h;
@@ -650,13 +648,14 @@ static void deblocking_filter_CTB(const HEVCContext *s, const HEVCLayerContext *
const int bs0 = l->horizontal_bs[( x + y * l->bs_width) >> 2];
const int bs1 = l->horizontal_bs[((x + 4 * h) + y * l->bs_width) >> 2];
if ((bs0 == 2) || (bs1 == 2)) {
+ const int off = x >= x0 ? cur_tc_offset : left_tc_offset;
const int qp0 = bs0 == 2 ? (get_qPy(sps, l->qp_y_tab, x, y - 1) +
get_qPy(sps, l->qp_y_tab, x, y) + 1) >> 1 : 0;
const int qp1 = bs1 == 2 ? (get_qPy(sps, l->qp_y_tab, x + (4 * h), y - 1) +
get_qPy(sps, l->qp_y_tab, x + (4 * h), y) + 1) >> 1 : 0;
- c_tc[0] = bs0 == 2 ? chroma_tc(pps, sps, qp0, chroma, tc_offset) : 0;
- c_tc[1] = bs1 == 2 ? chroma_tc(pps, sps, qp1, chroma, cur_tc_offset) : 0;
+ c_tc[0] = bs0 == 2 ? chroma_tc(pps, sps, qp0, chroma, off) : 0;
+ c_tc[1] = bs1 == 2 ? chroma_tc(pps, sps, qp1, chroma, off) : 0;
src = &data[chroma][(y >> sps->vshift[1]) * linesize[chroma] + ((x >> sps->hshift[1]) << sps->pixel_shift)];
if (pcmf) {
no_p[0] = get_pcm(sps, l->is_pcm, x, y - 1);