8000 Add `split_silence` example · squidarth/ffmpeg-python@4311e33 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4311e33

Browse files
committed
Add split_silence example
1 parent f818cff commit 4311e33

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

examples/split_silence.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python
2+
from __future__ import unicode_literals
3+
4+
import argparse
5+
import ffmpeg
6+
import logging
7+
import re
8+
import subprocess
9+
10+
11+
logging.basicConfig(level=logging.INFO)
12+
logger = logging.getLogger(__file__)
13+
logger.setLevel(logging.INFO)
14+
15+
DEFAULT_DURATION = 0.3
16+
DEFAULT_THRESHOLD = -60
17+
18+
parser = argparse.ArgumentParser(description='Split media into separate chunks wherever silence occurs')
19+
parser.add_argument('in_filename', help='Input filename (`-` for stdin)')
20+
parser.add_argument('out_pattern', help='Output filename pattern (e.g. `out/chunk_%%04d.wav`)')
21+
parser.add_argument('--silence-threshold', default=DEFAULT_THRESHOLD, type=int, help='Silence threshold (in dB)')
22+
parser.add_argument('--silence-duration', default=DEFAULT_DURATION, type=float, help='Silence duration')
23+
parser.add_argument('--start-time', type=float, help='Start time (seconds)')
24+
parser.add_argument('--end-time', type=float, help='End time (seconds)')
25+
26+
27+
silence_start_re = re.compile(' silence_start: (?P<start>[0-9]+(\.?[0-9]*))$')
28+
silence_end_re = re.compile(' silence_end: (?P<end>[0-9]+(\.?[0-9]*)) ')
29+
total_duration_re = re.compile(
30+
'size=[^ ]+ time=(?P<hours>[0-9]{2}):(?P<minutes>[0-9]{2}):(?P<seconds>[0-9\.]{5}) bitrate=')
31+
32+
33+
def get_chunk_times(in_filename, silence_threshold, silence_duration, start_time=None, end_time=None):
34+
input_kwargs = {}
35+
if start_time is not None:
36+
input_kwargs['ss'] = start_time
37+
else:
38+
start_time = 0.
39+
if end_time is not None:
40+
input_kwargs['t'] = end_time - start_time
41+
42+
args = (ffmpeg
43+
.input(in_filename, **input_kwargs)
44+
.filter_('silencedetect', n='{}dB'.format(silence_threshold), d=silence_duration)
45+
.output('-', format='null')
46+
.get_args()
47+
)
48+
p = subprocess.Popen(['ffmpeg'] + args, stderr=subprocess.PIPE)
49+
output = p.communicate()[1].decode('utf-8')
50+
lines = output.splitlines()
51+
52+
# Chunks start when silence ends, and chunks end when silence starts.
53+
chunk_starts = []
54+
chunk_ends = []
55+
for line in lines:
56+
silence_start_match = silence_start_re.search(line)
57+
silence_end_match = silence_end_re.search(line)
58+
total_duration_match = total_duration_re.search(line)
59+
if silence_start_match:
60+
chunk_ends.append(float(silence_start_match.group('start')))
61+
if len(chunk_starts) == 0:
62+
# Started with non-silence.
63+
chunk_starts.append(start_time or 0.)
64+
elif silence_end_match:
65+
chunk_starts.append(float(silence_end_match.group('end')))
66+
elif total_duration_match:
67+
hours = int(total_duration_match.group('hours'))
68+
minutes = int(total_duration_match.group('minutes'))
69+
seconds = float(total_duration_match.group('seconds'))
70+
end_time = hours * 3600 + minutes * 60 + seconds
71+
72+
if len(chunk_starts) == 0:
73+
# No silence found.
74+
chunk_starts.append(start_time)
75+
76+
if len(chunk_starts) > len(chunk_ends):
77+
# Finished with non-silence.
78+
chunk_ends.append(end_time or 10000000.)
79+
80+
return list(zip(chunk_starts, chunk_ends))
81+
82+
83+
def split_audio(
84+
in_filename,
85+
out_pattern,
86+
silence_threshold=DEFAULT_THRESHOLD,
87+
silence_duration=DEFAULT_DURATION,
88+
start_time=None,
89+
end_time=None,
90+
):
91+
chunk_times = get_chunk_times(in_filename, silence_threshold, silence_duration, start_time, end_time)
92+
for i, (start_time, end_time) in enumerate(chunk_times):
93+
time = end_time - start_time
94+
out_filename = out_pattern % i
95+
logger.info('{}: start={:.02f}, end={:.02f}, duration={:.02f}'.format(out_filename, start_time, end_time,
96+
time))
97+
subprocess.Popen(
98+
(ffmpeg
99+
.input(in_filename, ss=start_time, t=time)
100+
.output(out_filename)
101+
.overwrite_output()
102+
.compile()
103+
),
104+
stdout=subprocess.PIPE,
105+
stderr=subprocess.PIPE,
106+
).communicate()
107+
108+
109+
if __name__ == '__main__':
110+
args = parser.parse_args()
111+
split_audio(**vars(args))

0 commit comments

Comments
 (0)
0