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 <noreply@anthropic.com>
This commit is contained in:
Roland 2026-10-23 11:11:30 -06:00
parent 9ccf19b82b
commit 2eb4737689
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
2 changed files with 26 additions and 1 deletions

View file

@ -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

View file

@ -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."""