Skip to content
Permalink
main
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
executable file 644 lines (604 sloc) 30.2 KB
#!/bin/bash
# dependencies: dvrescue, ffmpeg, xmlstarlet
_usage(){
cat <<EOF
dvpackager
Rewrap a DV stream. This script is part of the dvrescue project.
Usage:
dvpackager [options] file.dv
Options:
By default, dvpackager will split the output files so that each time
significant technical characteristics of the dv stream change (such as aspect
ratio, frame rate, audio channel count, or audio sample rate) a new output file
will be written. The following flags adjust the way dvpackager will split the
output.
-f (forces dvpackager to ignore changes in significant technical
characteristics of the dv stream when splitting the output)
-s (split the output file at recording start markers)
-d (split the output file at non-consecutive recording timestamps)
-t (split the output file at non-consecutive timecode values)
-o <dir> (provide a custom output directory)
-e <ext> (specify the extension of the container to use. Tested with:
mkv, mov, dv. Defaults to mkv. Note that using the 'dv' option
shall simply extract the dv from the file while using the
selected options to split the output.)
-l <code>(specify the language code to use for the audio tracks.
If two languages are provided with a comma delimiter such as
'eng,deu' then the first would be used for the first audio
track and the second for the second audio track, if it exists.)
-L <code>(specify the language code to use for the caption tracks (if
any).
-S (enables technical subtitle track to show timecode,
recording time, and dv error data and a caption track if
there are captions to represent in the source DV)
-n (do not repackage, simply generate a dvrescue xml if one doesn't
already exist, and report on what the output files would be)
-v (shows ffmpeg stderr output, otherwise this is hidden)
-F <path> (provide a custom ffmpeg path)
-M <path> (provide a custom mediainfo path)
-D <path> (provide a custom dvrescue path)
-X <path> (provide a custom xmlstarlet path)
-x <path> (provide a custom path to a DVRescue XML that corresponds to the
input file)
-V <path> (provide a custom path to a DVRescue technical subtitle file that
corresponds to the input file, ignored if -S is not set)
-C <path> (provide a custom path to a DVRescue SCC that corresponds to the
input file, ignored if -S is not set)
For example, the following command:
dvpackager -s INPUT.dv
will read INPUT.dv and rewrap those dv frames into an output file while making
one new output file whenever there is a change in significant technical
characteristics or frames with recording-start flags.
dvpackager also has an 'unpackage' mode
-u (export the dv stream from each provided file into a single dv stream)
For example, the following command:
dvpackager -u INPUT_1.mkv INPUT_2.mkv INPUT_3.mkv
will create one dv stream that contains all the DV of each input file.
EOF
}
if [ "${#}" = 0 ] ; then
_usage
exit 0
fi
_maketemp(){
mktemp -q -t "$(basename "${0}").XXXXXX"
if [ "${?}" -ne 0 ]; then
echo "${0}: Can't create temp file, exiting..."
exit 1
fi
}
_get_ranges(){
FRAME_MATCH="d:dvrescue/d:media/d:frames"
if [[ "${FORCE_FRAMES}" = "Y" ]] ; then
FRAME_MATCH="${FRAME_MATCH}[1]"
RANGE_MATCH="d:frame[1]"
RELATIVE_FRAMES="../../d:frames[last()]"
elif [[ -n "${1}" ]] ; then
RANGE_MATCH="${1}"
RELATIVE_FRAMES="parent::d:frames"
else
RANGE_MATCH="${MATCH_FRAMES}"
RELATIVE_FRAMES="parent::d:frames"
fi
RANGE_MATCH_FS="$(echo "${RANGE_MATCH}" | cut -s -d "|" -f2- | sed 's|d:|following-sibling::d:|g')"
"${XMLSTARLET_PATH}" sel -N "d=https://mediaarea.net/dvrescue" -t -m "${FRAME_MATCH}" -m "${RANGE_MATCH}" \
--var "p=number((${RANGE_MATCH_FS:-x})[1]/@n)-1" \
-v "@pts" -o "|" \
--if "\$p" -v "(${RANGE_MATCH_FS:-x})[1]/@pts" --else -v "${RELATIVE_FRAMES}/@end_pts" -b -o "|" \
-v "@n" -o "|" \
--if "\$p" -v "\$p" --else -v "${RELATIVE_FRAMES}/d:frame[last()]/@n" -b -o "|" \
-v "@tc" -o "|" \
-v "@pos" -o "|" \
-v "@rdt" -o "|" \
-v "${RELATIVE_FRAMES}/@size" -o "|" \
-v "${RELATIVE_FRAMES}/@video_rate" -o "|" \
-v "${RELATIVE_FRAMES}/@chroma_subsampling" -o "|" \
-v "${RELATIVE_FRAMES}/@aspect_ratio" -o "|" \
-v "${RELATIVE_FRAMES}/@audio_rate" -o "|" \
-v "${RELATIVE_FRAMES}/@channels" -o "|" \
-v "@rec_start" -o "|" \
-v "@rdt_nc" -o "|" \
-v "@tc_nc" -o "|" \
-v "count(preceding-sibling::d:frame)" -o "|" \
--if "\$p" \
-v "count(//d:frame[@n=\$p]/@no_pack|//d:frame[@n=\$p]/@no_pack_aud|//d:frame[@n=\$p]/@no_sourceorcontrol_aud)" \
--else \
-v "count(${RELATIVE_FRAMES}/d:frame[last()]/@no_pack|${RELATIVE_FRAMES}/d:frame[last()]/@no_pack_aud|${RELATIVE_FRAMES}/d:frame[last()]/@no_sourceorcontrol_aud)" \
--break \
-n "${DVRESCUE_XML}"
}
_ranges_2_table(){
RANGES="${1}"
echo
echo "# St='Flagged Start of a recording', ncTC='non-continuous timecode value', ncR='non-continuous recording timestamp value', 1st='first frame is sequence'"
echo "| PTS Range | Duration | Frame Range | Timecode | Recording Timestamp | Size | Frame Rate | DAR | ChSub | Audio | St | ncTC | ncR | 1st |"
echo "${RANGES}" | while read RANGE_LINE ; do
PTS="$(echo "${RANGE_LINE}" | cut -d "|" -f 1)"
END_PTS="$(echo "${RANGE_LINE}" | cut -d "|" -f 2)"
COUNT_FRAMES="$(echo "${RANGE_LINE}" | cut -d "|" -f 17)"
if [[ "${COUNT_FRAMES}" = "0" ]] ; then
IS_FIRST="Y"
else
IS_FIRST=""
fi
DURATION="$(_duration_from_pts_range "${PTS}" "${END_PTS}")"
echo "${RANGE_LINE}|${DURATION}|${IS_FIRST}" | awk -F "|" '{PTS=$1; END_PTS=$2; DURATION=$19; FRAME_START=$3; FRAME_END=$4; TC=$5; PKT_POS=$6; RDT=$7; SIZE=$8; VIDEO_RATE=$9; CH_SUB=$10; AR=$11; AUDIO_RATE=$12; CH=$13; REC_ST=$14; RDT_NC=$15; TC_NC=$16; IS_FIRST=$20}\
{printf "| %15s - %15s | %8.3f | %6i - %6i | %11s | %19s | %9s | %10s | %5s | %5s | %1sch %5i | %2s | %4s | %3s | %3s |\n", \
PTS, END_PTS, DURATION, FRAME_START, FRAME_END, TC, RDT, SIZE, VIDEO_RATE, AR, CH_SUB, CH, AUDIO_RATE, REC_ST, TC_NC, RDT_NC, IS_FIRST }'
done
echo
}
_convert_hhmmssmmm2s(){
TS="${1}"
H="$(echo "${TS}" | cut -d ":" -f1)"
M="$(echo "${TS}" | cut -d ":" -f2)"
S="$(echo "${TS}" | cut -d ":" -f3)"
echo "${H}*60*60+${M}*60+${S}" | bc -l
}
_duration_from_pts_range(){
START="${1}"
END="${2}"
START_SEC="$(_convert_hhmmssmmm2s "${START}")"
END_SEC="$(_convert_hhmmssmmm2s "${END}")"
echo "${END_SEC}-${START_SEC}" | bc -l
}
_count_dv_frames(){
if [[ -f "${MEDIAINFO_PATH}" ]] ; then
INPUT_FILE="${1}"
"${MEDIAINFO_PATH}" -f --inform="Video;%FrameCount%" "${INPUT_FILE}" | head -n 1
else
_report -w "Warning: Missing mediainfo, dvpackager can still package but some verification checks can not run."
fi
}
_make_chapter_metadata_file(){
CHAP_RANGE_START="$(_convert_hhmmssmmm2s "${1}" | awk '{printf "%i", $1 * 100000}')"
CHAP_RANGE_END="$(_convert_hhmmssmmm2s "${2}" | awk '{printf "%i", $1 * 100000}')"
echo ";FFMETADATA1"
_get_ranges "d:frame[1]|d:frame[@rec_start='1']|d:frame[@rdt_nc='1']|d:frame[@tc_nc='1']" | \
while IFS="|" read PTS END_PTS FRAME_START FRAME_END TC PKT_POS RDT SIZE VIDEO_RATE CH_SUB AR AUDIO_RATE CH REC_ST RDT_NC TC_NC ; do
START_CHAPTER="$(_convert_hhmmssmmm2s "${PTS}" | awk '{printf "%i", $1 * 100000}')"
END_CHAPTER="$(_convert_hhmmssmmm2s "${END_PTS}" | awk '{printf "%i", $1 * 100000}')"
START_CHAPTER_OFFSET="$((START_CHAPTER-CHAP_RANGE_START))"
END_CHAPTER_OFFSET="$((END_CHAPTER-CHAP_RANGE_START))"
if [[ "${START_CHAPTER}" -ge "${CHAP_RANGE_START}" ]] && [[ "${END_CHAPTER}" -le "${CHAP_RANGE_END}" ]] ; then
echo "[CHAPTER]"
echo "TIMEBASE=1/100000"
echo "START=${START_CHAPTER_OFFSET}"
echo "END=${END_CHAPTER_OFFSET}"
echo "title=${TC} - ${RDT}"
fi
done
}
_get_iso8601(){
date +%FT%T
}
_report(){
local RED="$(tput setaf 1)" # Red - For Warnings
local GREEN="$(tput setaf 2)" # Green - For Declarations
local BLUE="$(tput setaf 4)" # Blue - For Questions
local MAGENTA="$(tput setaf 5)" # Magenta - For Verbose Statements
local NC="$(tput sgr0)" # No Color
local COLOR=""
local STARTMESSAGE=""
local ECHOOPT=""
local VERBOSE_CHECK=false
OPTIND=1
while getopts "vqdwstn" opt ; do
case "${opt}" in
v) VERBOSE_CHECK=true ;
COLOR="${MAGENTA}" ;
STARTMESSAGE+="#" ;; # only output the message if DV_VERBOSE=true
q) COLOR="${BLUE}" ;; # question mode, use color blue
d) COLOR="${GREEN}" ;; # declaration mode, use color green
w) COLOR="${RED}" ;; # warning mode, use color red
s) STARTMESSAGE+=([${SCRIPTNAME}] ) ;; # prepend scriptname to the message
t) STARTMESSAGE+=($(_get_iso8601) '- ' ) ;; # prepend timestamp to the message
n) ECHOOPT="-n" ;; # to avoid line breaks after echo
esac
done
shift "$((OPTIND-1))"
MESSAGE="${1}"
if ! "${VERBOSE_CHECK}" || ( "${DV_VERBOSE}" && "${VERBOSE_CHECK}" ) ; then
echo ${ECHOOPT} "${COLOR}${STARTMESSAGE[@]}${MESSAGE}${NC}"
fi
}
MATCH_FRAMES="d:frame[1]|"
if [[ -d "/usr/local/opt/ffmpegdecklink" ]] ; then
BREW_PREFIX="/usr/local/opt/ffmpegdecklink"
elif [[ -d "/home/linuxbrew/.linuxbrew/opt/ffmpegdecklink" ]] ; then
BREW_PREFIX="/home/linuxbrew/.linuxbrew/opt/ffmpegdecklink"
else
BREW_PREFIX="$(brew --prefix ffmpegdecklink 2>/dev/null)"
fi
if [[ -n "${BREW_PREFIX}" && -d "${BREW_PREFIX}/bin" ]] ; then
FFMPEG_PATH="${BREW_PREFIX}/bin/ffmpeg-dl"
else
FFMPEG_PATH="$(which ffmpeg)"
FFMPEG_DL="N"
fi
# verify that ffmpeg qualifies
FFversion="$("${FFMPEG_PATH}" -version)"
LAVFversion="$(echo "${FFversion}" | grep "libavformat" | head -n 1 | sed 's/[^0-9./]//g' | cut -d "/" -f 1)"
LAVFversion_maj="$(echo "${LAVFversion}" | cut -d. -f1)"
LAVFversion_min="$(echo "${LAVFversion}" | cut -d. -f2)"
LAVFversion_mic="$(echo "${LAVFversion}" | cut -d. -f3)"
if [[ "${FFMPEG_DL}" = "N" ]] && ! ( \
[[ "${LAVFversion_maj}" -gt 58 ]] || \
( [[ "${LAVFversion_maj}" -eq 58 ]] && [[ "${LAVFversion_min}" -gt 65 ]] ) || \
( [[ "${LAVFversion_maj}" -eq 58 ]] && [[ "${LAVFversion_min}" -eq 65 ]] && [[ "${LAVFversion_min}" -ge 101 ]] ) \
) ; then
_report -w "FFmpeg's libavformat version 58.65.101 or greater is recommend and ${FFMPEG_PATH} is running ${LAVFversion}."
_report -w "Please update ffmpeg or consider installing amiaopensource/amiaos/ffmpegdecklink."
_report -w "Using this current install of ffmpeg may result in out of sync audio."
fi
FFMPEG_VERBOSE=(-v quiet -stats)
DV_VERBOSE=false
REPORT_ONLY="N"
EXT="mov"
SUBS="N"
# command-line options to set media id and original variables
OPTIND=1
while getopts ":fsdto:e:l:L:SnvuhF:M:D:X:x:V:C:" opt ; do
case "${opt}" in
f) FORCE_FRAMES="Y" ;;
s) MATCH_FRAMES+="d:frame[@rec_start='1']|" ;;
d) MATCH_FRAMES+="d:frame[@rdt_nc='1']|" ;;
t) MATCH_FRAMES+="d:frame[@tc_nc='1']|" ;;
o) OUTPUTDIR="${OPTARG}" ;;
e) EXT="${OPTARG}" ;;
l) LANGUAGES="${OPTARG}" ;;
L) CAPTION_LANG="${OPTARG}" ;;
n) REPORT_ONLY="Y" ;;
S) SUBS="Y" ;;
v) unset FFMPEG_VERBOSE ; DV_VERBOSE=true ;;
u) UNPACKAGER="Y" ;;
F) FFMPEG_PATH="${OPTARG}" ;;
M) MEDIAINFO_PATH="${OPTARG}" ;;
D) DVRESCUE_PATH="${OPTARG}" ;;
X) XMLSTARLET_PATH="${OPTARG}" ;;
x) DVRESCUE_XML="${OPTARG}" ;;
V) DVRESCUE_TECHSUBS="${OPTARG}" ;;
C) DVRESCUE_SCC="${OPTARG}" ;;
h) _usage ; exit 0 ;;
:) echo "Option -${OPTARG} requires an argument" ; _usage ; exit 1 ;;
*) echo "bad option -${OPTARG}" ; _usage ; exit 1 ;;
esac
done
shift "$((OPTIND-1))"
DVFILE="${1}"
shift
if [[ "${UNPACKAGER}" != "Y" && "${@}" != "" ]] ; then
_report -w "Error: there are additional unused arguments (${*}). Please reference the DV file last."
_usage
exit 0
fi
MATCH_FRAMES="${MATCH_FRAMES%?}"
OPT_INPUT=(-y)
OPT_INPUT+=(-nostdin)
OPT_INPUT+=(-hide_banner)
OPT_OUTPUT+=(-c:v:0 copy)
if [[ -z "${MEDIAINFO_PATH}" ]] ; then
MEDIAINFO_PATH="$(which mediainfo)"
fi
if [[ ! -f "${MEDIAINFO_PATH}" ]] ; then
_report -d "The mediainfo command-line tool is not found, but is used to verify that the output is well synchronized."
_report -d "Please install a mediainfo CLI from https://mediaarea.net/en/MediaInfo/Download or provide a path to mediainfo with the -M argument."
fi
if [[ -z "${DVRESCUE_PATH}" ]] ; then
DVRESCUE_PATH="$(which dvrescue)"
fi
if [[ ! -f "${DVRESCUE_PATH}" ]] ; then
_report -w "The dvrescue command-line tool is not found."
exit 1
fi
if [[ -z "${XMLSTARLET_PATH}" ]] ; then
XMLSTARLET_PATH="$(which xmlstarlet)"
fi
if [[ ! -f "${XMLSTARLET_PATH}" ]] ; then
_report -w "The xmlstarlet command-line tool is not found."
exit 1
fi
LANG1="$(echo "${LANGUAGES}" | cut -d "," -f1 )"
LANG2="$(echo "${LANGUAGES}" | cut -d "," -f2 )"
if [[ -n "${LANG1}" ]] ; then
OPT_OUTPUT+=(-metadata:s:a:0 language="${LANG1}")
fi
if [[ -n "${LANG2}" ]] ; then
OPT_OUTPUT+=(-metadata:s:a:1 language="${LANG2}")
fi
if [[ ! -f "${FFMPEG_PATH}" ]] ; then
_report -w "Error: ffmpeg is needed but not found."
exit 1
elif [[ ! -x "${FFMPEG_PATH}" ]] ; then
_report -w "Error: The ffmpeg needed at ${FFMPEG_PATH} is not executable."
exit 1
fi
if [[ "${UNPACKAGER}" == "Y" ]] ; then
OUTPUTNAME="unpackaged_$(uuidgen).dv"
echo "Unpackaging mode. Unpackaging the input files into ${OUTPUTNAME}."
for i in "${@}" ; do
"${FFMPEG_PATH}" -nostdin "${FFMPEG_VERBOSE[@]}" -i "$i" -map 0:v:0 -c copy -f rawvideo - >> "${OUTPUTNAME}"
done
exit
fi
case "${EXT}" in
"mov")
FORMAT="mov"
EXTENSION="mov"
OPT_OUTPUT+=(-map 0:v:0 -map 0:a?)
OPT_OUTPUT+=(-c:a pcm_s16le)
;;
"mkv")
FORMAT="matroska"
EXTENSION="mkv"
OPT_OUTPUT+=(-map 0:v:0 -map 0:a?)
OPT_OUTPUT+=(-c:a pcm_s16le)
;;
"dv")
FORMAT="rawvideo"
EXTENSION="dv"
SUBS="N"
OPT_OUTPUT+=(-map 0:v:0)
;;
*)
_report -w "Error: ${EXT} is an invalid extension option."
_usage
exit 1
;;
esac
OPT_OUTPUT+=(-f "$FORMAT")
unset OUTPUTOPTS
BASENAME="$(basename "${DVFILE}")"
if [[ -d "${OUTPUTDIR}" ]] ; then
SIDECAR_DIR="${OUTPUTDIR}"
elif [[ -n "${OUTPUTDIR}" ]] ; then
_report -w "ERROR: ${OUTPUTDIR} is not a directory."
exit
else
SIDECAR_DIR="${DVFILE}_dvrescue"
fi
if [[ -z "${DVRESCUE_XML}" ]] ; then
DVRESCUE_XML="${SIDECAR_DIR}/${BASENAME}.dvrescue.xml"
if [[ ! -f "${DVRESCUE_XML}" ]] ; then
DVRESCUE_XML_TEMP="$(_maketemp)"
OUTPUTOPTS+=(--xml-output "${DVRESCUE_XML_TEMP}")
XML_TO_CHECK="${DVRESCUE_XML_TEMP}"
else
XML_TO_CHECK="${DVRESCUE_XML}"
fi
else
XML_TO_CHECK="${DVRESCUE_XML}"
fi
if [[ "${SUBS}" = "Y" ]]; then
if [[ -z "${DVRESCUE_TECHSUBS}" ]] ; then
DVRESCUE_TECHSUBS="${SIDECAR_DIR}/${BASENAME}.dvrescue.techsubs.vtt"
if [[ ! -f "${DVRESCUE_TECHSUBS}" ]] ; then
DVRESCUE_TECHSUBS_TEMP="$(_maketemp).vtt"
OUTPUTOPTS+=(--webvtt-output "${DVRESCUE_TECHSUBS_TEMP}")
fi
fi
if [[ -z "${DVRESCUE_SCC}" ]] ; then
DVRESCUE_SCC="${SIDECAR_DIR}/${BASENAME}.dvrescue.scc"
DVRESCUE_SCC_VTT="${SIDECAR_DIR}/${BASENAME}.dvrescue.scc.vtt"
if [[ ! -f "${DVRESCUE_SCC}" ]] ; then
DVRESCUE_SCC_TEMP="$(_maketemp).scc"
OUTPUTOPTS+=(--cc-format scc)
OUTPUTOPTS+=(--cc-output "${DVRESCUE_SCC_TEMP}")
fi
fi
fi
TOTAL_DVFRAMES_PACKAGED="0"
if [[ -f "${DVFILE}" ]] ; then
if [[ "${REPORT_ONLY}" = "Y" ]] ; then
echo -n "Analyzing ${BASENAME}"
else
echo -n "Packaging ${BASENAME}"
fi
if [[ "${#OUTPUTOPTS[@]}" != 0 ]]; then
# if dvrescue outputs are needed then let's make them
echo ", making dvrescue outputs"
"${DVRESCUE_PATH}" "${DVFILE}" "${OUTPUTOPTS[@]}"
fi
ERROR="$("${XMLSTARLET_PATH}" sel -N "d=https://mediaarea.net/dvrescue" -t -m "/d:dvrescue/d:media" -v "@error" "${XML_TO_CHECK}")"
if [[ -n "${ERROR}" ]] ; then
echo ". Error: ${BASENAME} (${ERROR})."
exit 1
else
_report -v "No xmlstarlet error"
# check if the sidecar directory is there
if [[ ! -d "${SIDECAR_DIR}" ]] ; then
_report -v "Trying to make a directory at ${SIDECAR_DIR}"
mkdir -p "${SIDECAR_DIR}"
fi
if [[ -s "${DVRESCUE_XML_TEMP}" ]] ; then
_report -v "Trying to move the temporary xml from ${DVRESCUE_XML_TEMP} to ${DVRESCUE_XML}"
mv "${DVRESCUE_XML_TEMP}" "${DVRESCUE_XML}"
fi
if [[ -s "${DVRESCUE_TECHSUBS_TEMP}" ]] ; then
_report -v "Trying to move the temporary vtt tfrom ${DVRESCUE_TECHSUBS_TEMP} ${DVRESCUE_TECHSUBS}"
mv "${DVRESCUE_TECHSUBS_TEMP}" "${DVRESCUE_TECHSUBS}"
fi
if [[ -s "${DVRESCUE_SCC_TEMP}" ]] ; then
_report -v "Trying to move the temporary scc from ${DVRESCUE_SCC_TEMP} to ${DVRESCUE_SCC}"
mv "${DVRESCUE_SCC_TEMP}" "${DVRESCUE_SCC}"
fi
if [[ -f "${DVRESCUE_SCC}" && ! -f "${DVRESCUE_SCC_VTT}" ]] ; then
_report -v "Trying to convert the scc to vtt"
"${FFMPEG_PATH}" -nostdin -v 0 -i "${DVRESCUE_SCC}" "${DVRESCUE_SCC_VTT}"
fi
fi
DV_FRAME_COUNT="$("${XMLSTARLET_PATH}" sel -N "d=https://mediaarea.net/dvrescue" -t -v "sum(/d:dvrescue/d:media[1]/d:frames/@count)" "${DVRESCUE_XML}" 2>/dev/null)"
_report -v "DV_FRAME_COUNT=(${DV_FRAME_COUNT})"
SOURCE_FORMAT="$("${XMLSTARLET_PATH}" sel -N "d=https://mediaarea.net/dvrescue" -t -m "/d:dvrescue/d:media[1]" -v "@format" "${DVRESCUE_XML}" 2>/dev/null)"
_report -v "SOURCE_FORMAT=(${SOURCE_FORMAT})"
if [[ "${SOURCE_FORMAT}" == "QuickTime" || "${SOURCE_FORMAT}" == "MPEG-4" ]] ; then
_report -v "Performing test of QuickTime PTS coherency."
MOV_INPUT_OPT=(-ignore_editlist true)
MOV_SHOWINFO="$("${FFMPEG_PATH}" "${MOV_INPUT_OPT[@]}" -i "${DVFILE}" -vframes 2 -vf showinfo -f null -map 0:v - 2>&1)"
MOV_PTS_0="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*0" | grep -o "pts:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_PTS_1="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*1" | grep -o "pts:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_PTS_2="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*2" | grep -o "pts:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_PTS_TIME_0="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*0" | grep -o "pts_time:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_PTS_TIME_1="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*1" | grep -o "pts_time:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_PTS_TIME_2="$(echo "${MOV_SHOWINFO}" | grep "showinfo.*n:[ ]*2" | grep -o "pts_time:[ ]*[0-9.]*" | cut -d : -f2 | sed 's/ //g')"
MOV_FIRST_FRAME_CHECK="$(echo "if ((${MOV_PTS_2} - ${MOV_PTS_0})/2 == ${MOV_PTS_1}) 1 else 0" | bc -l)"
_report -v "MOV_FIRST_FRAME_CHECK=(${MOV_FIRST_FRAME_CHECK}), MOV_SHOWINFO=(${MOV_SHOWINFO})"
fi
_report -v "Trying to present a table of ranges"
_ranges_2_table "$(_get_ranges)"
#DVRESCUE_VERSION="$("${XMLSTARLET_PATH}" sel -N "d=https://mediaarea.net/dvrescue" -t -v "d:dvrescue/d:creator/d:program" -o "-" -v "d:dvrescue/d:creator/d:version" -n "${DVRESCUE_XML}")"
#MUXER="${LAVF_VERSION} + ${DVRESCUE_VERSION}"
if [[ "$REPORT_ONLY" != "Y" ]] ; then
case "${EXT}" in
"mov")
if [[ -s "${DVRESCUE_TECHSUBS}" && "${SUBS}" = "Y" ]] ; then
OPT_OUTPUT+=(-map 1:s:0)
OPT_OUTPUT+=(-c:s mov_text)
OPT_OUTPUT+=(-tag:s:s:0 tx3g)
OPT_OUTPUT+=(-metadata:s:s:0 "title=dvrescue techsubs")
OPT_OUTPUT+=(-metadata:s:s:0 "language=zxx")
fi
if [[ -s "${DVRESCUE_SCC_VTT}" && "${SUBS}" = "Y" ]]; then
OPT_OUTPUT+=(-map 2:s:0)
OPT_OUTPUT+=(-c:s mov_text)
OPT_OUTPUT+=(-tag:s:s:1 tx3g)
OPT_OUTPUT+=(-metadata:s:s:1 "title=Captions")
if [[ -n "${CAPTION_LANG}" ]] ; then
OPT_OUTPUT+=(-metadata:s:s:1 "language=${CAPTION_LANG}")
else
OPT_OUTPUT+=(-metadata:s:s:1 "language=und")
fi
fi
;;
"mkv")
if [[ -s "${DVRESCUE_TECHSUBS}" && "${SUBS}" = "Y" ]] ; then
OPT_OUTPUT+=(-map 1:s:0)
OPT_OUTPUT+=(-c:s copy)
OPT_OUTPUT+=(-metadata:s:s:0 "title=dvrescue techsubs")
OPT_OUTPUT+=(-metadata:s:s:0 "language=zxx")
fi
if [[ -s "${DVRESCUE_SCC_VTT}" && "${SUBS}" = "Y" ]]; then
OPT_OUTPUT+=(-map 2:s:0)
OPT_OUTPUT+=(-c:s copy)
OPT_OUTPUT+=(-metadata:s:s:1 "title=Captions")
if [[ -n "${CAPTION_LANG}" ]] ; then
OPT_OUTPUT+=(-metadata:s:s:1 "language=${CAPTION_LANG}")
else
OPT_OUTPUT+=(-metadata:s:s:1 "language=und")
fi
fi
;;
esac
_report -d "The results will be written to ${SIDECAR_DIR}"
while IFS="|" read PTS END_PTS FRAME_START FRAME_END TC PKT_POS RDT SIZE VIDEO_RATE CH_SUB AR AUDIO_RATE CH REC_ST RDT_NC TC_NC COUNT_FRAMES LAST_FRAME_NO_AUDIO ; do
unset START_TIME METADATA START_BYTE FRAME_COUNT DURATION_SECS DURATION_ARG AUDIO_FILTER APAD SUB_INPUT MOV_PTS_OFFSET MOV_INPUT_RATE_OPT
PTS_START_FILENAME_SAFE="${PTS//:/-}"
OUTPUT_FILE="${SIDECAR_DIR}/${BASENAME%.*}_${PTS_START_FILENAME_SAFE}.${EXTENSION}"
OUTPUT_FILE_BASENAME="$(basename "${OUTPUT_FILE}")"
if [[ "${SOURCE_FORMAT}" == "QuickTime" || "${SOURCE_FORMAT}" == "MPEG-4" ]] ; then
if [[ "${MOV_FIRST_FRAME_CHECK}" == "0" ]] ; then
_report -d "Noting that the first few frames use uneven timestamps [$MOV_PTS_TIME_0,$MOV_PTS_TIME_1,$MOV_PTS_TIME_2]"
MOV_PTS_OFFSET="$(echo "(${MOV_PTS_TIME_1} - (1/(${VIDEO_RATE})) )" | bc -l | awk '{printf "%f", $0}')"
_report -d "Will apply a pts offset of ${MOV_PTS_OFFSET} to correlate between the timestamps of the encoding and the timestamps of the container."
fi
MOV_INPUT_RATE_OPT=(-r "${VIDEO_RATE}") # apply input framerate after pts offset coherency check
fi
if [[ "${SOURCE_FORMAT}" == "DV" ]] ; then
if [[ "${PKT_POS}" != "0" ]] ; then
START_BYTE=(-skip_initial_bytes "${PKT_POS}")
fi
else
if [[ "${PTS}" = "00:00:00.000000" ]] ; then
:
elif [[ -n "${MOV_PTS_OFFSET}" ]] ; then
PTS_OFFED="$(echo "$(_convert_hhmmssmmm2s "${PTS}") + ${MOV_PTS_OFFSET}" | bc -l | awk '{printf "%.9f\n", $0}')"
START_TIME=(-ss "${PTS_OFFED}")
else
START_TIME=(-ss "${PTS}")
fi
fi
FRAME_COUNT="$(echo "${FRAME_END} - ${FRAME_START}" + 1 | bc)"
DURATION_SECS="$(echo "${FRAME_COUNT}/(${VIDEO_RATE})" | bc -l | awk '{printf "%.9f\n", $0}')"
DURATION_ARG=(-t "${DURATION_SECS}")
INPUT_COUNT="1"
if [[ -n "${TC}" ]] && [[ "${EXTENSION}" != "dv" ]] ; then
METADATA+=(-metadata "timecode=${TC}")
fi
if [[ "${EXTENSION}" != "dv" ]] ; then
if [[ "${LAST_FRAME_NO_AUDIO}" != "0" ]] ; then
APAD=",apad=whole_dur=${DURATION_SECS}"
_report -d "FYI: Padding a little silence at the end of ${OUTPUT_FILE_BASENAME} since the source ends with frames missing audio."
fi
AUDIO_FILTER=(-af "aresample=async=1:min_hard_comp=0.01${APAD}")
fi
if [[ "${RDT:0:4}" =~ "^[0-9]{4}" ]] && [[ "${RDT:0:4}" -ge 1995 ]] ; then
if [[ "${EXTENSION}" = "mkv" ]] ; then
METADATA+=(-metadata "DATE_RECORDED=${RDT}")
elif [[ "${EXTENSION}" = "mov" ]] ; then
METADATA+=(-metadata "date=${RDT}")
fi
fi
if [[ "${SUBS}" = "Y" ]] ; then
if [[ "${PTS}" != "00:00:00.000000" ]] ; then
SUB_INPUT=(-ss "${PTS}")
fi
SUB_INPUT+=(-i "${DVRESCUE_TECHSUBS}")
((INPUT_COUNT++))
if [[ -s "${DVRESCUE_SCC_VTT}" ]]; then
if [[ "${PTS}" != "00:00:00.000000" ]] ; then
SUB_INPUT+=(-ss "${PTS}")
fi
SUB_INPUT+=(-i "${DVRESCUE_SCC_VTT}")
((INPUT_COUNT++))
fi
fi
if [[ "${EXTENSION}" != "dv" ]] ; then
CHAPTER_FFMETADATA="$(_maketemp).ffmetadata"
_make_chapter_metadata_file "${PTS}" "${END_PTS}" > "${CHAPTER_FFMETADATA}"
CHAPTER_PROCESS=(-i "${CHAPTER_FFMETADATA}")
CHAPTER_PROCESS+=(-map_metadata "${INPUT_COUNT}")
fi
((INPUT_COUNT++))
if [[ "${SOURCE_FORMAT}" == "DV" ]] ; then
_report -v "Trying to run: ${FFMPEG_PATH} ${FFMPEG_VERBOSE[@]} ${OPT_INPUT[@]} ${START_BYTE[@]} -i ${DVFILE} ${SUB_INPUT[@]} ${CHAPTER_PROCESS[@]} ${AUDIO_FILTER[@]} ${DURATION_ARG[@]} ${OPT_OUTPUT[@]} ${METADATA[@]} ${OUTPUT_FILE}"
"${FFMPEG_PATH}" "${FFMPEG_VERBOSE[@]}" "${OPT_INPUT[@]}" "${START_BYTE[@]}" -i "${DVFILE}" \
"${SUB_INPUT[@]}" "${CHAPTER_PROCESS[@]}" "${AUDIO_FILTER[@]}" "${DURATION_ARG[@]}" "${OPT_OUTPUT[@]}" "${METADATA[@]}" "${OUTPUT_FILE}"
else
"${FFMPEG_PATH}" "${FFMPEG_VERBOSE[@]}" "${OPT_INPUT[@]}" "${MOV_INPUT_OPT[@]}" "${MOV_INPUT_RATE_OPT[@]}" "${START_TIME[@]}" -i "${DVFILE}" \
-map 0:v:0 -c:v:0 copy -f rawvideo - | ffmpeg "${FFMPEG_VERBOSE[@]}" "${OPT_INPUT[@]}" -i - \
"${SUB_INPUT[@]}" "${CHAPTER_PROCESS[@]}" "${AUDIO_FILTER[@]}" "${DURATION_ARG[@]}" "${OPT_OUTPUT[@]}" "${METADATA[@]}" "${OUTPUT_FILE}"
fi
if [[ -f "${MEDIAINFO_PATH}" ]] ; then
PACKAGED_DVFRAMES="$(_count_dv_frames "${OUTPUT_FILE}")"
TOTAL_DVFRAMES_PACKAGED="$(echo "${TOTAL_DVFRAMES_PACKAGED} + ${PACKAGED_DVFRAMES}" | bc -l)"
fi
# quick track duration check on the result
if [[ -f "${MEDIAINFO_PATH}" ]] ; then
AUD_STREAM_COUNT="$("${MEDIAINFO_PATH}" -f --Output="General;%AudioCount%" "${OUTPUT_FILE}" | head -n 1 | awk '{printf "%s", $0}')"
_report -v "AUD_STREAM_COUNT=(${AUD_STREAM_COUNT})"
if [[ -n "${AUD_STREAM_COUNT}" ]] ; then
VID_DUR="$("${MEDIAINFO_PATH}" -f --Output="Video;%Duration%\n" "${OUTPUT_FILE}" | head -n 1 | awk '{printf "%s", $0}')"
AUD_DUR="$("${MEDIAINFO_PATH}" -f --Output="Audio;%Duration%\n" "${OUTPUT_FILE}" | head -n 1 | awk '{printf "%s", $0}')"
DUR_DIFF="$(echo "${VID_DUR}" - "${AUD_DUR}" | bc | awk '{printf "%f", $0}')"
_report -v "VID_DUR=(${VID_DUR}), AUD_DIR=(${AUD_DUR}), DUR_DIFF=(${DUR_DIFF})"
if (( $( echo "${DUR_DIFF} >= -033" |bc -l) )) && (( $(echo "${DUR_DIFF} <= 033" |bc -l) )); then
_report -v "DUR_DIFF is within an acceptable range."
else
_report -w "ERROR: In ${OUTPUT_FILE_BASENAME} the video track is ${VID_DUR} milliseconds and the audio track is ${AUD_DUR}. These tracks may lose sync."
fi
else
_report -d "FYI: ${OUTPUT_FILE_BASENAME} has no audio track."
fi
fi
_report -d "FYI: Packaged to ${OUTPUT_FILE_BASENAME}."
if [[ -f "${MEDIAINFO_PATH}" ]] && [[ "${PACKAGED_DVFRAMES}" != "${FRAME_COUNT}" ]] ; then
_report -w "ERROR: ${BASENAME} contained ${FRAME_COUNT} DV frames at ${PTS}-${END_PTS} but ${OUTPUT_FILE_BASENAME} contains ${PACKAGED_DVFRAMES} frames of DV."
fi
done < <(_get_ranges)
if [[ -f "${MEDIAINFO_PATH}" ]] && [[ "${TOTAL_DVFRAMES_PACKAGED}" != "${DV_FRAME_COUNT}" ]] ; then
_report -w "ERROR: ${BASENAME} contained ${DV_FRAME_COUNT} DV frames, but the outputs contain ${TOTAL_DVFRAMES_PACKAGED} frames of DV."
fi
fi
else
echo "${BASENAME} is not a file, skipping."
fi