Files
playground/encode_comparison.py
T

78 lines
2.3 KiB
Python
Executable File

#!/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()