[FFmpeg-user] Using segment and concat with multiple videos containing LTC audio on CH1
robertlazarski
robertlazarski at gmail.com
Tue Jun 5 02:13:38 EEST 2018
On Fri, May 4, 2018 at 8:31 AM, robertlazarski <robertlazarski at gmail.com>
wrote:
> Hello all, first post.
>
> I am trying to segment mov videos from multiple Zoom Q8 cameras with
> exactly the same specs, by using the Linux command ltcdump, bash and
> ffmpeg.
>
To answer my own question and after many weeks later of experimentation,
using segment was the wrong tool for the job.
Each LTC audio frame at 30000/1001 in this case is 1600/48000 or around
.034 seconds and the closest I could come to that with segment while still
being able to read the LTC, was '-force_key_frames
"expr:gte(t,n_forced*0.07)" ' , which usually meant segments of 64ms but
often ranged to 85ms ... didn't work for me.
I had much better luck calculating the -ss and -to times using the sample
position of the LTC. I was still somehow off by about a frame or .034
seconds on each video transition between cameras as the cut accuracy wasn't
good enough. The vocals of the result video clearly had drift.
See below, using 2pass and lots of params because debugging the transcoded
videos is easier the closer they match the source. 16 bit depth instead of
24 on source videos for debugging purposes (long story).
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 1 -passlogfile q81.log -ss
5.506292 -to 25.5264 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
aresample=async=1:min_hard_comp=0.000100:first_pts=0 -crf 18 q81_cut_0.mov
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 2 -passlogfile q81.log -ss
5.506292 -to 25.5264 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
aresample=async=1:min_hard_comp=0.000100:first_pts=0 -crf 18 q81_cut_0.mov
So, 25.5264 - 5.506292 = 20.020108
However:
ffprobe -v error -show_entries format=duration -of
default=noprint_wrappers=1:nokey=1 q81_cut_0.mov
20.054000
Each video in the script below is off by the same amount. Strangely using
an offset of 0.033892 from 20.054000 - 20.020108 didn't work as it cut too
much though splitting the value in half at 0.017 works splendidly. By the
end of the video I was able to come under .002 of drift.
See below for the script, magicNumberOffset="0.017" is as described above.
Might be generally useful as an example how to use bash with ffmpeg.
#!/bin/bash
# This script generates a single video from parts of videos from 2 Zoom Q8
cameras
# by using ltc on ch1 of each video. The priority is as little ltc audio
drift as possible,
# script performance and extra transcoding are not an issue. Little audio
drift in this case
# means the start and end times match the source videos closely, with no
dropped video frames
# nor dropped ltc frames. I was able to get within 88/48000 or 0.00183
seconds by the end of the video,
# and while I have no immediate ideas how to do better I will continue to
try.
# The ltc comes from 2 Tentacle Sync units, that were 'jammed' to a
# Zoom F8. The main idea is to sync the final export.mov video with a BWF 8
track audio file generated on
# a Zoom F8, via the ffmpeg command to generate the export.mov file.
# This is possible by using the BWF timecode metadata, and ltc on ch1 of
each video.
# ch2 contains a scratch track.
# A fixed duration such as 20 seconds is set, indicating a camera
# transition. Videos are mov at 1920x1080 with ltc audio at 48KHZ / 16 bits
# usage:
# /home/myuser/input> ls
# f8.wav output parseLTC.sh q81.mov q82.mov
# /home/myuser/input> cd output/
# /home/myuser/input/output> sh ../parseLTC.sh
# clear any files from previous processing
rm -f *.txt
rm -f *cut*
rm -f *.csv
# read LTC from wav files, first camera is q81 and the second camera is q82
ffmpeg -y -v error -nostdin -i ../q81.mov -vn -c:a pcm_s16le -ar 48000 -ac
2 q81.wav
ffmpeg -y -v error -nostdin -i ../q82.mov -vn -c:a pcm_s16le -ar 48000 -ac
2 q82.wav
ltcdump -f 29.97 q81.wav 2> /dev/null > q81.ltc.txt
ltcdump -f 29.97 q82.wav 2> /dev/null > q82.ltc.txt
# some errors I found while playing option bingo with ffmpeg
validate() {
lastTimecodeInSegment=0
lastTimecodeInSegment=`tail -n 1 ltc.txt | awk '{ print $2 }' | sed
's/\(.*\):/\1./' `
# can sometimes receive unparsable dates
if [[ ${#lastTimecodeInSegment} -ne 11 ]]; then
lastTimecodeInSegment=0
printf "\nError: timecode number of chars is not correct: "
printf " %s" "${#lastTimecodeInSegment}"
return
fi
# can receive invalid time such as: 00000680 00:13:61:10 |
22 1609
if ! date -d "$lastTimecodeInSegment" >/dev/null 2>&1; then
printf "\nError: invalid date format: "
printf " %s" "$lastTimecodeInSegment"
return
fi
epoch_secs_ltc_to_process=$(date +%s --date="$timecode_date
${lastTimecodeInSegment}")
ltc_to_process_formatted="$timecode_date ${lastTimecodeInSegment}"
printf "\nltc_to_process_formatted: $ltc_to_process_formatted"
if [[ $epoch_secs_ltc_to_process -ne 0 && $epoch_secs_stop_time -ne 0
&& $epoch_secs_ltc_to_process -ge $epoch_secs_stop_time ]]; then
found_stop_time=1
printf "\nError: timecode found:"
printf " %s" "${ltc_to_process_formatted}"
printf "\nis after or equal to stop time: "
printf " %s" "${stop_time_formatted}"
return
fi
startingSamplePositionInSegment=0
startingSamplePositionInSegment=`tail -n 1 ltc.txt | awk '{ print $4 }'
| sed 's/\(.*\):/\1./' `
endingSamplePositionInSegment=0
endingSamplePositionInSegment=`tail -n 1 ltc.txt | awk '{ print $5 }' |
sed 's/\(.*\):/\1./' `
printf "\nltc seems valid"
isLTCDumpOutputValid=1
}
# EDIT STARTS HERE
# define timecode start time, just add date
timecode_date=2018-06-01
# skip segments before this timecode
epoch_secs_start_time=$(date +%s --date="$timecode_date 00:05:05")
start_timecode=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_start_time seconds" | cut -b1-11 | sed 's/\(.*\):/\1./' `
start_time_formatted="$timecode_date $start_timecode"
# define timecode stop time
epoch_secs_stop_time=$(date +%s --date="$timecode_date 00:08:03")
# EDIT ENDS HERE
stop_timecode=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_stop_time seconds" | cut -b1-11 | sed 's/\(.*\):/\1./' `
stop_time_formatted="$timecode_date ${stop_timecode}"
timeDivisionInSeconds=20
let epoch_secs_timecode_to_find=$((epoch_secs_start_time +
timeDivisionInSeconds))
find_timecode=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_timecode_to_find seconds" | cut -b1-11 | sed 's/\(.*\):/\1./'
`
find_timecode_formatted="$timecode_date ${find_timecode}"
# for example, in seconds: f8_start_timecode=294.027375
f8_start_timecode=`sndfile-info -b ../f8.wav | grep "Time ref" | awk '{
print $5 }' | cut -c 2-`
f8_start_timecode_formatted=`date -u -d @${f8_start_timecode} +"%T"`
epoch_secs_f8_start_time=$(date +%s --date="$timecode_date
$f8_start_timecode_formatted")
let epoch_secs_f8_trim_time=$epoch_secs_start_time-$epoch_secs_f8_start_time
printf "\nfound epoch_secs_f8_trim_time: $epoch_secs_f8_trim_time \n"
ffmpeg -y -v error -nostdin -ss $epoch_secs_f8_trim_time -i ../f8.wav
f8t.wav
let
epoch_secs_next_timecode_to_find=$epoch_secs_timecode_to_find+$timeDivisionInSeconds
next_timecode_to_find=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_next_timecode_to_find seconds" | cut -b1-11 | sed
's/\(.*\):/\1./' `
next_timecode_to_find_formatted="$timecode_date ${next_timecode_to_find}"
magicNumberOffset="0.017"
q81HasLTCStartTime=0
hasRejectBeforeTime=0
q81TimeCodeInProgress=0
q82TimeCodeInProgress=0
q81PositionTimeStart=''
q81PositionTimeStop=''
q82PositionTimeStart=''
q82PositionTimeStop=''
fourtyEightKHZ=48000
# drop frames == 1 , non-drop frames == 0. ltcdump uses different timestamp
formats for each
# Zoon Q8 uses drop frames and doesn't seem to support non-drop frames
dropFrames=1
while read -r lineq81
do
ltc_line=''
ltc_line="$lineq81"
hasLTC=0
hasLTC=`echo $ltc_line | grep -v '#' | grep ':' | wc -w`
if [[ $hasLTC -eq 0 ]]; then
printf "\ncannot parse timecode on line: $ltc_line \n"
continue
fi
rm -f ltc.txt
# find timecode in format 'HH:MM:SS.00' with frames as last 2 digits
used for .00 time.
# disallow occasional '.' in the form 00:00:00.0 instead of 00:00:00:0
(non-drop frames only)
# disallow negative values
# disallow 0000000f 00:12:25:45
if [[ $dropFrames -eq 0 ]]; then
echo $ltc_line | grep -v '#' | grep 00000000 | grep -v '\.' | grep
-v '-' > ltc.txt
else
echo $ltc_line | grep -v '#' | grep 00000000 | grep -v '-' | sed
's/\(.*\)\./\1:/' > ltc.txt
fi
validate
if [[ $found_stop_time -eq 1 ]]; then
printf "\nq81 video is after stop time, executing break after
writing last csv entry\n"
if [[ $q81PositionTimeStart != '' && $q81PositionTimeStop != '' ]];
then
printf "\nExecuting: echo
$q81PositionTimeStart","$q81PositionTimeStop >> q81_out.csv"
echo $q81PositionTimeStart","$q81PositionTimeStop >>
q81_out.csv
fi
break
fi
if [[ $q81HasLTCStartTime -eq 0 && $isLTCDumpOutputValid -eq 0 ]]; then
printf "\non q81HasLTCStartTime=0 and isLTCDumpOutputValid=0 ...
skipping"
continue
fi
if [[ $q81HasLTCStartTime -eq 0 && $isLTCDumpOutputValid -eq 1 ]]; then
dateDiff=0
dateDiff=`date -d "$ltc_to_process_formatted $(date -d
"$start_time_formatted" +%s.%N) seconds ago" +%s.%N`
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "0" "$dateDiff"
then
q81HasLTCStartTime=1
printf "\non dateDiff $dateDiff , ltc_to_process_formatted
$ltc_to_process_formatted is later than or equal to start_time_formatted
$start_time_formatted"
printf "\nproceeding ...\n"
samplePositionOffsetQ81=0
samplePositionOffsetQ81=$(awk "BEGIN {printf
\"%.6f\",${startingSamplePositionInSegment}/${fourtyEightKHZ}}")
q81PositionTimeStart=$samplePositionOffsetQ81
else
printf "\non dateDiff $dateDiff , ltc_to_process_formatted
$ltc_to_process_formatted is before start_time_formatted
$start_time_formatted"
printf "\nskipping ...\n"
continue
fi
fi
if [[ $isLTCDumpOutputValid -eq 0 && $q81TimeCodeInProgress -eq 1 ]];
then
printf "\nq81 video is the default and is in progress, occasional
bad timecode values are ok"
continue
fi
if [[ $isLTCDumpOutputValid -eq 0 && $q81TimeCodeInProgress -eq 0 ]];
then
printf "\nskipping , ltc is invalid and q81TimeCodeInProgress is
false: "
continue
fi
if [[ $hasRejectBeforeTime -eq 1 ]]; then
dateDiff=0
dateDiff=`date -d "$ltc_to_process_formatted $(date -d
"$rejectBeforeTime_formatted" +%s.%N) seconds ago" +%s.%N`
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "$dateDiff" "0"
then
printf "\nexecuting continue , dateDiff $dateDiff ,
ltc_to_process_formatted $ltc_to_process_formatted is before or equal to
rejectBeforeTime_formatted $rejectBeforeTime_formatted \n"
continue
else
printf "\nproceeding , dateDiff $dateDiff ,
ltc_to_process_formatted $ltc_to_process_formatted is after
$rejectBeforeTime_formatted \n"
fi
fi
dateDiff=0
dateDiff=`date -d "$ltc_to_process_formatted $(date -d
"$find_timecode_formatted" +%s.%N) seconds ago" +%s.%N`
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "0" "$dateDiff"
then
printf "\non dateDiff $dateDiff , ltc_to_process_formatted
$ltc_to_process_formatted is later than or equal to find_timecode_formatted
$find_timecode_formatted \n"
else
printf "\non dateDiff $dateDiff , ltc_to_process_formatted
$ltc_to_process_formatted is less than find_timecode_formatted
$find_timecode_formatted \n"
printf "\nproceeding ..."
if [[ $q81TimeCodeInProgress -eq 0 ]]; then
q81PositionTimeStart=0
q81PositionTimeStart=$(awk "BEGIN {printf
\"%.6f\",${startingSamplePositionInSegment}/${fourtyEightKHZ}}")
else
# will reset on each loop
q81PositionTimeStop=0
q81PositionTimeStop=$(awk "BEGIN {printf
\"%.6f\",${endingSamplePositionInSegment}/${fourtyEightKHZ}}")
fi
q81TimeCodeInProgress=1
continue
fi
printf "\nfound find_timecode: "
printf " %s" "${find_timecode}"
printf "\nwill now process the q82 files looking for timecode later
than or equal to: "
printf " %s" "${find_timecode_formatted}"
printf "\nand timecode before or equal to: "
printf " %s" "${next_timecode_to_find_formatted}"
if [[ $q81PositionTimeStart != '' && $q81PositionTimeStop != '' ]]; then
printf "\nExecuting: echo $q81PositionTimeStop $magicNumberOffset |
awk "{printf "%.6g", $1-$2}"" >> debug.txt
q81PositionTimeStop=`echo $q81PositionTimeStop $magicNumberOffset |
awk '{printf "%.6g", $1-$2}'`
printf "\nExecuting: echo
$q81PositionTimeStart","$q81PositionTimeStop >> q81_out.csv"
echo $q81PositionTimeStart","$q81PositionTimeStop >> q81_out.csv
duration=0
printf "\nExecuting: echo $q81PositionTimeStop
$q81PositionTimeStart | awk "{printf "%.6g", $1-$2}""
duration=`echo $q81PositionTimeStop $q81PositionTimeStart | awk
'{printf "%.6g", $1-$2}'`
echo $q81PositionTimeStart","$q81PositionTimeStop" # q81
transition, endingSamplePositionInSegment: $endingSamplePositionInSegment
to q82 starting timecode ${find_timecode} , total duration of calculated
cut: $duration" >> debug.txt
else
printf "\nloop completed without valid timecode, executing break"
break
fi
q81PositionTimeStop=0
q81TimeCodeInProgress=0
samplePositionOffsetQ82=0
q81PositionTimeStart=''
q81PositionTimeStop=''
while read -r lineq82
do
ltc_line=''
ltc_line="$lineq82"
hasLTC=0
hasLTC=`echo $ltc_line | grep -v '#' | grep ':' | wc -w`
if [[ $hasLTC -eq 0 ]]; then
printf "\ncannot parse timecode on line: $ltc_line \n"
continue
fi
rm -f ltc.txt
# find timecode in format 'HH:MM:SS.00' with frames as last 2
digits used for .00 time.
# disallow occasional '.' in the form 00:00:00.0 instead of
00:00:00:0 (non-drop frames only)
# disallow negative values
# disallow 0000000f 00:12:25:45
if [[ $dropFrames -eq 0 ]]; then
echo $ltc_line | grep -v '#' | grep 00000000 | grep -v '\.' |
grep -v '-' > ltc.txt
else
echo $ltc_line | grep -v '#' | grep 00000000 | grep -v '-' |
sed 's/\(.*\)\./\1:/' > ltc.txt
fi
validate
if [[ $found_stop_time -eq 1 ]]; then
printf "\nq82 video is after stop time, executing break after
writing last csv entry\n"
break
fi
if [[ $isLTCDumpOutputValid -eq 0 && $q82TimeCodeInProgress -eq 1
]]; then
printf "\nfound LTC error while q82TimeCodeInProgress=1,
skipping"
continue
fi
if [[ $isLTCDumpOutputValid -eq 0 ]]; then
printf "\nFound ltc error outside time range while processing
q82 , skipping"
continue
fi
dateDiff=0
dateDiff=`date -d "$ltc_to_process_formatted $(date -d
"$find_timecode_formatted" +%s.%N) seconds ago" +%s.%N`
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "0" "$dateDiff"
then
printf "\nproceeding , dateDiff $dateDiff ,
ltc_to_process_formatted $ltc_to_process_formatted is later or equal to
find_timecode_formatted $find_timecode_formatted"
else
printf "\nskipping q82 timecode on dateDiff $dateDiff ,
ltc_to_process_formatted $ltc_to_process_formatted is before
find_timecode_formatted $find_timecode_formatted"
continue
fi
dateDiff=0
dateDiff=`date -d "$ltc_to_process_formatted $(date -d
"$next_timecode_to_find_formatted" +%s.%N) seconds ago" +%s.%N`
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "$dateDiff" "0"
then
printf "\nproceeding on dateDiff $dateDiff ,
$ltc_to_process_formatted is before or equal to
$next_timecode_to_find_formatted"
else
printf "\nexecuting break , dateDiff $dateDiff ,
ltc_to_process_formatted $ltc_to_process_formatted is greater than
next_timecode_to_find $next_timecode_to_find"
break
fi
if [[ $q82TimeCodeInProgress -eq 0 ]]; then
samplePositionOffsetQ82=0
samplePositionOffsetQ82=$(awk "BEGIN {printf
\"%.6f\",${startingSamplePositionInSegment}/${fourtyEightKHZ}}")
q82PositionTimeStart=$samplePositionOffsetQ82
else
# will reset on each loop
samplePositionOffsetQ82=0
samplePositionOffsetQ82=$(awk "BEGIN {printf
\"%.6f\",${endingSamplePositionInSegment}/${fourtyEightKHZ}}")
q82PositionTimeStop=$samplePositionOffsetQ82
fi
q82TimeCodeInProgress=1
printf "\nfound ltc in range. loop ended"
done < q82.ltc.txt
if [[ $q82PositionTimeStart != '' && $q82PositionTimeStop != '' ]]; then
printf "\nExecuting: echo $q82PositionTimeStop $magicNumberOffset |
awk "{printf "%.6g", $1-$2}"" >> debug.txt
q82PositionTimeStop=`echo $q82PositionTimeStop $magicNumberOffset |
awk '{printf "%.6g", $1-$2}'`
printf "\nExecuting: echo
$q82PositionTimeStart","$q82PositionTimeStop >> q82_out.csv"
echo $q82PositionTimeStart","$q82PositionTimeStop >> q82_out.csv
duration=0
printf "\nExecuting: echo $q82PositionTimeStop $q82PositionTimeStart
| awk "{printf "%.6g", $1-$2}""
duration=`echo $q82PositionTimeStop $q82PositionTimeStart | awk
'{printf "%.6g", $1-$2}'`
echo $q82PositionTimeStart","$q82PositionTimeStop" # q82 transition,
endingSamplePositionInSegment: $endingSamplePositionInSegment to q81
starting timecode $epoch_secs_timecode_to_find+$increment , total duration
of calculated cut: $duration" >> debug.txt
else
printf "\nloop completed without valid timecode, executing break"
break
fi
q82TimeCodeInProgress=0
q82PositionTimeStart=''
q82PositionTimeStop=''
# reset everything for next loop
let epoch_secs_rejectBeforeTime=$epoch_secs_next_timecode_to_find
reject_timecode=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_rejectBeforeTime seconds" | cut -b1-11 | sed 's/\(.*\):/\1./'
`
rejectBeforeTime_formatted="$timecode_date ${reject_timecode}"
hasRejectBeforeTime=1
increment=$(( $timeDivisionInSeconds * 2 ))
let epoch_secs_timecode_to_find=$epoch_secs_timecode_to_find+$increment
find_timecode=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00 +0000
$epoch_secs_timecode_to_find seconds" | cut -b1-11 | sed 's/\(.*\):/\1./' `
find_timecode_formatted="$timecode_date ${find_timecode}"
let
epoch_secs_next_timecode_to_find=$epoch_secs_next_timecode_to_find+$increment
next_timecode_to_find=`date +%H:%M:%S:%N --date="Jan 1, 1970 00:00:00
+0000 $epoch_secs_next_timecode_to_find seconds" | cut -b1-11 | sed
's/\(.*\):/\1./' `
next_timecode_to_find_formatted="$timecode_date ${next_timecode_to_find}"
printf "\nloop completed, timecode_to_find has been reset: "
printf " %s" "${find_timecode_formatted}"
printf "\nnext_timecode_to_find_formatted has been reset: "
printf " %s" "${next_timecode_to_find_formatted}"
printf "\nrejectBeforeTime_formatted has been reset: "
printf " %s" "${rejectBeforeTime_formatted}"
done < q81.ltc.txt
xx=0
while IFS=, read -r col1 col2
do
rm -f q81.log
if [[ $xx -eq 0 ]]; then
rm -f q81t.log
# debug, trim source file so it matches the timecode of the
generated files
printf "\nExecuting first pass: ffmpeg -y -v error -nostdin -i
../q81.mov -pass 1 -passlogfile q81t.log -ss $col1 -c:a pcm_s16le -c:v
libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q81t.mov" >>
debug.txt
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 1 -passlogfile
q81t.log -ss $col1 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q81t.mov
printf "\nExecuting second pass: ffmpeg -y -v error -nostdin -i
../q81.mov -pass 2 -passlogfile q81t.log -ss $col1 -c:a pcm_s16le -c:v
libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q81t.mov" >>
debug.txt
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 2 -passlogfile
q81t.log -ss $col1 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q81t.mov
# debug
printf "\nExecuting: ffmpeg -y -v error -nostdin -i q81t.mov -vn
-acodec pcm_s16le -ar 48000 -ac 2 q81t.wav"
ffmpeg -y -v error -nostdin -i q81t.mov -vn -acodec pcm_s16le -ar
48000 -ac 2 q81t.wav
fi
printf "\nExecuting first pass: ffmpeg -y -v error -nostdin -i
../q81.mov -pass 1 -passlogfile q81.log -ss $col1 -to $col2 -c:a pcm_s16le
-c:v libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q81_cut_"$xx".mov" >> debug.txt
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 1 -passlogfile q81.log
-ss $col1 -to $col2 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q81_cut_"$xx".mov
printf "\nExecuting second pass: ffmpeg -y -v error -nostdin -i
../q81.mov -pass 2 -passlogfile q81.log -ss $col1 -to $col2 -c:a pcm_s16le
-c:v libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q81_cut_"$xx".mov" >> debug.txt
ffmpeg -y -v error -nostdin -i ../q81.mov -pass 2 -passlogfile q81.log
-ss $col1 -to $col2 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q81_cut_"$xx".mov
# debug
printf "\nExecuting: ffmpeg -y -v error -nostdin -i q81_cut_"$xx".mov
-vn -acodec pcm_s16le -ar 48000 -ac 2 "q81_cut_"$xx".mov.wav" "
ffmpeg -y -v error -nostdin -i q81_cut_"$xx".mov -vn -acodec pcm_s16le
-ar 48000 -ac 2 "q81_cut_"$xx".mov.wav"
xx=$((xx + 1))
done < q81_out.csv
yy=0
while IFS=, read -r col1 col2
do
rm -f q82.log
if [[ $yy -eq 0 ]]; then
rm -f q82t.log
# debug, trim source file so it matches the timecode of the
generated files
printf "\nExecuting first pass: ffmpeg -y -v error -nostdin -i
../q82.mov -pass 1 -passlogfile q82t.log -ss $col1 -c:a pcm_s16le -c:v
libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q82t.mov" >>
debug.txt
ffmpeg -y -v error -nostdin -i ../q82.mov -pass 1 -passlogfile
q82t.log -ss $col1 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q82t.mov
printf "\nExecuting second pass: ffmpeg -y -v error -nostdin -i
../q82.mov -pass 2 -passlogfile q82t.log -ss $col1 -c:a pcm_s16le -c:v
libx264 -profile:v baseline -level 3.0 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -refs 1 -r
30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k
-muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q82t.mov" >>
debug.txt
ffmpeg -y -v error -nostdin -i ../q82.mov -pass 2 -passlogfile
q82t.log -ss $col1 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -x264-params "keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1"
-refs 1 -r 30000/1001 -s 1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a
48k -muxrate 24M -sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 q82t.mov
# debug
printf "\nExecuting: ffmpeg -y -v error -nostdin -i q82t.mov -vn
-acodec pcm_s16le -ar 48000 -ac 2 q82t.wav"
ffmpeg -y -v error -nostdin -i q82t.mov -vn -acodec pcm_s16le -ar
48000 -ac 2 q82t.wav
fi
printf "\nffmpeg -y -v error -nostdin -i ../q82.mov -pass 1
-passlogfile q82.log -ss $col1 -to $col2 -c:a pcm_s16le -c:v libx264
-profile:v baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q82_cut_"$yy".mov" >> debug.txt
ffmpeg -y -v error -nostdin -i ../q82.mov -pass 1 -passlogfile q82.log
-ss $col1 -to $col2 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q82_cut_"$yy".mov
printf "\nExecuting second pass: ffmpeg -y -v error -nostdin -i
../q82.mov -pass 2 -passlogfile q82.log -ss $col1 -to $col2 -c:a pcm_s16le
-c:v libx264 -profile:v baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q82_cut_"$yy".mov" >> debug.txt
ffmpeg -y -v error -nostdin -i ../q82.mov -pass 2 -passlogfile q82.log
-ss $col1 -to $col2 -c:a pcm_s16le -c:v libx264 -profile:v baseline -level
3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18
q82_cut_"$yy".mov
printf "\nExecuting: ffmpeg -y -v error -nostdin -i q82_cut_"$yy".mov
-vn -acodec pcm_s16le -ar 48000 -ac 2 "q82_cut_"$yy".mov.wav" "
# debug
ffmpeg -y -v error -nostdin -i q82_cut_"$yy".mov -vn -acodec pcm_s16le
-ar 48000 -ac 2 "q82_cut_"$yy".mov.wav"
yy=$((yy + 1))
done < q82_out.csv
zz=0
if [[ $xx -eq $yy ]]; then
zz=$xx
elif [[ $xx -gt $yy ]]; then
zz=$xx
else
zz=$yy
fi
for (( c=0; c<$zz; c++ ))
do
printf "\nExecuting: ls *cut_"$c"*.mov | sort | sed 's/^/file /' >>
cuts.txt"
ls *cut_"$c"*.mov | sort | sed 's/^/file /' >> cuts.txt
done
printf "\nExecuting first pass: ffmpeg -y -v error -nostdin -f concat
-fflags +genpts -safe 0 -i cuts.txt -pass 1 -passlogfile cuts.log -c:a
pcm_s16le -c:v libx264 -profile:v baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 cuts.mov" >>
debug.txt
# use -x264-params, -refs 1, two-pass encoding for a constant bit rate 24M
etc
# to match the source videos , avoid variable GOP size in the final video
ffmpeg -y -v error -nostdin -f concat -fflags +genpts -safe 0 -i cuts.txt
-pass 1 -passlogfile cuts.log -c:a pcm_s16le -c:v libx264 -profile:v
baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 cuts.mov
printf "\nExecuting second pass: ffmpeg -y -v error -nostdin -f concat
-fflags +genpts -safe 0 -i cuts.txt -pass 2 -passlogfile cuts.log -c:a
pcm_s16le -c:v libx264 -profile:v baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 cuts.mov" >>
debug.txt
ffmpeg -y -v error -nostdin -f concat -fflags +genpts -safe 0 -i cuts.txt
-pass 2 -passlogfile cuts.log -c:a pcm_s16le -c:v libx264 -profile:v
baseline -level 3.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1920x1080 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 cuts.mov
# debug
ffmpeg -y -v error -nostdin -i cuts.mov -vn -c:a pcm_s16le -ar 48000 -ac 2
cuts.wav
printf "\nExecuting first pass: ffmpeg -v error -i cuts.mov -i f8t.wav
-pass 1 -passlogfile export.log -c:a pcm_s16le -c:v libx264 -profile:v main
-level 4.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1280x720 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 -metadata
comment="Created with parseLTC.sh" -map 0:0 -map 1:0 -y export.mov" >>
debug.txt
ffmpeg -v error -i cuts.mov -i f8t.wav -pass 1 -passlogfile export.log -c:a
pcm_s16le -c:v libx264 -profile:v main -level 4.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1280x720 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 -metadata
comment="Created with parseLTC.sh" -map 0:0 -map 1:0 -y export.mov
printf "\nExecuting second pass: ffmpeg -v error -i cuts.mov -i f8t.wav
-pass 2 -passlogfile export.log -c:a pcm_s16le -c:v libx264 -profile:v main
-level 4.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1280x720 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 -metadata
comment="Created with parseLTC.sh" -map 0:0 -map 1:0 -y export.mov" >>
debug.txt
ffmpeg -v error -i cuts.mov -i f8t.wav -pass 2 -passlogfile export.log -c:a
pcm_s16le -c:v libx264 -profile:v main -level 4.0 -refs 1 -x264-params
"keyint=8:b-pyramid=0:no-scenecut:nal-hrd=cbr:force-cfr=1" -r 30000/1001 -s
1280x720 -b:v: 24M -bufsize 24M -maxrate 24M -b:a 48k -muxrate 24M
-sample_fmt s16 -ac 2 -ar 48000 -af
"aresample=async=1:min_hard_comp=0.000100:first_pts=0" -crf 18 -metadata
comment="Created with parseLTC.sh" -map 0:0 -map 1:0 -y export.mov
exit
More information about the ffmpeg-user
mailing list