diff --git a/manim/_config/utils.py b/manim/_config/utils.py index b453b290e2..0382272cdd 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -320,6 +320,7 @@ class MyScene(Scene): ... "force_window", "no_latex_cleanup", "preview_command", + "lossless", } def __init__(self) -> None: @@ -591,6 +592,7 @@ def digest_parser(self, parser: configparser.ConfigParser) -> Self: "enable_wireframe", "force_window", "no_latex_cleanup", + "lossless", ]: setattr(self, key, parser["CLI"].getboolean(key, fallback=False)) @@ -767,6 +769,7 @@ def digest_args(self, args: argparse.Namespace) -> Self: "dry_run", "no_latex_cleanup", "preview_command", + "lossless", ]: if hasattr(args, key): attr = getattr(args, key) @@ -1491,6 +1494,15 @@ def zero_pad(self) -> int: def zero_pad(self, value: int) -> None: self._set_int_between("zero_pad", value, 0, 9) + @property + def lossless(self) -> bool: + """Whether to use lossless x265 encoding (mp4 format only).""" + return self._d["lossless"] + + @lossless.setter + def lossless(self, value: bool) -> None: + self._set_boolean("lossless", value) + def get_dir(self, key: str, **kwargs: Any) -> Path: """Resolve a config option that stores a directory. diff --git a/manim/cli/render/render_options.py b/manim/cli/render/render_options.py index 9a31d36ed4..8372e37205 100644 --- a/manim/cli/render/render_options.py +++ b/manim/cli/render/render_options.py @@ -212,4 +212,10 @@ def validate_resolution( help="Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices.", default=None, ), + option( + "--lossless", + is_flag=True, + help="Render with lossless x265 encoding (mp4 format only).", + default=False, + ), ) diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py index d256afb736..66b5abef61 100644 --- a/manim/scene/scene_file_writer.py +++ b/manim/scene/scene_file_writer.py @@ -536,12 +536,20 @@ def open_partial_movie_stream(self, file_path=None) -> None: fps = to_av_frame_rate(config.frame_rate) - partial_movie_file_codec = "libx264" partial_movie_file_pix_fmt = "yuv420p" av_options = { "an": "1", # ffmpeg: -an, no audio - "crf": "23", # ffmpeg: -crf, constant rate factor (improved bitrate) } + if config.lossless: + partial_movie_file_codec = "libx265" + av_options["x265-params"] = ( + "lossless=1" # ffmpeg: set lossless mode for x265 + ) + else: + partial_movie_file_codec = "libx264" + av_options["crf"] = ( + "23" # ffmpeg: -crf, constant rate factor (improved bitrate) + ) if config.movie_file_extension == ".webm": partial_movie_file_codec = "libvpx-vp9"