From 2eb47376899cfa8ed2e7243669b257e8b54ec9bd Mon Sep 17 00:00:00 2001 From: Roland Date: Fri, 23 Oct 2026 11:11:30 -0600 Subject: [PATCH] Ignore the null muxer's duplicate-DTS warning in the tail-decode check ffmpeg exits 0 but prints "non monotonically increasing dts to muxer" from the null output when two frames share a timestamp. Any stderr failed the completeness check, so such videos (common on X) were rejected as incomplete on every retry. Co-Authored-By: Claude Opus 5.5 --- modules/videos/common.py | 7 ++++++- modules/videos/test/test_common.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/videos/common.py b/modules/videos/common.py index 1d8cadc7..b93123c4 100644 --- a/modules/videos/common.py +++ b/modules/videos/common.py @@ -163,8 +163,13 @@ def tail_decode_command(video_path: Path, seconds: int = None) -> Tuple[str | Pa return FFMPEG_BIN, '-v', 'error', '-sseof', f'-{int(seconds)}', '-i', video_path, '-f', 'null', '-' +# The null muxer complains about duplicate timestamps after the frames have decoded; it is not damage. +TAIL_DECODE_IGNORED_ERRORS = re.compile(r'^\[null @ \w+] Application provided invalid, non monotonically increasing dts') + + def _tail_decode_verdict(return_code: int, stderr: bytes | str) -> Tuple[bool, str]: - errors = (stderr.decode(errors='replace') if isinstance(stderr, bytes) else (stderr or '')).strip() + errors = stderr.decode(errors='replace') if isinstance(stderr, bytes) else (stderr or '') + errors = '\n'.join(i for i in errors.strip().splitlines() if not TAIL_DECODE_IGNORED_ERRORS.match(i)) ok = return_code == 0 and not errors return ok, errors diff --git a/modules/videos/test/test_common.py b/modules/videos/test/test_common.py index 4045f731..911e5b1a 100644 --- a/modules/videos/test/test_common.py +++ b/modules/videos/test/test_common.py @@ -667,6 +667,26 @@ def test_ffmpeg_video_complete_real_files(test_directory, video_file): assert cmd[cmd.index('-sseof') + 1] == '-30' and cmd[cmd.index('-f') + 1] == 'null' and cmd[-1] == '-' +NULL_MUXER_DTS = b'[null @ 0x5555faf7fa70] Application provided invalid, non monotonically increasing dts to muxer' \ + b' in stream 0: 460 >= 460\n' + + +@pytest.mark.parametrize('return_code,stderr,expected_ok,expected_errors', [ + (0, b'', True, ''), + # Duplicate timestamps are reported by the null muxer, not a decoder; the stream decoded. + (0, NULL_MUXER_DTS, True, ''), + (0, NULL_MUXER_DTS * 3, True, ''), + (0, NULL_MUXER_DTS + b'[h264 @ 0x1] Invalid NAL unit size\n', False, '[h264 @ 0x1] Invalid NAL unit size'), + (0, b'[h264 @ 0x1] error while decoding MB 3 4\n', False, '[h264 @ 0x1] error while decoding MB 3 4'), + (1, NULL_MUXER_DTS, False, ''), +]) +def test_tail_decode_verdict(return_code, stderr, expected_ok, expected_errors): + """Only decode errors, or a failed exit, mark a video incomplete.""" + from modules.videos.common import _tail_decode_verdict + assert _tail_decode_verdict(return_code, stderr) == (expected_ok, expected_errors) + assert _tail_decode_verdict(return_code, stderr.decode()) == (expected_ok, expected_errors) + + @pytest.mark.asyncio async def test_ffmpeg_video_complete_async_real_files(test_directory, video_file): """The async variant reaches the same verdicts and returns ffmpeg's error text."""