[PR] avcodec/vc1_block: don't drop the last macroblock when 1 bit remains (PR #24235)

"J. Brandt Buckley via ffmpeg-devel" <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
PR #24235 opened by J. Brandt Buckley (brandt)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24235
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24235.patch

The P- and B-picture per-MB guards added in 2a8785966c bail out of the loop
to error concealment when get_bits_left() <= 1, but P- and B-picture MBs can
legitimately cost exactly 1 bit in certain cases. This patch relaxes the guards
to accommodate those cases.

## Explanation

With the SKIPMB bitplane in Raw mode a skipped macroblock is a single SKIPMBBIT
(SMPTE ST 421:2013, 7.1.3.7, p.109) and then "may only contain the HYBRIDPRED
syntax element" (8.3.4.4, p.140), which is only read when predictors A and C
are in-bounds and the predicted vector differs from A or C by more than 32
quarter-pels (8.3.5.3.5, Figure 55, pp.149-150). That condition isn't met in a
static region so such a MB costs just 1 bit. A direct+skipped B-picture MB
likewise reads nothing beyond its raw flag (Table 30, p.86).

In Simple/Main profile a picture is byte-aligned with no stop bit, so if a
picture ends with a 1-bit skipped macroblock and its last coded bit lands on a
byte boundary, the final MB is reached with exactly one bit left. The guard
then conceals a valid macroblock, sets FF_DECODE_ERROR_CONCEALMENT_ACTIVE
on an undamaged frame, and the damage propagates to every frame up to the next
keyframe. (Advanced Profile BDUs end in a trailing '1' bit per Annex E.2/E.3
p.437, so they always have at least two bit left and are therefore unaffected.)

## Reproducing

Reproducible with an existing sample (prior to this patch):

    $ ffmpeg -i https://samples.ffmpeg.org/V-codecs/WMV9/wmv-samples/[email protected] -map 0:v -f null - 2>&1 | grep -A1 concealing
    [wmv3 @ 0x122605430] concealing 75 DC, 75 AC, 75 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame
    [wmv3 @ 0x122605430] concealing 50 DC, 50 AC, 50 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame
    [wmv3 @ 0x122605430] concealing 100 DC, 100 AC, 100 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame

## References

- SMPTE ST 421:2013 spec: https://pub.smpte.org/doc/st421/20131008-pub/

Signed-off-by: J. Brandt Buckley <[email protected]>



>From ac8f924be0bfef6428b3f054b3f97d5cd11b5987 Mon Sep 17 00:00:00 2001
From: "J. Brandt Buckley" <[email protected]>
Date: Fri, 21 Aug 2026 09:49:15 -0600
Subject: [PATCH] avcodec/vc1_block: don't drop the last macroblock when 1 bit
 remains

The P- and B-picture per-MB guards added in 2a8785966c bail out of the loop
to error concealment when get_bits_left() <= 1, but P- and B-picture MBs can
legitimately cost exactly 1 bit in certain cases. This patch relaxes the guards
to accommodate those cases.

## Explanation

With the SKIPMB bitplane in Raw mode a skipped macroblock is a single SKIPMBBIT
(SMPTE ST 421:2013, 7.1.3.7, p.109) and then "may only contain the HYBRIDPRED
syntax element" (8.3.4.4, p.140), which is only read when predictors A and C
are in-bounds and the predicted vector differs from A or C by more than 32
quarter-pels (8.3.5.3.5, Figure 55, pp.149-150). That condition isn't met in a
static region so such a MB costs just 1 bit. A direct+skipped B-picture MB
likewise reads nothing beyond its raw flag (Table 30, p.86).

In Simple/Main profile a picture is byte-aligned with no stop bit, so if a
picture ends with a 1-bit skipped macroblock and its last coded bit lands on a
byte boundary, the final MB is reached with exactly one bit left. The guard
then conceals a valid macroblock, sets FF_DECODE_ERROR_CONCEALMENT_ACTIVE
on an undamaged frame, and the damage propagates to every frame up to the next
keyframe. (Advanced Profile BDUs end in a trailing '1' bit per Annex E.2/E.3
p.437, so they always have at least two bit left and are therefore unaffected.)

## Reproducing

Reproducible with an existing sample (prior to this patch):

    $ ffmpeg -i https://samples.ffmpeg.org/V-codecs/WMV9/wmv-samples/[email protected] -map 0:v -f null - 2>&1 | grep -A1 concealing
    [wmv3 @ 0x122605430] concealing 75 DC, 75 AC, 75 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame
    [wmv3 @ 0x122605430] concealing 50 DC, 50 AC, 50 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame
    [wmv3 @ 0x122605430] concealing 100 DC, 100 AC, 100 MV errors in P frame
    [vist#0:0/wmv3 @ 0x122604080] [dec:wmv3 @ 0x122604df0] corrupt decoded frame

## References

- SMPTE ST 421:2013 spec: https://pub.smpte.org/doc/st421/20131008-pub/

Signed-off-by: J. Brandt Buckley <[email protected]>
---
 libavcodec/vc1_block.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vc1_block.c b/libavcodec/vc1_block.c
index 10cb459082..a84c853b6f 100644
--- a/libavcodec/vc1_block.c
+++ b/libavcodec/vc1_block.c
@@ -2788,7 +2788,7 @@ static void vc1_decode_p_blocks(VC1Context *v)
             update_block_index(s);
 
             if (v->fcm == ILACE_FIELD || (v->fcm == PROGRESSIVE && v->mv_type_is_raw) || v->skip_is_raw)
-                if (get_bits_left(&v->gb) <= 1) {
+                if (get_bits_left(&v->gb) < 1) {
                     ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR);
                     return;
                 }
@@ -2873,7 +2873,7 @@ static void vc1_decode_b_blocks(VC1Context *v)
             update_block_index(s);
 
             if (v->fcm == ILACE_FIELD || v->skip_is_raw || v->dmb_is_raw)
-                if (get_bits_left(&v->gb) <= 1) {
+                if (get_bits_left(&v->gb) < 1) {
                     ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR);
                     return;
                 }
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.