Follow me on YouTube and Twitter for more video editing tricks with ffmpeg and a little python now!
I recently updated my python script for automatically removing the silence parts of a video.
Previously, I had to call the shell script separate to generate the silence timestamps.
Now, the python script grabs the output of the shell script directly using subprocess run.
Script
#!/usr/bin/env python
import sys
import subprocess
import os
from moviepy.editor import VideoFileClip, concatenate_videoclips
input_path = sys.argv[1]
out_path = sys.argv[2]
threshold = sys.argv[3]
duration = sys.argv[4]
try:
ease = float(sys.argv[5])
except IndexError:
ease = 0.2
minimum_duration = 1.0
def generate_timestamps(path, threshold, duration):
command = "detect_silence {} {} {}".format(input_path, threshold, duration)
output = subprocess.run(command, shell=True, capture_output=True, text=True)
return output.stdout.split('\n')[:-1]
def main():
count = 0
last = 0
timestamps = generate_timestamps(input_path, threshold, duration)
print("Timestamps: {}".format(timestamps))
video = VideoFileClip(input_path)
full_duration = video.duration
clips = []
for times in timestamps:
end,dur = times.strip().split()
print("End: {}, Duration: {}".format(end, dur))
to = float(end) - float(dur) + ease
start = float(last)
clip_duration = float(to) - start
# Clips less than one seconds don't seem to work
print("Clip Duration: {} seconds".format(clip_duration))
if clip_duration < minimum_duration:
continue
if full_duration - to < minimum_duration:
continue
print("Clip {} (Start: {}, End: {})".format(count, start, to))
clip = video.subclip(start, to)
clips.append(clip)
last = end
count += 1
if not clips:
print("No silence detected, exiting...")
return
if full_duration - float(last) > minimum_duration:
print("Clip {} (Start: {}, End: {})".format(count, last, 'EOF'))
clips.append(video.subclip(last))
processed_video = concatenate_videoclips(clips)
processed_video.write_videofile(
out_path,
fps=60,
preset='ultrafast',
codec='libx264',
audio_codec='aac'
)
video.close()
main()
I won't go over this in full detail, as I did that in the last post about the silence trimming script. I will break down the changes I made.
For a break down of the scripts in more detail, check out the last post I made about it.
Automatically trim silence from video with ffmpeg and python
Donald Feury ・ May 9 '20 ・ 4 min read
Changes
def generate_timestamps(path, threshold, duration):
command = "detect_silence {} {} {}".format(input_path, threshold, duration)
output = subprocess.run(command, shell=True, capture_output=True, text=True)
return output.stdout.split('\n')[:-1]
Here I created a function to pass in the arguments needed by the detect silence script, and execute it using subprocess.run.
It needs the capture_output=True
to actually save the output, and text=True
to make the output be in the form of a string, otherwise its returned as raw bytes.
I then split on the newlines and remove the last entry, as its an empty string that is not needed.
Since we are grabbing the script output straight from stdout, we no longer have to open and read an arbitrary text file to get the timestamps.
One last change was, before I was adding a padding to the start of the next clip, to make the transitions less abrupt. Now I add it the end of the last clip, as it seems more natural.
if not clips:
print("No silence detected, exiting...")
return
I also added this sanity check to make sure there were actually clips generated, can't concatenate clips that don't exist.
Thats it! Now I can remove the silence parts of a video by calling only script! It also avoids having to create the intermittent timestamp file as well.
Top comments (1)
added some comments