Script for cutting videos based on edit list
Solution 1:
For this you need FFmpeg (download a static build) and Bash.
Create a CSV-like list of input and output points, e.g.:
00:01:00,00:02:00
00:03:00,00:03:02
…
You can then run the following Bash script to cut the video into individual parts:
#!/usr/bin/env bash
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <input> <editList>"
exit 1
fi
inputFile=$1
editFile=$2
inputFileBase="${inputFile%.*}"
extension="${inputFile##*.}"
cnt=0
while IFS=, read -r start end; do
suffix="$(printf "%05d" $cnt)"
outfile="${inputFileBase}-${suffix}.${extension}"
ffmpeg -nostdin -ss "$start" -i "$inputFile" -to "$end" -c copy -map 0 "$outfile"
cnt=$((cnt+1))
done < "$editFile"
Save this file as cut.sh
(or similar), then run:
chmod +x cut.sh
./cut.sh /path/to/input.mp4 /path/to/editList.csv
It'll copy the video and audio bitstreams over to output files, sequentially numbered with the suffix 00001
, 00002
and so on.
Old answer below …
I once wrote a Ruby script which does exactly that. You need Ruby ≥1.9.2 (e.g. through RVM) and a recent version of FFmpeg installed (see here on how to install from source).
-
My script is available here:
video-extract.rb
-
You need to feed it with a CSV input list of edits, most importantly containing the following columns:
- a prefix (can be empty)
- a video ID (some sequential number)
- the input file name
- the in point in
HH:MM:SS.ms
or seconds - the length of the edit in
HH:MM:SS.ms
or seconds
For example (note that the Out
column is unused):
- Then, adjust the variables in the script header. Most importantly, change
COPY
totrue
if you want a bitstream copy and no re-encode. Also change the index of the CSV columns and the CSV separator.
Feel free to improve the script or suggest changes (especially if you already know Ruby). I've used this script very often and haven't run into problems yet. The only thing that's missing is proper audio support – it'll just copy the audio stream, which might or might not work in your case. In case of trouble, report back.
If you need to calculate the difference between an in and out-point, you can do this with this little Ruby script, based on this Stack Overflow Q&A:
require "Time"
def time_diff(time1_str, time2_str)
t = Time.at( Time.parse(time2_str) - Time.parse(time1_str) )
(t - t.gmt_offset).strftime("%H:%M:%S.%L")
end
ins, outs, diffs = File.open("ins.txt"), File.open("outs.txt"), File.new("diffs.txt", "w")
inlines, outlines = [], []
ins.each { |l| inlines << l }
outs.each { |l| outlines << l }
inlines.zip(outlines).each { |ins, outs| diffs.puts time_diff(ins, outs) }
diffs.close
You just create a file called ins.txt
and outs.txt
where each line corresponds to an in- and out-point (see the screenshot above). The difference will be written to diffs.txt
. Simple as that.