27
27
from auto_editor .utils .log import Log
28
28
29
29
30
+ __all__ = ("LevelError" , "Levels" , "iter_audio" , "iter_motion" )
31
+
32
+
30
33
class LevelError (Exception ):
31
34
pass
32
35
@@ -69,45 +72,39 @@ def mut_remove_large(
69
72
active = False
70
73
71
74
72
- def iter_audio (src , tb : Fraction , stream : int = 0 ) -> Iterator [np .float32 ]:
75
+ def iter_audio (audio_stream : av . AudioStream , tb : Fraction ) -> Iterator [np .float32 ]:
73
76
fifo = AudioFifo ()
74
- try :
75
- container = av .open (src .path , "r" )
76
- audio_stream = container .streams .audio [stream ]
77
- sample_rate = audio_stream .rate
77
+ sr = audio_stream .rate
78
78
79
- exact_size = (1 / tb ) * sample_rate
80
- accumulated_error = 0
79
+ exact_size = (1 / tb ) * sr
80
+ accumulated_error = Fraction ( 0 )
81
81
82
- # Resample so that audio data is between [-1, 1]
83
- resampler = av .AudioResampler (
84
- av .AudioFormat ("flt" ), audio_stream .layout , sample_rate
85
- )
82
+ # Resample so that audio data is between [-1, 1]
83
+ resampler = av .AudioResampler (av .AudioFormat ("flt" ), audio_stream .layout , sr )
86
84
87
- for frame in container . decode ( audio = stream ):
88
- frame . pts = None # Skip time checks
85
+ container = audio_stream . container
86
+ assert isinstance ( container , av . container . InputContainer )
89
87
90
- for reframe in resampler . resample ( frame ):
91
- fifo . write ( reframe )
88
+ for frame in container . decode ( audio_stream ):
89
+ frame . pts = None # Skip time checks
92
90
93
- while fifo .samples >= ceil (exact_size ):
94
- size_with_error = exact_size + accumulated_error
95
- current_size = round (size_with_error )
96
- accumulated_error = size_with_error - current_size
91
+ for reframe in resampler .resample (frame ):
92
+ fifo .write (reframe )
97
93
98
- audio_chunk = fifo .read (current_size )
99
- assert audio_chunk is not None
100
- arr = audio_chunk .to_ndarray ().flatten ()
101
- yield np .max (np .abs (arr ))
102
-
103
- finally :
104
- container .close ()
94
+ while fifo .samples >= ceil (exact_size ):
95
+ size_with_error = exact_size + accumulated_error
96
+ current_size = round (size_with_error )
97
+ accumulated_error = size_with_error - current_size
105
98
99
+ audio_chunk = fifo .read (current_size )
100
+ assert audio_chunk is not None
101
+ arr = audio_chunk .to_ndarray ().flatten ()
102
+ yield np .max (np .abs (arr ))
106
103
107
- def iter_motion (src , tb , stream : int , blur : int , width : int ) -> Iterator [np .float32 ]:
108
- container = av .open (src .path , "r" )
109
104
110
- video = container .streams .video [stream ]
105
+ def iter_motion (
106
+ video : av .VideoStream , tb : Fraction , blur : int , width : int
107
+ ) -> Iterator [np .float32 ]:
111
108
video .thread_type = "AUTO"
112
109
113
110
prev_frame = None
@@ -125,6 +122,9 @@ def iter_motion(src, tb, stream: int, blur: int, width: int) -> Iterator[np.floa
125
122
graph .add ("buffersink" ),
126
123
).configure ()
127
124
125
+ container = video .container
126
+ assert isinstance (container , av .container .InputContainer )
127
+
128
128
for unframe in container .decode (video ):
129
129
if unframe .pts is None :
130
130
continue
@@ -151,8 +151,6 @@ def iter_motion(src, tb, stream: int, blur: int, width: int) -> Iterator[np.floa
151
151
prev_frame = current_frame
152
152
prev_index = index
153
153
154
- container .close ()
155
-
156
154
157
155
def obj_tag (path : Path , kind : str , tb : Fraction , obj : Sequence [object ]) -> str :
158
156
mod_time = int (path .stat ().st_mtime )
@@ -175,7 +173,11 @@ def media_length(self) -> int:
175
173
if (arr := self .read_cache ("audio" , (0 ,))) is not None :
176
174
return len (arr )
177
175
178
- result = sum (1 for _ in iter_audio (self .src , self .tb , 0 ))
176
+ with av .open (self .src .path , "r" ) as container :
177
+ audio_stream = container .streams .audio [0 ]
178
+ self .log .experimental (audio_stream .codec )
179
+ result = sum (1 for _ in iter_audio (audio_stream , self .tb ))
180
+
179
181
self .log .debug (f"Audio Length: { result } " )
180
182
return result
181
183
@@ -239,21 +241,26 @@ def audio(self, stream: int) -> NDArray[np.float32]:
239
241
if (arr := self .read_cache ("audio" , (stream ,))) is not None :
240
242
return arr
241
243
242
- with av .open (self .src .path , "r" ) as container :
243
- audio = container .streams .audio [stream ]
244
- if audio .duration is not None and audio .time_base is not None :
245
- inaccurate_dur = int (audio .duration * audio .time_base * self .tb )
246
- elif container .duration is not None :
247
- inaccurate_dur = int (container .duration / av .time_base * self .tb )
248
- else :
249
- inaccurate_dur = 1024
244
+ container = av .open (self .src .path , "r" )
245
+ audio = container .streams .audio [stream ]
246
+
247
+ if audio .codec .experimental :
248
+ self .log .error (f"`{ audio .codec .name } ` is an experimental codec" )
249
+
250
+ if audio .duration is not None and audio .time_base is not None :
251
+ inaccurate_dur = int (audio .duration * audio .time_base * self .tb )
252
+ elif container .duration is not None :
253
+ inaccurate_dur = int (container .duration / av .time_base * self .tb )
254
+ else :
255
+ inaccurate_dur = 1024
250
256
251
257
bar = self .bar
252
258
bar .start (inaccurate_dur , "Analyzing audio volume" )
253
259
254
260
result = np .zeros ((inaccurate_dur ), dtype = np .float32 )
255
261
index = 0
256
- for value in iter_audio (self .src , self .tb , stream ):
262
+
263
+ for value in iter_audio (audio , self .tb ):
257
264
if index > len (result ) - 1 :
258
265
result = np .concatenate (
259
266
(result , np .zeros ((len (result )), dtype = np .float32 ))
@@ -263,6 +270,7 @@ def audio(self, stream: int) -> NDArray[np.float32]:
263
270
index += 1
264
271
265
272
bar .end ()
273
+ assert len (result ) > 0
266
274
return self .cache (result [:index ], "audio" , (stream ,))
267
275
268
276
def motion (self , stream : int , blur : int , width : int ) -> NDArray [np .float32 ]:
@@ -273,20 +281,25 @@ def motion(self, stream: int, blur: int, width: int) -> NDArray[np.float32]:
273
281
if (arr := self .read_cache ("motion" , mobj )) is not None :
274
282
return arr
275
283
276
- with av .open (self .src .path , "r" ) as container :
277
- video = container .streams .video [stream ]
278
- inaccurate_dur = (
279
- 1024
280
- if video .duration is None or video .time_base is None
281
- else int (video .duration * video .time_base * self .tb )
282
- )
284
+ container = av .open (self .src .path , "r" )
285
+ video = container .streams .video [stream ]
286
+
287
+ if video .codec .experimental :
288
+ self .log .experimental (video .codec )
289
+
290
+ inaccurate_dur = (
291
+ 1024
292
+ if video .duration is None or video .time_base is None
293
+ else int (video .duration * video .time_base * self .tb )
294
+ )
283
295
284
296
bar = self .bar
285
297
bar .start (inaccurate_dur , "Analyzing motion" )
286
298
287
299
result = np .zeros ((inaccurate_dur ), dtype = np .float32 )
288
300
index = 0
289
- for value in iter_motion (self .src , self .tb , stream , blur , width ):
301
+
302
+ for value in iter_motion (video , self .tb , blur , width ):
290
303
if index > len (result ) - 1 :
291
304
result = np .concatenate (
292
305
(result , np .zeros ((len (result )), dtype = np .float32 ))
0 commit comments