@@ -37,6 +37,9 @@ class SceneFileWriter(object):
37
37
The PIL image mode to use when outputting PNGs
38
38
"movie_file_extension" (str=".mp4")
39
39
The file-type extension of the outputted video.
40
+ "partial_movie_files"
41
+ List of all the partial-movie files.
42
+
40
43
"""
41
44
42
45
def __init__ (self , video_quality_config , scene , ** kwargs ):
@@ -47,7 +50,7 @@ def __init__(self, video_quality_config, scene, **kwargs):
47
50
self .init_output_directories ()
48
51
self .init_audio ()
49
52
self .frame_count = 0
50
- self .index_partial_movie_file = 0
53
+ self .partial_movie_files = []
51
54
52
55
# Output directories and files
53
56
def init_output_directories (self ):
@@ -114,6 +117,29 @@ def init_output_directories(self):
114
117
)
115
118
)
116
119
120
+ def add_partial_movie_file (self , hash_animation ):
121
+ """Adds a new partial movie file path to scene.partial_movie_files from an hash. This method will compute the path from the hash.
122
+
123
+ Parameters
124
+ ----------
125
+ hash_animation : str
126
+ Hash of the animation.
127
+ """
128
+
129
+ # None has to be added to partial_movie_files to keep the right index with scene.num_plays.
130
+ # i.e if an animation is skipped, scene.num_plays is still incremented and we add an element to partial_movie_file be even with num_plays.
131
+ if hash_animation is None :
132
+ self .partial_movie_files .append (None )
133
+ return
134
+ new_partial_movie_file = os .path .join (
135
+ self .partial_movie_directory ,
136
+ "{}{}" .format (
137
+ hash_animation ,
138
+ file_writer_config ["movie_file_extension" ],
139
+ ),
140
+ )
141
+ self .partial_movie_files .append (new_partial_movie_file )
142
+
117
143
def get_default_module_directory (self ):
118
144
"""
119
145
This method gets the name of the directory containing
@@ -150,7 +176,7 @@ def get_resolution_directory(self):
150
176
This method gets the name of the directory that immediately contains the
151
177
video file. This name is ``<height_in_pixels_of_video>p<frame_rate>``.
152
178
For example, if you are rendering an 854x480 px animation at 15fps,
153
- the name of the directory that immediately contains the video file
179
+ the name of the directory that immediately contains the video, file
154
180
will be ``480p15``.
155
181
156
182
The file structure should look something like::
@@ -187,29 +213,6 @@ def get_image_file_path(self):
187
213
"""
188
214
return self .image_file_path
189
215
190
- def get_next_partial_movie_path (self ):
191
- """
192
- Manim renders each play-like call in a short partial
193
- video file. All such files are then concatenated with
194
- the help of FFMPEG.
195
-
196
- This method returns the path of the next partial movie.
197
-
198
- Returns
199
- -------
200
- str
201
- The path of the next partial movie.
202
- """
203
- result = os .path .join (
204
- self .partial_movie_directory ,
205
- "{}{}" .format (
206
- self .scene .renderer .play_hashes_list [self .index_partial_movie_file ],
207
- file_writer_config ["movie_file_extension" ],
208
- ),
209
- )
210
- self .index_partial_movie_file += 1
211
- return result
212
-
213
216
def get_movie_file_path (self ):
214
217
"""
215
218
Returns the final path of the written video file.
@@ -397,7 +400,9 @@ def open_movie_pipe(self):
397
400
FFMPEG and begin writing to FFMPEG's input
398
401
buffer.
399
402
"""
400
- file_path = self .get_next_partial_movie_path ()
403
+ file_path = self .partial_movie_files [self .scene .renderer .num_plays ]
404
+
405
+ # TODO #486 Why does ffmpeg need temp files ?
401
406
temp_file_path = (
402
407
os .path .splitext (file_path )[0 ]
403
408
+ "_temp"
@@ -495,21 +500,20 @@ def combine_movie_files(self):
495
500
# cuts at all the places you might want. But for viewing
496
501
# the scene as a whole, one of course wants to see it as a
497
502
# single piece.
498
- partial_movie_files = [
499
- os .path .join (
500
- self .partial_movie_directory ,
501
- "{}{}" .format (hash_play , file_writer_config ["movie_file_extension" ]),
502
- )
503
- for hash_play in self .scene .renderer .play_hashes_list
504
- ]
505
- if len (partial_movie_files ) == 0 :
506
- logger .error ("No animations in this scene" )
507
- return
503
+ partial_movie_files = [el for el in self .partial_movie_files if el is not None ]
504
+ # NOTE : Here we should do a check and raise an exeption if partial movie file is empty.
505
+ # We can't, as a lot of stuff (in particular, in tests) use scene initialization, and this error would be raised as it's just
506
+ # an empty scene initialized.
507
+
508
508
# Write a file partial_file_list.txt containing all
509
509
# partial movie files. This is used by FFMPEG.
510
510
file_list = os .path .join (
511
511
self .partial_movie_directory , "partial_movie_file_list.txt"
512
512
)
513
+ logger .debug (
514
+ f"Partial movie files to combine ({ len (partial_movie_files )} files): %(p)s" ,
515
+ {"p" : partial_movie_files [:5 ]},
516
+ )
513
517
with open (file_list , "w" ) as fp :
514
518
fp .write ("# This file is used internally by FFMPEG.\n " )
515
519
for pf_path in partial_movie_files :
0 commit comments