diff --git a/docs/module-internals.md b/docs/module-internals.md index b640e00..9c401e7 100644 --- a/docs/module-internals.md +++ b/docs/module-internals.md @@ -171,6 +171,10 @@ failure regressions cover bounded mux diagnostics and atomic cleanup. and preserves the supported properties that survive the 8-bit BGR boundary: `yuv420p`/`yuv422p`/`yuv444p` chroma sampling, recognized color tags, encoder time base, MP4/MOV track timescale, source pixel format, and component depth. +Both raw-CFR and timestamped-NUT inputs use ffmpeg's passthrough FPS mode. This +keeps one encoded frame per supplied frame when an older ffmpeg receives a +fine-grained source encoder time base such as `1/90000`; implicit synchronization +can otherwise synthesize thousands of duplicate frames between CFR timestamps. HDR transfer functions and component depths above 8 bits are rejected before encoding so the OpenCV boundary cannot silently reduce them to SDR 8-bit. `probe_video_timestamps` reads authoritative per-frame display PTS through diff --git a/src/remove_ai_watermarks/video_encoding.py b/src/remove_ai_watermarks/video_encoding.py index 36ce507..e89da14 100644 --- a/src/remove_ai_watermarks/video_encoding.py +++ b/src/remove_ai_watermarks/video_encoding.py @@ -378,8 +378,7 @@ def raw_video_command( "-map_chapters", "-1", ] - if timestamped_input: - command.extend(["-fps_mode", "passthrough"]) + command.extend(["-fps_mode", "passthrough"]) if copy_input_timestamps: command.extend(["-avoid_negative_ts", "disabled"]) if output.suffix.lower() in {".mp4", ".mov", ".m4v"} and profile.time_base is not None: diff --git a/tests/test_video_invisible.py b/tests/test_video_invisible.py index abc030d..60e520a 100644 --- a/tests/test_video_invisible.py +++ b/tests/test_video_invisible.py @@ -127,6 +127,7 @@ def test_encoder_and_mux_commands_separate_streaming_frames_from_source_audio( assert "pipe:0" in command assert str(source) not in command assert command[command.index("-map") + 1] == "0:v:0" + assert command[command.index("-fps_mode") + 1] == "passthrough" assert "-shortest" not in command calls: list[list[str]] = []