I have written some ffmpeg shell scripts to process audio and video,
and perform some tasks you would normally need to use a video editor for.
If anyone has a free moment for some peer review or feedback that would be great,
i have already used shellcheck on all the scripts but if anyone can spot anything i missed let me know
Here is a list of the scripts and what they do
combine video and audio files into new clip
Suppose you have extracted the audio from a video clip to clean up with a program like audacity
and you want to reattach the new clean audio to the original video track.
Normally you would open a video editor import the original video, delete the audio track import the clean audio and export the video
The combine-clips script accepts a video file and audio file,
and then combines the original video with the new audio file and saves it in a new video file.
The script will convert the audio to aac if it is a different codec otherwise it will just copy the audio and video into a new video file
combine-clip -v video.(mp4|mov|mkv|m4v) -a audio.(m4a|aac|wav|mp3)
ebu-meter
The ebu-meter uses ffplay to display a real time audio meter using the ebu123 loudness standard
Note the Freebsd ffmpeg package doesn't include ffplay so you need to use poudriere to build a custom version of ffmpeg and enable SDL to build and install ffplay
ebu-meter -i infile.(mp4|mov|mkv|m4v|m4a|aac|wav|mp3)
fade-clip
fade video and audio in and out
fade-clip -i video.(mp4|mkv|mov|m4v)
fade-normalize
fade video and audio in and out and normalize the audio
The fade-normalize script fades the audio and video in and out and also normalizes the audio using the ebu128 standard,
use a noise gate and compressor, high and low audio pass, white noise removal, click removal and deesser
fade-normalize -i video.(mp4|mkv|mov|m4v)
fade-title
fade video and audio in and out, normalize and create video title from filename
The fade-title script fades the audio and video in and out and also normalizes the audio using the ebu128 standard,
use a noise gate and compressor, high and low audio pass, white noise removal, click removal and deesser
It then uses the ffmpeg drawtext and drawbox filters to create a lower third title using the video's filename for the title,
the text using the drawtext filter is faded in and out,
but the lower third bar which uses the drawbox filter doesnt support any opacity options to fade at the moment
fade-title -i video.(mp4|mkv|mov|m4v)
loudnorm
The loudnorm script analyzes a video or audio file and gives you stats about the loudness of the file using lufs and ebu128 standard
loudnorm -i infile.(mkv|mp4|mov|m4v|m4a|aac|wav|mp3)
normalize
normalize audio levels in a audio or video file to the ebu128 standard
normalize -i infile.(mp4|mkv|mov|m4v|aac|m4a|wav|mp3)
subtitle-add
add subtitles to a video file
The subtitle-add script lets you add a srt subtitles to a video file as a track that can be enabled or disabled by the user,
note this isnt hard subs burned on top of the video.
The video file with the subtitle track works in all players i have tested including Quicktime on the Mac
subtitle-add -v video.(mp4|mov|mkv|m4v) -s subtitle.srt
trim-clip
The trim-clip script lets you trim a video clip by specifying the start point and the number of seconds after the start point to create a new clip from
trim-clip -ss 00:00:00 -i video.(mp4|mov|mkv|m4v) -t 00:00:00
waveform
create a waveform from an audio or video file and save as a png
waveform -i infile.(mp4|mkv|mov|m4v|wav|aac|m4a|mp3)
fade-title code
This is the code for the fade-title script,
all of the ffmpeg shell scripts are on github
Lower third video title example
This is a screen shot of Big Bug Bunny with a lower third titles using the fade-title script
and perform some tasks you would normally need to use a video editor for.
If anyone has a free moment for some peer review or feedback that would be great,
i have already used shellcheck on all the scripts but if anyone can spot anything i missed let me know
Here is a list of the scripts and what they do
- combine-clips
- ebu-meter
- fade-clip
- fade-normalize
- fade-title
- loudnorm
- normalize
- subtitle-add
- trim-clip
- waveform
combine video and audio files into new clip
Suppose you have extracted the audio from a video clip to clean up with a program like audacity
and you want to reattach the new clean audio to the original video track.
Normally you would open a video editor import the original video, delete the audio track import the clean audio and export the video
The combine-clips script accepts a video file and audio file,
and then combines the original video with the new audio file and saves it in a new video file.
The script will convert the audio to aac if it is a different codec otherwise it will just copy the audio and video into a new video file
- script usage
combine-clip -v video.(mp4|mov|mkv|m4v) -a audio.(m4a|aac|wav|mp3)
ebu-meter
The ebu-meter uses ffplay to display a real time audio meter using the ebu123 loudness standard
Note the Freebsd ffmpeg package doesn't include ffplay so you need to use poudriere to build a custom version of ffmpeg and enable SDL to build and install ffplay
- script usage
ebu-meter -i infile.(mp4|mov|mkv|m4v|m4a|aac|wav|mp3)
fade-clip
fade video and audio in and out
- script usage
fade-clip -i video.(mp4|mkv|mov|m4v)
fade-normalize
fade video and audio in and out and normalize the audio
The fade-normalize script fades the audio and video in and out and also normalizes the audio using the ebu128 standard,
use a noise gate and compressor, high and low audio pass, white noise removal, click removal and deesser
- script usage
fade-normalize -i video.(mp4|mkv|mov|m4v)
fade-title
fade video and audio in and out, normalize and create video title from filename
The fade-title script fades the audio and video in and out and also normalizes the audio using the ebu128 standard,
use a noise gate and compressor, high and low audio pass, white noise removal, click removal and deesser
It then uses the ffmpeg drawtext and drawbox filters to create a lower third title using the video's filename for the title,
the text using the drawtext filter is faded in and out,
but the lower third bar which uses the drawbox filter doesnt support any opacity options to fade at the moment
- script usage
fade-title -i video.(mp4|mkv|mov|m4v)
loudnorm
The loudnorm script analyzes a video or audio file and gives you stats about the loudness of the file using lufs and ebu128 standard
- script usage
loudnorm -i infile.(mkv|mp4|mov|m4v|m4a|aac|wav|mp3)
normalize
normalize audio levels in a audio or video file to the ebu128 standard
- script usage
normalize -i infile.(mp4|mkv|mov|m4v|aac|m4a|wav|mp3)
subtitle-add
add subtitles to a video file
The subtitle-add script lets you add a srt subtitles to a video file as a track that can be enabled or disabled by the user,
note this isnt hard subs burned on top of the video.
The video file with the subtitle track works in all players i have tested including Quicktime on the Mac
- script usage
subtitle-add -v video.(mp4|mov|mkv|m4v) -s subtitle.srt
trim-clip
The trim-clip script lets you trim a video clip by specifying the start point and the number of seconds after the start point to create a new clip from
- script usage
trim-clip -ss 00:00:00 -i video.(mp4|mov|mkv|m4v) -t 00:00:00
waveform
create a waveform from an audio or video file and save as a png
- script usage
waveform -i infile.(mp4|mkv|mov|m4v|wav|aac|m4a|mp3)
fade-title code
This is the code for the fade-title script,
all of the ffmpeg shell scripts are on github
Bash:
#!/bin/sh
# fade video, audio add title from video filename
# script usage
script_usage="$(basename "$0") -i infile.(mp4|mkv|mov|m4v)"
# error messages
HOME_ERR='HOME directory not set or null'
# check arguments passed to script
if [ $# -eq 2 ]; then
{
[ "$1" = '-i' ] && \
[ -f "$2" ]
} || { echo "$script_usage" && exit; }
else
{ echo "$script_usage" && exit; }
fi
# infile, infile name and file extension
infile="$2"
infile_nopath="${infile##*/}"
infile_name="${infile_nopath%.*}"
infile_ext="${infile##*.}"
# file command check input file mime type
filetype="$(file --mime-type -b "$infile")"
# video mimetypes
mov_mime='video/quicktime'
mkv_mime='video/x-matroska'
mp4_mime='video/mp4'
m4v_mime='video/x-m4v'
# check the files mime type
case "$filetype" in
${mov_mime}|${mkv_mime}|${mp4_mime}|${m4v_mime});;
*) { echo "$script_usage" && exit; };;
esac
# file extension regular expressions for case statement
mp4='[Mm][Pp]4'
mkv='[Mm][Kk][Vv]'
mov='[Mm][[Oo][Vv]'
m4v='[Mm]4[Vv]'
# check the file extension
case "$infile_ext" in
${mp4}|${mkv}|${mov}|${m4v});;
*) { echo "$script_usage" && exit; };;
esac
# outfile file recording destination
normalized_file="${HOME:?${HOME_ERR}}/Desktop/${infile_name}-$(date +"%Y-%m-%d-%H-%M-%S").mp4"
# print analyzing file
echo '+ Analyzing file with ffmpeg'
# ffmpeg loudnorm get stats from file
normalize=$(ffmpeg \
-hide_banner \
-i "$infile" \
-af "loudnorm=I=-16:dual_mono=true:TP=-1.5:LRA=11:print_format=summary" \
-f null - 2>&1 | tail -n 12)
# read the output of normalize line by line and store in variables
for line in "$normalize"; do
measured_I=$(echo "$line" | awk -F' ' '/Input Integrated:/ {print $3}')
measured_TP=$(echo "$line" | awk -F' ' '/Input True Peak:/ {print $4}')
measured_LRA=$(echo "$line" | awk -F' ' '/Input LRA:/ {print $3}')
measured_thresh=$(echo "$line" | awk -F' ' '/Input Threshold:/ {print $3}')
offset=$(echo "$line" | awk -F' ' '/Target Offset:/ {print $3}')
done
# video duration and video offset minus 1 second for fade out
video_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$infile" | cut -d\. -f1)
vid_offset=$(echo "${video_dur}-1" | bc -l)
# video height
video_size=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 "$infile")
# video title from filename
title="$infile_name"
# video title variables
font="OpenSans-Regular.ttf"
font_color="white"
boxcolor="black@0.4"
# video title fade
DS=2.0 # display start
DE=16.0 # display end, number of seconds after start
FID=1.5 # fade in duration
FOD=1.5 # fade out duration
# calculate drawbox and drawtext size based on video height
case "$video_size" in
1080) # 1080 height
drawbox_height=$(printf "%s\n" "${video_size}/13.4" | bc)
drawtext_size=$(printf "%s\n" "${drawbox_height}/2" | bc)
;;
720) # 720 height
drawbox_height=$(printf "%s\n" "${video_size}/9" | bc)
drawtext_size=$(printf "%s\n" "${drawbox_height}/2" | bc)
;;
*) # all other heights
drawbox_height=$(printf "%s\n" "${video_size}/9" | bc)
drawtext_size=$(printf "%s\n" "${drawbox_height}/2" | bc)
;;
esac
# drawbox, drawtext size
boxheight="$drawbox_height"
font_size="$drawtext_size"
# video function
video () {
ffmpeg \
-hide_banner \
-stats -v panic \
-i "$infile" \
-filter_complex \
"[0:a] afade=t=in:st=0:d=1,afade=t=out:st='$vid_offset':d=1,
compand=attacks=0:points=-70/-90|-24/-12|0/-6|20/-6,
highpass=f=60,
lowpass=f=13700,
afftdn=nt=w,
adeclick,
deesser,
loudnorm=I=-16:
dual_mono=true:
TP=-1.5:
LRA=11:
measured_I=${measured_I}:
measured_LRA=${measured_LRA}:
measured_TP=${measured_TP}:
measured_thresh=${measured_thresh}:
offset=${offset}:
linear=true:
print_format=summary [audio];
[0:v] fade=t=in:st=0:d=1,fade=t=out:st='$vid_offset':d=1, \
format=yuv444p,
drawbox=enable='between(t,${DS},${DE})':
y=(ih-h/PHI)-(${boxheight}):
color=${boxcolor}:
width=iw:height=${boxheight}:t=fill,
drawtext=fontfile=${font}:
text=${title}:
fontcolor=${font_color}:fontsize=${font_size}:
x=20:
y=h-(${boxheight})-(${boxheight}/2)+th/4:
:fontcolor_expr=fdfdfd%{eif\\\\: clip(255*(1*between(t\\, $DS + $FID\\, $DE - $FOD) + ((t - $DS)/$FID)*between(t\\, $DS\\, $DS + $FID) + (-(t - $DE)/$FOD)*between(t\\, $DE - $FOD\\, $DE) )\\, 0\\, 255) \\\\: x\\\\: 2 }, \
format=yuv420p[video]" \
-map "[video]" -map "[audio]" \
-c:a aac \
-c:v libx264 -preset fast \
-profile:v high \
-crf 18 -coder 1 \
-pix_fmt yuv420p \
-movflags +faststart \
-f mp4 \
"$normalized_file"
}
# run the audio or video function based on the file extension
case "$infile_ext" in
${mp4}|${mkv}|${mov}|${m4v}) video "$infile";;
*) { echo "$script_usage" && exit; };;
esac
Lower third video title example
This is a screen shot of Big Bug Bunny with a lower third titles using the fade-title script