[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