From 44ac486dfd2215fa01c07398195ae0e6c46460f0 Mon Sep 17 00:00:00 2001 From: tusharbhatt7 Date: Thu, 25 Dec 2025 19:29:27 +0530 Subject: [PATCH 1/3] Fix infinite loop by skipping junk, preserving User Data (Issue #1754) --- src/lib_ccx/es_functions.c | 14 +++++++++++++- src/lib_ccx/general_loop.c | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/lib_ccx/es_functions.c b/src/lib_ccx/es_functions.c index 805fa433c..06ab6169b 100644 --- a/src/lib_ccx/es_functions.c +++ b/src/lib_ccx/es_functions.c @@ -323,7 +323,19 @@ static int es_video_sequence(struct encoder_ctx *enc_ctx, struct lib_cc_decode * } else { - mprint("\nUnexpected startcode: %02X\n", startcode); + // If we see User Data (B2) or Extension (B5) but we are not in a state to process them + // (e.g. haven't seen Sequence Header yet), we should NOT skip them, but return 0 + // and wait for more data/context. + // However, if we see junk or other unhandled codes, we MUST skip them to prevent infinite loops. + if (startcode != 0xB2 && startcode != 0xB5) + { + mprint("\nUnexpected startcode: %02X\n", startcode); + skip_u32(esstream); + } + else + { + // Keep silent for B2/B5 to avoid log spam, just return 0 to buffer more + } } dec_ctx->no_bitstream_error = 0; return 0; diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index 807d1a5c0..a41a00e60 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -1029,6 +1029,13 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx, } } size_t got = process_m2v(*enc_ctx, dec_ctx_video, data_node_video->buffer, data_node_video->len, dec_sub_video); + if (got == 0 && data_node_video->len >= 1048576) + { + // Prevent infinite loop if decoder consumes nothing from a very large buffer (1MB) + // This handles cases where process_m2v returns 0 (error or no progress) but buffer is full + // We use a large threshold to ensure we don't discard valid video data that is just waiting for more bytes. + got = data_node_video->len; + } if (got > 0) { memmove(data_node_video->buffer, data_node_video->buffer + got, (size_t)(data_node_video->len - got)); From cfdb397b1051b37e66d39e85e53dc5e74a57ffdf Mon Sep 17 00:00:00 2001 From: tusharbhatt7 Date: Fri, 26 Dec 2025 13:08:34 +0530 Subject: [PATCH 2/3] Fix: Infinite loop on junk data (whitelisting) and H.264 regression (guarding process_m2v) --- src/lib_ccx/es_functions.c | 20 ++++++++++++-------- src/lib_ccx/general_loop.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/lib_ccx/es_functions.c b/src/lib_ccx/es_functions.c index 06ab6169b..36e7c0aff 100644 --- a/src/lib_ccx/es_functions.c +++ b/src/lib_ccx/es_functions.c @@ -323,18 +323,22 @@ static int es_video_sequence(struct encoder_ctx *enc_ctx, struct lib_cc_decode * } else { - // If we see User Data (B2) or Extension (B5) but we are not in a state to process them - // (e.g. haven't seen Sequence Header yet), we should NOT skip them, but return 0 - // and wait for more data/context. - // However, if we see junk or other unhandled codes, we MUST skip them to prevent infinite loops. - if (startcode != 0xB2 && startcode != 0xB5) + // Whitelist valid MPEG-2 System start codes (B0-BF), including: + // B2 (User Data), B5 (Extension), B8 (GOP), BD (Private), BE (Padding). + // If we see these out of context, we return 0 to wait (buffer preserved). + // + // We SKIP everything else, including: + // - Slices (01-AF) appearing without Picture Header (Garbage/OutOfOrder) + // - H.264 Start Codes (67, 68...) if misinterpreted as MPEG-2 + // - Random Junk (e.g. 55) + if (startcode >= 0xB0 && startcode <= 0xBF) { - mprint("\nUnexpected startcode: %02X\n", startcode); - skip_u32(esstream); + // Keep silent, return 0 to buffer more } else { - // Keep silent for B2/B5 to avoid log spam, just return 0 to buffer more + mprint("\nUnexpected startcode: %02X. Skipping.\n", startcode); + skip_u32(esstream); } } dec_ctx->no_bitstream_error = 0; diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index a41a00e60..f73f6339c 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -1028,7 +1028,35 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx, set_fts(dec_ctx_video->timing); } } - size_t got = process_m2v(*enc_ctx, dec_ctx_video, data_node_video->buffer, data_node_video->len, dec_sub_video); + + size_t got = 0; + // Only call process_m2v for MPEG-1 and MPEG-2 video streams. + // H.264 (AVC) and HEVC have different start codes and structures that process_m2v + // will misinterpret (potentially skipping valid data as "unexpected startcodes"), + // leading to stream corruption and timing issues. + if (cinfo_video->stream == CCX_STREAM_TYPE_VIDEO_MPEG1 || + cinfo_video->stream == CCX_STREAM_TYPE_VIDEO_MPEG2) + { + got = process_m2v(*enc_ctx, dec_ctx_video, data_node_video->buffer, data_node_video->len, dec_sub_video); + } + else if (cinfo_video->stream == CCX_STREAM_TYPE_VIDEO_H264 || cinfo_video->stream == CCX_STREAM_TYPE_VIDEO_HEVC) + { + // For H.264/HEVC, we cannot use process_m2v. + // We currently skip analysis for these formats in this loop to prevent infinite loops + // (if we returned 0, the loop would retry the same buffer forever). + // TODO: Implement proper H.264/HEVC analysis (e.g. process_avc) if needed. + got = data_node_video->len; + } + else + { + // Unknown or unhandled, process_m2v might be safe or default? + // But original behavior was unconditional process_m2v. + // Let's stick to process_m2v for "other" to minimize change, + // unless we want to be strict. Strict is better to avoid new bugs. + // But let's mirror the "MPEG-2" assumption of legacy code for non-H264. + got = process_m2v(*enc_ctx, dec_ctx_video, data_node_video->buffer, data_node_video->len, dec_sub_video); + } + if (got == 0 && data_node_video->len >= 1048576) { // Prevent infinite loop if decoder consumes nothing from a very large buffer (1MB) From 728e6cd443d2878ce389fb4fa44596d36908eda9 Mon Sep 17 00:00:00 2001 From: tusharbhatt7 Date: Fri, 26 Dec 2025 14:06:01 +0530 Subject: [PATCH 3/3] Fix: Remove startcode whitelist to prevent stalls, rely on skip_u32 --- src/lib_ccx/es_functions.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/lib_ccx/es_functions.c b/src/lib_ccx/es_functions.c index 36e7c0aff..5c4f91f51 100644 --- a/src/lib_ccx/es_functions.c +++ b/src/lib_ccx/es_functions.c @@ -323,23 +323,10 @@ static int es_video_sequence(struct encoder_ctx *enc_ctx, struct lib_cc_decode * } else { - // Whitelist valid MPEG-2 System start codes (B0-BF), including: - // B2 (User Data), B5 (Extension), B8 (GOP), BD (Private), BE (Padding). - // If we see these out of context, we return 0 to wait (buffer preserved). - // - // We SKIP everything else, including: - // - Slices (01-AF) appearing without Picture Header (Garbage/OutOfOrder) - // - H.264 Start Codes (67, 68...) if misinterpreted as MPEG-2 - // - Random Junk (e.g. 55) - if (startcode >= 0xB0 && startcode <= 0xBF) - { - // Keep silent, return 0 to buffer more - } - else - { - mprint("\nUnexpected startcode: %02X. Skipping.\n", startcode); - skip_u32(esstream); - } + // Unhandled start codes (including valid MPEG-2 codes that appear out of context, + // like User Data when not expected) should be skipped to avoid infinite loops. + mprint("\nUnexpected startcode: %02X. Skipping.\n", startcode); + skip_u32(esstream); } dec_ctx->no_bitstream_error = 0; return 0;