Bash Make mp4 with ffmpeg
As part of learning how to use ffmpeg to make mp4 videos, I wrote this script to stitch a bunch of files that started with ss*.png into an mp3 called ss.mp3. This was the proof of concept for the more advanced version that creates drag and drop thumbnails.
One Liner Command
ffmpeg -loop 1 -t 2 -i 1.png -loop 1 -t 2 -i 2.png -loop 1 -t 2 -i 3.png -i s.mp3 -filter_complex "[0:v]scale=1960:2472,setdar=1[v0];[1:v]scale=1960:2472,setdar=1[v1];[2:v]scale=1960:2472,setdar=1[v2];[v0][v1][v2]concat=n=3:v=1:a=0[v]" -map "[v]" -map 3:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest output.mp4
Once we saw the ffmpeg command worked, then we made a script for multiple images, however, it puts the images every 2 seconds instead of at the time I wanted them. Still this was a process in learning. The final script is in python.
#!/usr/bin/bash
# Function to print usage information
usage() {
echo "Usage: $0 -i 'image_glob' -a audio_file -o output_file"
echo "Example: $0 -i 'ss*.png' -a ss.mp3 -o output.mp4"
exit 1
}
# Parse command line arguments
while getopts "i:a:o:" opt; do
case ${opt} in
i)
image_glob=${OPTARG}
;;
a)
audio_file=${OPTARG}
;;
o)
output_file=${OPTARG}
;;
*)
usage
;;
esac
done
# Check if required arguments are provided
if [[ -z "$image_glob" || -z "$audio_file" || -z "$output_file" ]]; then
usage
fi
# Collect images from the glob pattern
images=($(ls $image_glob))
# Check if any images are found
if [ ${#images[@]} -eq 0 ]; then
echo "No images found matching the glob pattern."
exit 1
fi
# Construct the FFmpeg input arguments and filter_complex parts
input_args=""
filter_complex=""
map_args=""
for i in "${!images[@]}"; do
input_args+="-loop 1 -t 2 -i ${images[$i]} "
filter_complex+="[$i:v]scale=1960:2472,setdar=1[v$i];"
map_args+="-map $i "
done
# Append the final part of the filter_complex
concat_inputs=$(printf "[v%d]" $(seq 0 $((${#images[@]}-1))))
filter_complex+="${concat_inputs}concat=n=${#images[@]}:v=1:a=0[v]"
# Construct the complete FFmpeg command
ffmpeg_command="ffmpeg $input_args -i $audio_file -filter_complex \"$filter_complex\" -map \"[v]\" -map ${#images[@]}:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest $output_file"
# Print the constructed command for debugging
echo "Constructed FFmpeg command:"
echo $ffmpeg_command
# Execute the FFmpeg command
eval $ffmpeg_command