--- a/libavcodec/mpegvideo_parser.c 2026-07-08 19:57:50.463694123 +0000 +++ b/libavcodec/mpegvideo_parser.c 2026-07-08 19:58:06.834674693 +0000 @@ -34,6 +34,34 @@ int width, height; }; +/* + * yauiclient/NextPVR: safety valve against a live long-running memory-growth + * issue observed on some broadcast MPEG-2 sources with closed-caption + * user_data interleaved in the picture stream. + * + * mpeg1_find_frame_end()'s frame_start_found state machine (0/1/2/3/4) can, + * on certain start-code sequences it wasn't validated against, fail to ever + * reach state 4 (frame end confirmed) or reset back to 0. When that happens + * this function returns END_NOT_FOUND on every subsequent call for the rest + * of the stream's lifetime, and ff_combine_frame() (libavcodec/parser.c) + * keeps av_fast_realloc()-growing pc->buffer forever, since it only resets + * pc->index to 0 once a real frame boundary is reported. pc->buffer is only + * ever freed in ff_parse_close(), which for a live stream is only reached + * at genuine EOF/stream teardown -- i.e. never during ordinary continuous + * playback. + * + * This bounds pc->index (the current accumulation depth) to a generous + * multiple of the largest plausible single MPEG-2 frame size. If that + * bound is ever exceeded without a boundary being found, force a resync: + * reset the state machine and report the whole accumulated buffer as a + * completed (likely visibly corrupt for one frame) frame so + * ff_combine_frame flushes and resets pc->index to 0. This should only + * ever trigger in the pathological stuck-forever case described above; + * see combine_frame_shrink.patch for the much more commonly-hit + * everyday-growth case. + */ +#define YAUI_MPV_PARSER_RESYNC_THRESHOLD (8 * 1024 * 1024) /* 8 MiB */ + /** * Find the end of the current frame in the bitstream. * @return the position of the first byte of the next frame, or -1 @@ -48,6 +76,12 @@ if (buf_size == 0) return 0; + if (pc->index + buf_size > YAUI_MPV_PARSER_RESYNC_THRESHOLD) { + pc->frame_start_found = 0; + pc->state = -1; + return buf_size; + } + /* 0 frame start -> 1/4 1 first_SEQEXT -> 0/2