diff --git a/src/lib_ccx/es_functions.c b/src/lib_ccx/es_functions.c index 805fa433c..5c4f91f51 100644 --- a/src/lib_ccx/es_functions.c +++ b/src/lib_ccx/es_functions.c @@ -323,7 +323,10 @@ static int es_video_sequence(struct encoder_ctx *enc_ctx, struct lib_cc_decode * } else { - mprint("\nUnexpected startcode: %02X\n", startcode); + // 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; diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index 807d1a5c0..f73f6339c 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -1028,7 +1028,42 @@ 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) + // 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));