Making writing to partial movies optional, and set the default to be False.

This commit is contained in:
Grant Sanderson 2021-01-23 11:02:22 -08:00
parent 090743aacb
commit 0ac155d150
4 changed files with 67 additions and 56 deletions

View file

@ -33,6 +33,13 @@ style:
# also, you can also specify the position(pixel) of the upper left corner of
# the window on the monitor, e.g. "960,540"
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:
low:
resolution: "854x480"

View file

@ -174,6 +174,7 @@ def get_configuration(args):
write_file = any([args.write_file, args.open, args.finder])
file_writer_config = {
"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_pngs": args.save_pngs,
"save_as_gif": args.save_as_gif,

View file

@ -66,6 +66,7 @@ class Scene(object):
def run(self):
self.virtual_animation_start_time = 0
self.real_animation_start_time = time.time()
self.file_writer.begin()
self.setup()
try:

View file

@ -17,6 +17,7 @@ from manimlib.utils.sounds import get_full_sound_file_path
class SceneFileWriter(object):
CONFIG = {
"write_to_movie": False,
"break_into_partial_movies": False,
# TODO, save_pngs is doing nothing
"save_pngs": False,
"png_mode": "RGBA",
@ -39,6 +40,7 @@ class SceneFileWriter(object):
def __init__(self, scene, **kwargs):
digest_config(self, kwargs)
self.scene = scene
self.writing_process = None
self.init_output_directories()
self.init_audio()
@ -62,9 +64,10 @@ class SceneFileWriter(object):
self.movie_file_extension,
self.gif_file_extension,
)
self.partial_movie_directory = guarantee_existence(os.path.join(
movie_dir, "partial_movie_files", scene_name,
))
if self.break_into_partial_movies:
self.partial_movie_directory = guarantee_existence(os.path.join(
movie_dir, "partial_movie_files", scene_name,
))
def get_default_module_directory(self):
path, _ = os.path.splitext(self.input_file_path)
@ -143,12 +146,16 @@ class SceneFileWriter(object):
self.add_audio_segment(new_segment, time, **kwargs)
# 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):
if self.write_to_movie:
self.open_movie_pipe()
if self.break_into_partial_movies and self.write_to_movie:
self.open_movie_pipe(self.get_next_partial_movie_path())
def end_animation(self):
if self.write_to_movie:
if self.break_into_partial_movies and self.write_to_movie:
self.close_movie_pipe()
def write_frame(self, camera):
@ -163,21 +170,24 @@ class SceneFileWriter(object):
def finish(self):
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.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:
self.scene.update_frame(ignore_skipping=True)
self.save_final_image(self.scene.get_image())
if self.should_open_file():
self.open_file()
def open_movie_pipe(self):
file_path = self.get_next_partial_movie_path()
temp_file_path = os.path.splitext(file_path)[0] + '_temp' + self.movie_file_extension
self.partial_movie_file_path = file_path
self.temp_partial_movie_file_path = temp_file_path
def open_movie_pipe(self, file_path):
stem, ext = os.path.splitext(file_path)
temp_file_path = stem + "_temp" + ext
fps = self.scene.camera.frame_rate
width, height = self.scene.camera.get_pixel_shape()
@ -209,23 +219,17 @@ class SceneFileWriter(object):
]
command += [temp_file_path]
self.writing_process = sp.Popen(command, stdin=sp.PIPE)
self.temp_file_path = temp_file_path
def close_movie_pipe(self):
self.writing_process.stdin.close()
self.writing_process.wait()
shutil.move(
self.temp_partial_movie_file_path,
self.partial_movie_file_path,
self.temp_file_path,
self.temp_file_path.replace("_temp", ""),
)
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 = {
"remove_non_integer_files": True,
"extension": self.movie_file_extension,
@ -273,41 +277,39 @@ class SceneFileWriter(object):
combine_process = sp.Popen(commands)
combine_process.wait()
if self.includes_sound:
sound_file_path = movie_file_path.replace(
self.movie_file_extension, ".wav"
)
# Makes sure sound file length will match video file
self.add_audio_segment(AudioSegment.silent(0))
self.audio_segment.export(
sound_file_path,
bitrate='312k',
)
temp_file_path = movie_file_path.replace(".", "_temp.")
commands = [
"ffmpeg",
"-i", movie_file_path,
"-i", sound_file_path,
'-y', # overwrite output file if it exists
"-c:v", "copy",
"-c:a", "aac",
"-b:a", "320k",
# select video stream from first file
"-map", "0:v:0",
# select audio stream from second file
"-map", "1:a:0",
'-loglevel', 'error',
# "-shortest",
temp_file_path,
]
sp.call(commands)
shutil.move(temp_file_path, movie_file_path)
os.remove(sound_file_path)
self.print_file_ready_message(movie_file_path)
def add_sound_to_video(self):
movie_file_path = self.get_movie_file_path()
stem, ext = os.path.splitext(movie_file_path)
sound_file_path = stem + ".wav"
# Makes sure sound file length will match video file
self.add_audio_segment(AudioSegment.silent(0))
self.audio_segment.export(
sound_file_path,
bitrate='312k',
)
temp_file_path = stem + "_temp" + ext
commands = [
"ffmpeg",
"-i", movie_file_path,
"-i", sound_file_path,
'-y', # overwrite output file if it exists
"-c:v", "copy",
"-c:a", "aac",
"-b:a", "320k",
# select video stream from first file
"-map", "0:v:0",
# select audio stream from second file
"-map", "1:a:0",
'-loglevel', 'error',
# "-shortest",
temp_file_path,
]
sp.call(commands)
shutil.move(temp_file_path, movie_file_path)
os.remove(sound_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):
return any([