diff --git a/movmaker.py b/movmaker.py index 08f7246..5798394 100755 --- a/movmaker.py +++ b/movmaker.py @@ -477,6 +477,14 @@ def console(message: str) -> None: print(f"[{timestamp()}] {ascii_safe(message)}") +def estimate_output_size(duration: float, width: int, height: int, fps: int, crf: int) -> int: + pixels = width * height + quality_factor = max(0.45, (28 - crf) / 8) + bitrate_mbps = max(2.0, pixels * fps * quality_factor / 7_000_000) + audio_mbps = 0.192 + return int(duration * (bitrate_mbps + audio_mbps) * 1_000_000 / 8) + + def default_output_path(first_media: Path, data: MovieData, out_dir: Path) -> Path: date = media_date(first_media).strftime("%Y%m%d") title = slugify(data.title or first_media.parent.name or "movie") @@ -968,9 +976,13 @@ def main(argv: list[str] | None = None) -> int: # Use probed normalized duration for accuracy. durations = [ffprobe_duration(out) for out in clips] + estimated_duration = durations[0] if len(durations) == 1 else durations[0] + sum(durations[1:]) - fade * (len(durations) - 1) + estimated_size = estimate_output_size(estimated_duration, width, height, fps, crf) captions = [data.captions.get(item.path.name, data.captions.get(str(item.path), "")) for item in media] audio = concat_or_mix_audio(audio_files, tmp / "music.m4a") + console(f"Audio: {audio if audio else 'none'}") + console(f"Estimated output: video length {estimated_duration:.1f}s, size about {format_bytes(estimated_size)}") build_video(clips, durations, audio, output, data, first_media_date, captions, fade, audio_fade, audio_fade_in, video_fade, width, height, fps, preset, crf, tmp) console(f"Done: {output}")