4
4
from dataclasses import dataclass , field
5
5
from fractions import Fraction
6
6
7
+ import av
8
+ from av .audio .resampler import AudioResampler
9
+
7
10
from auto_editor .ffwrapper import FFmpeg , FileInfo
11
+ from auto_editor .utils .bar import Bar
8
12
from auto_editor .utils .container import Container
9
13
from auto_editor .utils .log import Log
10
14
from auto_editor .utils .types import Args
11
15
16
+ av .logging .set_level (av .logging .VERBOSE )
17
+
12
18
13
19
@dataclass (slots = True )
14
20
class Ensure :
15
21
_ffmpeg : FFmpeg
22
+ _bar : Bar
16
23
_sr : int
17
24
temp : str
18
25
log : Log
@@ -31,12 +38,42 @@ def audio(self, src: FileInfo, stream: int) -> str:
31
38
out_path = os .path .join (self .temp , f"{ label :x} .wav" )
32
39
33
40
if first_time :
41
+ sample_rate = self ._sr
42
+ bar = self ._bar
34
43
self .log .debug (f"Making external audio: { out_path } " )
35
- self .log .conwrite ("Extracting audio" )
36
44
37
- cmd = ["-i" , f"{ src .path } " , "-map" , f"0:a:{ stream } " ]
38
- cmd += ["-ac" , "2" , "-ar" , f"{ self ._sr } " , "-rf64" , "always" , out_path ]
39
- self ._ffmpeg .run (cmd )
45
+ in_container = av .open (src .path , "r" )
46
+ out_container = av .open (
47
+ out_path , "w" , format = "wav" , options = {"rf64" : "always" }
48
+ )
49
+ astream = in_container .streams .audio [stream ]
50
+
51
+ if astream .duration is None or astream .time_base is None :
52
+ dur = 0
53
+ else :
54
+ dur = int (astream .duration * astream .time_base )
55
+
56
+ bar .start (dur , "Extracting audio" )
57
+
58
+ # PyAV always uses "stereo" layout, which is what we want.
59
+ output_astream = out_container .add_stream ("pcm_s16le" , rate = sample_rate )
60
+ assert isinstance (output_astream , av .audio .stream .AudioStream )
61
+
62
+ resampler = AudioResampler (format = "s16" , layout = "stereo" , rate = sample_rate ) # type: ignore
63
+ for i , frame in enumerate (in_container .decode (astream )):
64
+ if i % 1500 == 0 :
65
+ bar .tick (0 if frame .time is None else frame .time )
66
+
67
+ for new_frame in resampler .resample (frame ):
68
+ for packet in output_astream .encode (new_frame ):
69
+ out_container .mux_one (packet )
70
+
71
+ for packet in output_astream .encode ():
72
+ out_container .mux_one (packet )
73
+
74
+ out_container .close ()
75
+ in_container .close ()
76
+ bar .end ()
40
77
41
78
return out_path
42
79
0 commit comments