mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Making writing to partial movies optional, and set the default to be False.
This commit is contained in:
parent
090743aacb
commit
0ac155d150
4 changed files with 67 additions and 56 deletions
|
@ -33,6 +33,13 @@ style:
|
||||||
# also, you can also specify the position(pixel) of the upper left corner of
|
# also, you can also specify the position(pixel) of the upper left corner of
|
||||||
# the window on the monitor, e.g. "960,540"
|
# the window on the monitor, e.g. "960,540"
|
||||||
window_position: UR
|
window_position: UR
|
||||||
|
# If break_into_partial_movies is set to True, then many small
|
||||||
|
# files will be written corresponding to each Scene.play and
|
||||||
|
# Scene.wait call, and these files will then be combined
|
||||||
|
# to form the full scene. Sometimes video-editing is made
|
||||||
|
# easier when working with the broken up scene, which
|
||||||
|
# effectively has cuts at all the places you might want.
|
||||||
|
break_into_partial_movies: False
|
||||||
camera_qualities:
|
camera_qualities:
|
||||||
low:
|
low:
|
||||||
resolution: "854x480"
|
resolution: "854x480"
|
||||||
|
|
|
@ -174,6 +174,7 @@ def get_configuration(args):
|
||||||
write_file = any([args.write_file, args.open, args.finder])
|
write_file = any([args.write_file, args.open, args.finder])
|
||||||
file_writer_config = {
|
file_writer_config = {
|
||||||
"write_to_movie": not args.skip_animations and write_file,
|
"write_to_movie": not args.skip_animations and write_file,
|
||||||
|
"break_into_partial_movies": custom_defaults["break_into_partial_movies"],
|
||||||
"save_last_frame": args.skip_animations and write_file,
|
"save_last_frame": args.skip_animations and write_file,
|
||||||
"save_pngs": args.save_pngs,
|
"save_pngs": args.save_pngs,
|
||||||
"save_as_gif": args.save_as_gif,
|
"save_as_gif": args.save_as_gif,
|
||||||
|
|
|
@ -66,6 +66,7 @@ class Scene(object):
|
||||||
def run(self):
|
def run(self):
|
||||||
self.virtual_animation_start_time = 0
|
self.virtual_animation_start_time = 0
|
||||||
self.real_animation_start_time = time.time()
|
self.real_animation_start_time = time.time()
|
||||||
|
self.file_writer.begin()
|
||||||
|
|
||||||
self.setup()
|
self.setup()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -17,6 +17,7 @@ from manimlib.utils.sounds import get_full_sound_file_path
|
||||||
class SceneFileWriter(object):
|
class SceneFileWriter(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"write_to_movie": False,
|
"write_to_movie": False,
|
||||||
|
"break_into_partial_movies": False,
|
||||||
# TODO, save_pngs is doing nothing
|
# TODO, save_pngs is doing nothing
|
||||||
"save_pngs": False,
|
"save_pngs": False,
|
||||||
"png_mode": "RGBA",
|
"png_mode": "RGBA",
|
||||||
|
@ -39,6 +40,7 @@ class SceneFileWriter(object):
|
||||||
def __init__(self, scene, **kwargs):
|
def __init__(self, scene, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
self.scene = scene
|
self.scene = scene
|
||||||
|
self.writing_process = None
|
||||||
self.init_output_directories()
|
self.init_output_directories()
|
||||||
self.init_audio()
|
self.init_audio()
|
||||||
|
|
||||||
|
@ -62,9 +64,10 @@ class SceneFileWriter(object):
|
||||||
self.movie_file_extension,
|
self.movie_file_extension,
|
||||||
self.gif_file_extension,
|
self.gif_file_extension,
|
||||||
)
|
)
|
||||||
self.partial_movie_directory = guarantee_existence(os.path.join(
|
if self.break_into_partial_movies:
|
||||||
movie_dir, "partial_movie_files", scene_name,
|
self.partial_movie_directory = guarantee_existence(os.path.join(
|
||||||
))
|
movie_dir, "partial_movie_files", scene_name,
|
||||||
|
))
|
||||||
|
|
||||||
def get_default_module_directory(self):
|
def get_default_module_directory(self):
|
||||||
path, _ = os.path.splitext(self.input_file_path)
|
path, _ = os.path.splitext(self.input_file_path)
|
||||||
|
@ -143,12 +146,16 @@ class SceneFileWriter(object):
|
||||||
self.add_audio_segment(new_segment, time, **kwargs)
|
self.add_audio_segment(new_segment, time, **kwargs)
|
||||||
|
|
||||||
# Writers
|
# Writers
|
||||||
|
def begin(self):
|
||||||
|
if not self.break_into_partial_movies and self.write_to_movie:
|
||||||
|
self.open_movie_pipe(self.get_movie_file_path())
|
||||||
|
|
||||||
def begin_animation(self):
|
def begin_animation(self):
|
||||||
if self.write_to_movie:
|
if self.break_into_partial_movies and self.write_to_movie:
|
||||||
self.open_movie_pipe()
|
self.open_movie_pipe(self.get_next_partial_movie_path())
|
||||||
|
|
||||||
def end_animation(self):
|
def end_animation(self):
|
||||||
if self.write_to_movie:
|
if self.break_into_partial_movies and self.write_to_movie:
|
||||||
self.close_movie_pipe()
|
self.close_movie_pipe()
|
||||||
|
|
||||||
def write_frame(self, camera):
|
def write_frame(self, camera):
|
||||||
|
@ -163,21 +170,24 @@ class SceneFileWriter(object):
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
if self.write_to_movie:
|
if self.write_to_movie:
|
||||||
if hasattr(self, "writing_process"):
|
if not self.break_into_partial_movies:
|
||||||
|
self.close_movie_pipe()
|
||||||
|
if self.writing_process is not None:
|
||||||
self.writing_process.terminate()
|
self.writing_process.terminate()
|
||||||
self.combine_movie_files()
|
if self.break_into_partial_movies:
|
||||||
|
self.combine_movie_files()
|
||||||
|
if self.includes_sound:
|
||||||
|
self.add_sound_to_video()
|
||||||
|
self.print_file_ready_message(self.get_movie_file_path())
|
||||||
if self.save_last_frame:
|
if self.save_last_frame:
|
||||||
self.scene.update_frame(ignore_skipping=True)
|
self.scene.update_frame(ignore_skipping=True)
|
||||||
self.save_final_image(self.scene.get_image())
|
self.save_final_image(self.scene.get_image())
|
||||||
if self.should_open_file():
|
if self.should_open_file():
|
||||||
self.open_file()
|
self.open_file()
|
||||||
|
|
||||||
def open_movie_pipe(self):
|
def open_movie_pipe(self, file_path):
|
||||||
file_path = self.get_next_partial_movie_path()
|
stem, ext = os.path.splitext(file_path)
|
||||||
temp_file_path = os.path.splitext(file_path)[0] + '_temp' + self.movie_file_extension
|
temp_file_path = stem + "_temp" + ext
|
||||||
|
|
||||||
self.partial_movie_file_path = file_path
|
|
||||||
self.temp_partial_movie_file_path = temp_file_path
|
|
||||||
|
|
||||||
fps = self.scene.camera.frame_rate
|
fps = self.scene.camera.frame_rate
|
||||||
width, height = self.scene.camera.get_pixel_shape()
|
width, height = self.scene.camera.get_pixel_shape()
|
||||||
|
@ -209,23 +219,17 @@ class SceneFileWriter(object):
|
||||||
]
|
]
|
||||||
command += [temp_file_path]
|
command += [temp_file_path]
|
||||||
self.writing_process = sp.Popen(command, stdin=sp.PIPE)
|
self.writing_process = sp.Popen(command, stdin=sp.PIPE)
|
||||||
|
self.temp_file_path = temp_file_path
|
||||||
|
|
||||||
def close_movie_pipe(self):
|
def close_movie_pipe(self):
|
||||||
self.writing_process.stdin.close()
|
self.writing_process.stdin.close()
|
||||||
self.writing_process.wait()
|
self.writing_process.wait()
|
||||||
shutil.move(
|
shutil.move(
|
||||||
self.temp_partial_movie_file_path,
|
self.temp_file_path,
|
||||||
self.partial_movie_file_path,
|
self.temp_file_path.replace("_temp", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
def combine_movie_files(self):
|
def combine_movie_files(self):
|
||||||
# Manim renders the scene as many smaller movie files
|
|
||||||
# which are then concatenated to a larger one. The reason
|
|
||||||
# for this is that sometimes video-editing is made easier when
|
|
||||||
# one works with the broken up scene, which effectively has
|
|
||||||
# cuts at all the places you might want. But for viewing
|
|
||||||
# the scene as a whole, one of course wants to see it as a
|
|
||||||
# single piece.
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"remove_non_integer_files": True,
|
"remove_non_integer_files": True,
|
||||||
"extension": self.movie_file_extension,
|
"extension": self.movie_file_extension,
|
||||||
|
@ -273,41 +277,39 @@ class SceneFileWriter(object):
|
||||||
combine_process = sp.Popen(commands)
|
combine_process = sp.Popen(commands)
|
||||||
combine_process.wait()
|
combine_process.wait()
|
||||||
|
|
||||||
if self.includes_sound:
|
def add_sound_to_video(self):
|
||||||
sound_file_path = movie_file_path.replace(
|
movie_file_path = self.get_movie_file_path()
|
||||||
self.movie_file_extension, ".wav"
|
stem, ext = os.path.splitext(movie_file_path)
|
||||||
)
|
sound_file_path = stem + ".wav"
|
||||||
# Makes sure sound file length will match video file
|
# Makes sure sound file length will match video file
|
||||||
self.add_audio_segment(AudioSegment.silent(0))
|
self.add_audio_segment(AudioSegment.silent(0))
|
||||||
self.audio_segment.export(
|
self.audio_segment.export(
|
||||||
sound_file_path,
|
sound_file_path,
|
||||||
bitrate='312k',
|
bitrate='312k',
|
||||||
)
|
)
|
||||||
temp_file_path = movie_file_path.replace(".", "_temp.")
|
temp_file_path = stem + "_temp" + ext
|
||||||
commands = [
|
commands = [
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
"-i", movie_file_path,
|
"-i", movie_file_path,
|
||||||
"-i", sound_file_path,
|
"-i", sound_file_path,
|
||||||
'-y', # overwrite output file if it exists
|
'-y', # overwrite output file if it exists
|
||||||
"-c:v", "copy",
|
"-c:v", "copy",
|
||||||
"-c:a", "aac",
|
"-c:a", "aac",
|
||||||
"-b:a", "320k",
|
"-b:a", "320k",
|
||||||
# select video stream from first file
|
# select video stream from first file
|
||||||
"-map", "0:v:0",
|
"-map", "0:v:0",
|
||||||
# select audio stream from second file
|
# select audio stream from second file
|
||||||
"-map", "1:a:0",
|
"-map", "1:a:0",
|
||||||
'-loglevel', 'error',
|
'-loglevel', 'error',
|
||||||
# "-shortest",
|
# "-shortest",
|
||||||
temp_file_path,
|
temp_file_path,
|
||||||
]
|
]
|
||||||
sp.call(commands)
|
sp.call(commands)
|
||||||
shutil.move(temp_file_path, movie_file_path)
|
shutil.move(temp_file_path, movie_file_path)
|
||||||
os.remove(sound_file_path)
|
os.remove(sound_file_path)
|
||||||
|
|
||||||
self.print_file_ready_message(movie_file_path)
|
|
||||||
|
|
||||||
def print_file_ready_message(self, file_path):
|
def print_file_ready_message(self, file_path):
|
||||||
print("\nFile ready at {}\n".format(file_path))
|
print(f"\nFile ready at {file_path}\n")
|
||||||
|
|
||||||
def should_open_file(self):
|
def should_open_file(self):
|
||||||
return any([
|
return any([
|
||||||
|
|
Loading…
Add table
Reference in a new issue