Skip to content

Commit f619909

Browse files
committed
Use pyav for extracting audio
1 parent 326aea4 commit f619909

File tree

4 files changed

+44
-7
lines changed

4 files changed

+44
-7
lines changed

auto_editor/edit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def edit_media(
209209
else:
210210
samplerate = args.sample_rate
211211

212-
ensure = Ensure(ffmpeg, samplerate, temp, log)
212+
ensure = Ensure(ffmpeg, bar, samplerate, temp, log)
213213

214214
if tl is None:
215215
tl = make_timeline(sources, ensure, args, samplerate, bar, temp, log)

auto_editor/output.py

+41-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
from dataclasses import dataclass, field
55
from fractions import Fraction
66

7+
import av
8+
from av.audio.resampler import AudioResampler
9+
710
from auto_editor.ffwrapper import FFmpeg, FileInfo
11+
from auto_editor.utils.bar import Bar
812
from auto_editor.utils.container import Container
913
from auto_editor.utils.log import Log
1014
from auto_editor.utils.types import Args
1115

16+
av.logging.set_level(av.logging.VERBOSE)
17+
1218

1319
@dataclass(slots=True)
1420
class Ensure:
1521
_ffmpeg: FFmpeg
22+
_bar: Bar
1623
_sr: int
1724
temp: str
1825
log: Log
@@ -31,12 +38,42 @@ def audio(self, src: FileInfo, stream: int) -> str:
3138
out_path = os.path.join(self.temp, f"{label:x}.wav")
3239

3340
if first_time:
41+
sample_rate = self._sr
42+
bar = self._bar
3443
self.log.debug(f"Making external audio: {out_path}")
35-
self.log.conwrite("Extracting audio")
3644

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()
4077

4178
return out_path
4279

auto_editor/subcommands/levels.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
8585
src = sources[0]
8686

8787
tb = src.get_fps() if args.timebase is None else args.timebase
88-
ensure = Ensure(ffmpeg, src.get_sr(), temp, log)
88+
ensure = Ensure(ffmpeg, bar, src.get_sr(), temp, log)
8989

9090
if ":" in args.edit:
9191
method, attrs = args.edit.split(":", 1)

auto_editor/subcommands/repl.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
7373
sources = [initFileInfo(path, log) for path in args.input]
7474
src = sources[0]
7575
tb = src.get_fps() if args.timebase is None else args.timebase
76-
ensure = Ensure(ffmpeg, src.get_sr(), temp, log)
7776
bar = Bar("none")
77+
ensure = Ensure(ffmpeg, bar, src.get_sr(), temp, log)
7878
env["timebase"] = tb
7979
env["@levels"] = Levels(ensure, src, tb, bar, temp, log)
8080
env["@filesetup"] = FileSetup(src, ensure, strict, tb, bar, temp, log)

0 commit comments

Comments
 (0)