@@ -27,19 +27,29 @@ def __init__(self, carrier_path: Path, output_path: Path, temp_dir: Path, matche
27
27
self .carrier_path = carrier_path
28
28
self .output_path = output_path
29
29
self .temp_dir = temp_dir
30
- self .frames_dir = self .temp_dir / 'frames'
31
- self .outframes_dir = self .temp_dir / 'outframes'
30
+ self .frames_dir = self .temp_dir / 'frames'
32
31
33
32
self .framecount = int (framecount )
34
33
self .frame_length = frame_length
35
34
36
35
self .color_mode = color_mode
37
36
38
37
self .frames_written = 0
38
+ self .out_proc = self .create_output_proc ()
39
39
40
40
def get_frame (self ,idx ):
41
- assert (idx < self .framecount )
42
-
41
+ try :
42
+ assert (idx < self .framecount )
43
+ except AssertionError :
44
+ print ("ERROR:" )
45
+ print (f"STAMMER just tried to use carrier frame { idx } " )
46
+ print (f"but carrier only has { self .framecount } frames." )
47
+ print ()
48
+ print ("This is a critical known issue with how carrier frames are handled." )
49
+ print ("Please report STAMMER's output at this link:\n https://github.com/ArdenButterfield/stammer/issues/62" )
50
+ print ("\n Quitting." )
51
+ quit ()
52
+
43
53
def write_frame (self ):
44
54
self .frames_written += 1
45
55
self .print_progress ()
@@ -61,54 +71,65 @@ def progress_strings_separated(self):
61
71
def print_progress (self ):
62
72
print (self .progress_strings_separated (),end = ' \r ' )
63
73
64
- def get_output_cmd (handler : VideoHandler ,input ):
65
- cmd = [
66
- 'ffmpeg' ,
67
- '-v' , 'quiet' ,
68
- '-y' ,
69
- '-framerate' , str (1.0 / handler .frame_length ),
70
- '!inputs!' ,
71
- '-c:a' , 'aac' ,
72
- '-c:v' , 'libx264' ,
73
- '-crf' , '20' ,
74
- '-pix_fmt' , 'yuv420p' ,
75
- '-shortest' ,
76
- str (handler .output_path )
77
- ]
78
-
79
- def replace (value , list ):
80
- idx = cmd .index (value )
81
- cmd .pop (idx )
82
- for i , x in enumerate (list ): cmd .insert (idx + i ,x )
83
-
84
- replace ('!inputs!' ,input )
74
+ def get_output_cmd (self ,input = None ):
75
+ if input == None :
76
+ input = [
77
+ '-f' , 'image2pipe' , '-i' , 'pipe:' ,
78
+ '-i' , str (self .temp_dir / 'out.wav' )
79
+ ]
80
+ cmd = [
81
+ 'ffmpeg' ,
82
+ '-v' , 'quiet' ,
83
+ '-y' ,
84
+ '-framerate' , str (1.0 / self .frame_length ),
85
+ '!inputs!' ,
86
+ '-c:a' , 'aac' ,
87
+ '-c:v' , 'libx264' ,
88
+ '-crf' , '24' ,
89
+ '-pix_fmt' , 'yuv420p' ,
90
+ '-shortest' ,
91
+ str (self .output_path )
92
+ ]
93
+
94
+ def replace (value , list ):
95
+ idx = cmd .index (value )
96
+ cmd .pop (idx )
97
+ for i , x in enumerate (list ): cmd .insert (idx + i ,x )
98
+
99
+ replace ('!inputs!' ,input )
100
+
101
+ return cmd
85
102
86
- return cmd
103
+ def create_output_proc (self ):
104
+ call = self .get_output_cmd ()
105
+
106
+ return subprocess .Popen (
107
+ call ,
108
+ stdin = subprocess .PIPE ,
109
+ stdout = subprocess .DEVNULL
110
+ )
111
+
87
112
88
113
class VideoHandlerDisk (VideoHandler ):
114
+ def __init__ (self , * args ):
115
+ super ().__init__ (* args )
116
+
89
117
def get_frame (self ,idx ):
90
118
super ().get_frame (idx )
91
- f = open (self .frames_dir / f"frame{ idx :06d} .png" , 'rb' )
92
- return f
119
+
120
+ # Video frame filenames start at 1, not 0
121
+ idx += 1
122
+ return open (self .frames_dir / f"frame{ idx :06d} .png" , 'rb' )
93
123
94
124
def write_frame (self ,idx ,frame : io .BytesIO ):
95
125
super ().write_frame ()
96
126
frame .seek (0 )
97
- f = open (self .outframes_dir / f"frame{ idx :06d} .png" , 'wb' )
98
- f .write (frame .read ())
99
- f .close ()
127
+ self .out_proc .stdin .write (frame .read ())
100
128
101
129
def complete (self ):
102
130
super ().complete ()
103
- call = get_output_cmd (
104
- self ,
105
- input = [
106
- '-stats' ,
107
- '-i' , str (self .outframes_dir / 'frame%06d.png' ),
108
- '-i' , str (self .temp_dir / 'out.wav' )
109
- ]
110
- )
111
- subprocess .run (call ,check = True )
131
+
132
+ self .out_proc .communicate ()
112
133
113
134
114
135
PNG_MAGIC = int ("89504e47" ,16 ).to_bytes (4 ,byteorder = 'big' )
@@ -123,8 +144,6 @@ def __init__(self, *args):
123
144
self .frame_length_max = self .frame_length / max (self .frame_length ,self .matcher .frame_length )
124
145
self .frames_backtrack = 0
125
146
self .frames_lookahead = int (max (1.0 / self .frame_length_max ,2 ))
126
-
127
- self .out_proc = self .__create_output_proc ()
128
147
129
148
def set_min_cached_frames (self ,mcf ):
130
149
# if a decayed frame is about to be used, we fetch the frame + this amount of frames around it
@@ -217,20 +236,6 @@ def get_progress_strings(self):
217
236
strs .append (f"{ self .framecount - self .cache .decayed_items } /{ self .framecount } cached frames" )
218
237
return strs
219
238
220
- def __create_output_proc (self ):
221
- call = get_output_cmd (
222
- self ,
223
- [
224
- '-f' , 'image2pipe' , '-i' , 'pipe:' ,
225
- '-i' , str (self .temp_dir / 'out.wav' )
226
- ]
227
- )
228
-
229
- return subprocess .Popen (
230
- call ,
231
- stdin = subprocess .PIPE ,
232
- stdout = subprocess .DEVNULL
233
- )
234
239
235
240
def complete (self ):
236
241
super ().complete ()
0 commit comments