mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +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
|
||||
# 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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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([
|
||||
|
|
Loading…
Add table
Reference in a new issue