I like to create a Bash script to concatenate MP4 files using FFmpeg, in batches of 5 files at a time for a directory with 100 MP4 files, so that afterwards there would be 20 files like:
001_005.mp4, 006_010.mp4, and so on...
instead of just 1 file consisting of all 100 files.
Contents of mylist.txt:
file 001.mp4
file 002.mp4
file 003.mp4
file 004.mp4
file 005.mp4
............
file 099.mp4
file 100.mp4Though I've found a command that works just fine (from this StackOverflow thread), it would create only 1 file consisting all 100 files.
#!/bin/bash
cd /home/admn/Downloads/MP4_Files;
# Create mylist.txt:
for f in *.mp4
do echo "file $f" >> mylist.txt
done;
# Concatenate files:
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4;So, how do I modify the ffmpeg command so that it concat in batches of 5 files at a time.
All the files have exact same resolution (1080p), audio, and video codecs.
OS: Ubuntu MATE 21.04
ffmpeg version: 4.3.2-0+deb11u1ubuntu1
03 Answers
I think the following script will work.
- First try it as it is and check that it seems to do what you want
- Then remove
echofrom the line withffmpegto make it do its thing.
Check that the content of the temporary files xaa' ... xat` matches the names (and content) of the output files.
#!/bin/bash
> mylist.txt
for f in *.mp4
do echo "file '$f'" >> mylist.txt
done
< mylist.txt sort -t \' -n -k2 | split -l 5
k=1
for j in x*
do inc=$(wc -l "$j" | cut -d ' ' -f 1) m=$(printf "%03d" $((k))) n=$(printf "%03d" $((k+inc-1))) name="${m}_${n}.mp4" echo ffmpeg -f concat -safe 0 -i "$j" -c copy "$name" k=$((k+5))
done 9 Another solution is to use nested loops.
As long as there are files remaining, the inner loop uses ${@:1:5} to take the next slice of (up to) 5 files.
#!/bin/bash
cd /home/admn/Downloads/MP4_Files;
shopt -s failglob
set -- *.mp4
while [[ $# -gt 0 ]]; do from=$(basename "$1" .mp4) for f in "${@:1:5}"; do #not essential in your case, but use @Q to quote/escape special characters echo "file ${f@Q}" >> mylist.txt shift done to=$(basename "$f" .mp4) ffmpeg -f concat -safe 0 -i mylist.txt -c copy "${from}_${to}.mp4" rm mylist.txt
done 3 Assuming mylist.txt exists in your current working directory, you can do something like this,
LINES=$(cat mylist.txt | wc -l)
BATCH_SIZE=5
BATCHES=$(($LINES/$BATCH_SIZE))
for ((i=0;i<$BATCHES;i++))
do cat mylist.txt | head -n $((($i+1)*$BATCH_SIZE)) | tail -n $BATCH_SIZE > slist.txt ffmpeg -f concat -safe 0 -i slist.txt -c copy $(($i*$BATCH_SIZE+1))_$((($i+1)*$BATCH_SIZE)).mp4
done
rm slist.txt 3