diff --git a/encode_comparison.py b/encode_comparison.py new file mode 100755 index 0000000..81b4c4f --- /dev/null +++ b/encode_comparison.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3.11 +import argparse +import logging +import subprocess +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Encode middle 1-min slice of video with multiple CRF values.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("video", type=Path, help="Input video file") + parser.add_argument("--codec", choices=["x264", "x265"], default="x265", help="Codec to use") + parser.add_argument("--crf", nargs="+", type=int, default=[15, 20, 25, 30, 35, 40], help="CRF values") + return parser.parse_args() + + +def read_video_duration(video_path: Path) -> float: + cmd = [ + "ffprobe", + "-v", + "error", + "-show_entries", + "format=duration", + "-of", + "default=noprint_wrappers=1:nokey=1", + str(video_path), + ] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return float(result.stdout.strip()) + + +def encode_clip(clip_path: Path, codec: str, crf: int, out_path: Path) -> Path: + # clip_path is actually the input video, we encode the middle slice on the fly + total_duration = read_video_duration(clip_path) + slice_duration = 60 + start = max(0, int(total_duration // 2 - slice_duration // 2)) + if codec == "x264": + lib = "h264_videotoolbox" + else: + lib = "hevc_videotoolbox" + cmd = [ + "ffmpeg", + "-y", + "-ss", + str(start), + "-t", + str(slice_duration), + "-i", + str(clip_path), + "-c:v", + lib, + "-q:v", + str(crf), + "-preset", + "medium", + "-c:a", + "copy", + str(out_path), + ] + logging.info(f"Encoding {out_path.name} with {lib} q:v={crf} (hwaccel) from middle slice") + subprocess.run(cmd, check=True) + + +def main(): + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") + args = parse_args() + outdir = args.video.parent + outdir.mkdir(parents=True, exist_ok=True) + for crf in args.crf: + out_path = outdir / f"{args.video.stem}.{args.codec}_crf{crf}.mp4" + encode_clip(clip_path=args.video, codec=args.codec, crf=crf, out_path=out_path) + + +if __name__ == "__main__": + main()