-
Notifications
You must be signed in to change notification settings - Fork 27
/
makelossless
executable file
·200 lines (185 loc) · 9.29 KB
/
makelossless
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/bin/bash
# makelossless
VERSION="1.0"
SCRIPTDIR=$(dirname $(which "${0}"))
. "${SCRIPTDIR}/mmfunctions" || { echo "Missing '${SCRIPTDIR}/mmfunctions'. Exiting." ; exit 1 ;};
DEPENDENCIES=(ffmpeg ffprobe mediainfo)
FFV1_VERSION_EXPECTED=3.4
_initialize_make
_usage(){
echo
echo "$(basename "${0}") ${VERSION}"
echo "This application will losslessly transcode a video file or package input with the following options."
echo "Dependencies: ${DEPENDENCIES[@]}"
echo "Usage: $(basename ${0}) fileorpackage1 [ fileorpackage2 ...]"
echo " -j (use lossless jpeg2000 instead of ffv1 version 3)"
echo " -n (dry-run mode, show the commands that would be run but don't do anything)"
echo " -h display this help"
echo
exit
}
[ "${#}" = 0 ] && _usage
_get_ffv1_version(){
FFV1_VERSION="$(ffmpeg -nostdin -debug 1 -i "${1}" -vframes 1 -f null - 2>&1 | grep -m 1 "^\[ffv1" | grep -o "ver:[0-9.]*" | cut -d":" -f2)"
}
_get_frame_rate(){
FRAMERATE=$(ffprobe -i "${1}" -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1)
}
_update_filename(){
filename="${1}"
filename_noext="${filename%.*}"
filename_ext="${filename##*.}"
filename_lastunderscore="${filename_noext##*_}"
filename_wolastunderscore="${filename_noext%_*}"
if [[ "${filename_lastunderscore}" == "${SUFFIX}" ]] ; then
echo "${filename_wolastunderscore}_${SUFFIX}r1.${filename_ext}"
elif [[ "${filename_lastunderscore}" == "${SUFFIX}r"* ]] ; then
REVISION="$(echo "${filename_lastunderscore}" | cut -d "r" -f 2-)"
NEWREVISION=$((REVISION+1))
echo "${filename_wolastunderscore}_${SUFFIX}r${NEWREVISION}.${filename_ext}"
else
echo "${filename_wolastunderscore}_${SUFFIX}.${filename_ext}"
fi
}
# command-line options to set mediaid and original variables
OPTIND=1
while getopts ":jnh" OPT ; do
case "${OPT}" in
j) JPEG2000MODE="Y";;
n) DRYRUN=true;;
h) _usage ;;
*) echo "bad option -${OPTARG}" ; _usage ;;
:) echo "Option -${OPTARG} requires an argument" ; _writeerrorlog "makelossless" "The option selected required an argument and none was provided. The script had to exit." ; exit 1 ;;
esac
done
shift $(( ${OPTIND} - 1 ))
while [ "${*}" != "" ] ; do
# get context about the input
INPUT="${1}"
[ -d "${INPUT}" ] && LOGDIR="${INPUT}/metadata/logs"
[ -f "${INPUT}" ] && LOGDIR="$(dirname "${INPUT}")/lossless/logs"
[ ! "${LOGDIR}" ] && LOGDIR="${INPUT}/metadata/logs"
INPUTFILES=$(_maketemp)
if [ -f "${INPUT}" ] ; then
streamcount=$(ffprobe -loglevel quiet "$file" -show_entries format=nb_streams -of default=nw=1:nk=1)
duration_ts=$(ffprobe -loglevel quiet "$file" -show_entries stream=duration_ts -of default=nw=1:nk=1)
if [[ "$streamcount" > 0 && "${duration_ts}" != 1 ]] ; then
_report -d "Input file: $file"
echo "$file" >> "${INPUTFILES}"
fi
elif [ -d "${INPUT}" ] ; then
# find av files in a directory and output to a temp list
find "${1}/objects" -type f -size +0 "${OBJECTS_FIND_EXCLUSIONS[@]}" | while read file ; do
streamcount=$(ffprobe -loglevel quiet "$file" -show_entries format=nb_streams -of default=nw=1:nk=1)
duration_ts=$(ffprobe -loglevel quiet "$file" -show_entries stream=duration_ts -of default=nw=1:nk=1)
if [[ "$streamcount" > 0 && "${duration_ts}" != 1 ]] ; then
_report -d "Input file: $file"
echo "$file" >> "${INPUTFILES}"
fi
done
else
_report -wt "Error: ${INPUT} is not a file or directory"
exit 1
fi
INPUTFILECOUNT=$(wc -l "${INPUTFILES}" | awk '{print $1}')
if [[ "${INPUTFILECOUNT}" = 0 ]] ; then
_report -w "Error no audiovisual input files were found."
exit 1
fi
while read SOURCEFILE ; do
OUTPUTDIR=$(dirname "${SOURCEFILE}")
SOURCEFILENAME=$(basename "${SOURCEFILE}")
_log -b
# clear local arrays
_unset_variables
# encoding options
_get_codectagstring "${SOURCEFILE}"
_get_videostreamcount "${SOURCEFILE}"
_get_audiostreamcount "${SOURCEFILE}"
_get_width "${SOURCEFILE}"
_get_height "${SOURCEFILE}"
_get_frame_rate "${SOURCEFILE}"
INPUTOPTIONS+=(-nostdin)
INPUTOPTIONS+=(-vsync 0)
if [[ "${VIDEOSTREAMCOUNT}" > 0 ]] ; then
if [[ "${CODEC_TAG_STRING}" == "mjp2" ]] ; then
INPUTOPTIONS+=(-vcodec libopenjpeg)
fi
MIDDLEOPTIONS+=(-map 0:v)
MIDDLEOPTIONS+=(-map 0:a)
if [[ "${JPEG2000MODE}" == "Y" ]] ; then
MIDDLEOPTIONS+=(-c:v libopenjpeg)
INPUTOPTIONSFRAMEMD5+=(-vcodec libopenjpeg)
SUFFIX="j2k"
else
MIDDLEOPTIONS+=(-c:v ffv1)
MIDDLEOPTIONS+=(-level 3)
MIDDLEOPTIONS+=(-g 1)
MIDDLEOPTIONS+=(-slices 16)
MIDDLEOPTIONS+=(-slicecrc 1)
SUFFIX="ffv1"
fi
_add_video_filter "setfield=bff,setsar=40/27,setdar=4/3" # this is a presumption but much of the uncompressed input is bff but not probably labelled
fi
if [[ "${WIDTH}" = "720" && "${HEIGHT}" = "486" || "${HEIGHT}" = "480" ]] && [[ "${FRAMERATE}" = "30000/1001" || "${FRAMERATE}" = "29970/1001" || "${FRAMERATE}" = "2997/100" ]] ; then
_report -dt "Presuming this is NTSC and adding metadata about colorspace, aspect ratio, interlacement, and broadcast range."
MIDDLEOPTIONS+=(-color_primaries smpte170m)
MIDDLEOPTIONS+=(-color_trc bt709)
MIDDLEOPTIONS+=(-colorspace smpte170m)
MIDDLEOPTIONS+=(-aspect 4/3)
MIDDLEOPTIONS+=(-color_range mpeg)
MIDDLEOPTIONS+=(-field_order bt)
fi
if [[ "${AUDIOSTREAMCOUNT}" > 0 ]] ; then
MIDDLEOPTIONS+=(-c:a copy)
fi
_get_codectagstring "${SOURCEFILE}"
if [[ "${CODEC_TAG_STRING}" == "FFV1" ]] ; then
_get_ffv1_version "${SOURCEFILE}"
fi
if [[ "${CODEC_TAG_STRING}" == "2vuy" || \
"${CODEC_TAG_STRING}" == "v210" || \
("${CODEC_TAG_STRING}" == "FFV1" && "$(echo "${FFV1_VERSION} < ${FFV1_VERSION_EXPECTED}" | bc)" == "1") || \
("${CODEC_TAG_STRING}" == "FFV1" && "${SOURCEFILE##*.}" != "mkv") ]] ; then
if [[ -n "${FFV1_VERSION}" ]] ; then
_report -dt "${SOURCEFILENAME} is ${CODEC_TAG_STRING} version ${FFV1_VERSION} (${FFV1_VERSION}<${FFV1_VERSION_EXPECTED}), starting encode"
else
_report -dt "${SOURCEFILENAME} is ${CODEC_TAG_STRING}, starting encode"
fi
LOSSLESS_OUTPUT=$(_update_filename "${OUTPUTDIR}/${SOURCEFILENAME%.*}.mkv")
REFORMATTED_DIR="${1}/objects/reformatted/$(date +%Y-%m-%dT%H-%M-%S)"
_run_critical _mkdir2 "${REFORMATTED_DIR}"
SOURCE_FRAMEMD5_OUTPUT="${REFORMATTED_DIR}/${SOURCEFILENAME}_$(date +%Y-%m-%dT%H-%M-%S).framemd5"
LOSSLESS_FRAMEMD5_OUTPUT="${REFORMATTED_DIR}/$(basename "${LOSSLESS_OUTPUT}_$(date +%Y-%m-%dT%H-%M-%S).framemd5")"
if [[ ! -f "${LOSSLESS_OUTPUT}" && ! -f "${SOURCE_FRAMEMD5_OUTPUT}" ]] ; then
_run mkdir -p "${OUTPUTDIR}" "${LOGDIR}"
_prep_ffmpeg_log
_filter_to_middle_option
_report -dt "Creating a framemd5 report and lossless ffv1 for ${SOURCEFILENAME}."
_run_critical ffmpeg ${INPUTOPTIONS[@]} -i "${SOURCEFILE}" ${MIDDLEOPTIONS[@]} "${LOSSLESS_OUTPUT}" -f framemd5 -an "${SOURCE_FRAMEMD5_OUTPUT}"
_report -dt "Create a framemd5 for the output to verify against ${SOURCEFILENAME}."
if [ "${CODEC_TAG_STRING}" == "2vuy" ] ; then
_run_critical ffmpeg ${INPUTOPTIONS[@]} -i "${LOSSLESS_OUTPUT}" -f framemd5 -pix_fmt uyvy422 -an "${LOSSLESS_FRAMEMD5_OUTPUT}"
else
_run_critical ffmpeg ${INPUTOPTIONS[@]} ${INPUTOPTIONSFRAMEMD5[@]} -i "${LOSSLESS_OUTPUT}" -f framemd5 -an "${LOSSLESS_FRAMEMD5_OUTPUT}"
fi
if [ $(grep -v "^#" "${SOURCE_FRAMEMD5_OUTPUT}" | md5 -q) = $(grep -v "^#" "${LOSSLESS_FRAMEMD5_OUTPUT}" | md5 -q) ] ; then
_report -dt "Everything looks safe. Going to move original to reformatted directory."
_run_critical mediainfo -f --language=raw --output=XML "${SOURCEFILE}" > "${REFORMATTED_DIR}/${SOURCEFILENAME}_mediainfo.xml"
_run_critical mv -v -n -f -v "${SOURCEFILE}" "${REFORMATTED_DIR}/"
else
_report -wt "Not looking safe. Going to keep the original."
fi
else
_report -wt "Either ${LOSSLESS_OUTPUT} or ${SOURCE_FRAMEMD5_OUTPUT} already exists. Not proceeding."
fi
else
_report -wt "${SOURCEFILENAME} is not 2vuy or v210 or progressive ffv1 or progressive ffv1 or ffv1 less than 3.4, skipping"
_writeerrorlog "makelossless" "The source file is not 2vuy or v210 or progressive ffv1 or ffv1 less than 3.4, so makelossless was unable to run."
continue
fi
_report -dt done with "${SOURCEFILE}"
done < "${INPUTFILES}"
shift
_log -e
done