Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make manim crash when ffmpeg crashes #546

Merged
merged 13 commits into from
Oct 16, 2020
1 change: 0 additions & 1 deletion docs/source/examples/3d.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,3 @@
self.add(axes, curve1)
self.set_camera_orientation(phi=80 * DEGREES, theta=-60 * DEGREES)
self.wait()

10 changes: 5 additions & 5 deletions docs/source/installation/linux.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The two necessary dependencies are cairo and ffmpeg. LaTeX is strongly
recommended, as it is necessary to have access to the ``Tex`` and ``MathTex`` classes.

Ubuntu/Mint/Debian
*************
******************

Before installing anything, make sure that your system is up to date.

Expand Down Expand Up @@ -42,15 +42,15 @@ To install LaTeX:
If you don't have python3-pip installed, install it:

.. code-block:: bash

sudo apt install python3-pip

.. note:: These instructions are also valid for other Debian-based
distributions or distributions that use the ``apt`` package manager.


Fedora/CentOS/RHEL
*************
******************

To install cairo:

Expand Down Expand Up @@ -116,7 +116,7 @@ To install LaTeX:
If you don't have python-pip installed, install it:

.. code-block:: bash

sudo pacman -S python-pip


Expand Down
19 changes: 19 additions & 0 deletions docs/source/manim_directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ def run(self):
video_dir = os.path.join(media_dir, "videos")
output_file = f"{clsname}-{classnamedict[clsname]}"

# Important: note that all scenes are being rendered on the same python
# interpreter. That means that each time we change the config, that
# same config will be used for the next scene. For this reason, we
# have to make sure to restore the original config after each scene.
save_config_code = [
"original_config = copy.deepcopy(config)",
"original_fw_config = copy.deepcopy(file_writer_config)",
]
file_writer_config_code = [
f'config["frame_rate"] = {frame_rate}',
f'config["pixel_height"] = {pixel_height}',
Expand All @@ -181,6 +189,14 @@ def run(self):
f'file_writer_config["save_as_gif"] = {save_as_gif}',
f'file_writer_config["output_file"] = "{output_file}"',
]
file_writer_config_code.append(
'file_writer_config["write_to_movie"] = '
+ ("False" if save_last_frame else "True")
)
restore_config_code = [
"config = original_config",
"file_writer_config = original_fw_config",
]

user_code = self.content
if user_code[0].startswith(">>> "): # check whether block comes from doctest
Expand All @@ -190,9 +206,12 @@ def run(self):

code = [
"from manim import *",
f"logger.info('rendering {clsname}')",
*save_config_code,
*file_writer_config_code,
*user_code,
f"{clsname}().render()",
*restore_config_code,
]
exec("\n".join(code), globals())

Expand Down
4 changes: 1 addition & 3 deletions manim/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
Constant definitions.
"""
"""Constant definitions."""

import numpy as np

Expand Down
14 changes: 11 additions & 3 deletions manim/scene/scene_file_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,11 @@ def combine_movie_files(self):
if not self.includes_sound:
commands.insert(-1, "-an")

combine_process = subprocess.Popen(commands)
combine_process.wait()
try:
subprocess.check_call(commands)
except subprocess.CalledProcessError as exc:
logger.error(f"FFMPEG returned with code {exc.returncode}")
raise exc

if self.includes_sound:
sound_file_path = movie_file_path.replace(
Expand Down Expand Up @@ -582,7 +585,12 @@ def combine_movie_files(self):
# "-shortest",
temp_file_path,
]
subprocess.call(commands)
try:
subprocess.check_call(commands)
except subprocess.CalledProcessError as exc:
logger.error(f"FFMPEG returned with code {exc.returncode}")
raise exc

shutil.move(temp_file_path, movie_file_path)
os.remove(sound_file_path)

Expand Down
36 changes: 23 additions & 13 deletions manim/utils/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,21 @@ def wrapper(self, scene, *args, **kwargs):
hash_play = get_hash_from_play_call(
self, self.camera, animations, mobjects_on_scene
)
if self.file_writer.is_already_cached(hash_play):
logger.info(
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
{"hash_play": hash_play},
)
file_writer_config["skip_animations"] = True
if file_writer_config["write_to_movie"]:
if self.file_writer.is_already_cached(hash_play):
logger.info(
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
{"hash_play": hash_play},
)
file_writer_config["skip_animations"] = True
else:
hash_play = "uncached_{:05}".format(self.num_plays)
self.animations_hashes.append(hash_play)
self.file_writer.add_partial_movie_file(hash_play)
if (
not file_writer_config["disable_caching"]
and file_writer_config["write_to_movie"]
):
self.file_writer.add_partial_movie_file(hash_play)
logger.debug(
"List of the first few animation hashes of the scene: %(h)s",
{"h": str(self.animations_hashes[:5])},
Expand Down Expand Up @@ -84,15 +89,20 @@ def wrapper(self, scene, duration=DEFAULT_WAIT_TIME, stop_condition=None):
hash_wait = get_hash_from_wait_call(
self, self.camera, duration, stop_condition, scene.get_mobjects()
)
if self.file_writer.is_already_cached(hash_wait):
logger.info(
f"Wait {self.num_plays} : Using cached data (hash : {hash_wait})"
)
file_writer_config["skip_animations"] = True
if file_writer_config["write_to_movie"]:
if self.file_writer.is_already_cached(hash_wait):
logger.info(
f"Wait {self.num_plays} : Using cached data (hash : {hash_wait})"
)
file_writer_config["skip_animations"] = True
else:
hash_wait = "uncached_{:05}".format(self.num_plays)
self.animations_hashes.append(hash_wait)
self.file_writer.add_partial_movie_file(hash_wait)
if (
not file_writer_config["disable_caching"]
and file_writer_config["write_to_movie"]
):
self.file_writer.add_partial_movie_file(hash_wait)
logger.debug(
"List of the first few animation hashes of the scene: %(h)s",
{"h": str(self.animations_hashes[:5])},
Expand Down