-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathzoom-upconvert
244 lines (198 loc) · 9.74 KB
/
zoom-upconvert
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!/bin/bash
######### CONFIGURATION #########
SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
HOSTNAME=$(hostname) ## THE HOSTNAME OF THIS COMPUTER. USED IN FILE LIST FILENAME
WORK_DIR="${SCRIPTPATH}/convert_work" ## PATH TO DIRECTORY IN WHICH THE FILE LISTS ARE STORED
FILELIST="${WORK_DIR}/list_zoom_${HOSTNAME}.txt" ## FULL PATH TO THE FILELIST FILE
CURRENT_CONVERSION="${WORK_DIR}/current_zoom_conversion_${HOSTNAME}.txt" ## FULL PATH TO THE FILE THAT STORES THE CURRENT CONVERSION JOB TITLE
PATHS=("${SCRIPTPATH}") # BASH ARRAY OF PATHS TO RECURSIVELY CHECK FOR VIDEO FILES
FILE_EXT=("mp4") # FILE EXTENSIONS TO SEARCH FOR
######### END CONFIGURATION #########
######### GENERATE THE FILE LIST #########
FIND_FILE_EXT=() # FILE EXTENSIONS WILL BE UPATED WITH FIND SYNTAX AND STORED IN A SEPARATE ARRAY
# LOOP THROUGH THE FILE EXTENSIONS AND UPDATE THEM TO BE IN FIND SYNTAX
for EXT in ${FILE_EXT[@]}
do
FIND_FILE_EXT+=("-iname *.${EXT}")
done
#### JOIN ALL OF THE FIND FILE EXT ELEMENTS TOGETHER IN FIND SYNTAX ####
delim=$' -o ' # DELIMITER IS THE OR OPERATOR
# MERGE ALL OF THE EXTENSIONS USING THE -O OPERATOR AND REPLACE FIND_FILE_EXT WITH THE NEW STRING
printf -v FIND_FILE_EXT "%s$delim" "${FIND_FILE_EXT[@]}"
# REMOVE THE TRAILING -O
FIND_FILE_EXT="${FIND_FILE_EXT%$delim}"
# CREATE THE WORK DIRECTORY
echo "Creating work directory..."
mkdir -p "$WORK_DIR"
# CREATE/TRUNCATE THE FILE LIST
echo "Creating and clearing file list..."
touch "$FILELIST"
truncate -s 0 "$FILELIST"
# POPULATE THE FILE LIST
echo "Building file list..."
for i in "${PATHS[@]}"
do
## SEARCHES ALL PATHS FOR EACH OF THE EXTENSIONS AND
## STORES THE FULL PATH FOR EACH ONE FOUND, ONE PER LINE
## SKIPS DIRECTORIES NAMED PROCESSED
find "$i" -type d -name "processed" -prune -o \( $FIND_FILE_EXT \) -exec greadlink -f {} >> "$FILELIST" \;
done
FILE_COUNT=$(wc -l "$FILELIST" | awk '{print $1}')
CURRENT_INDEX=0
####### END FILE LIST BUILD #######
####### BEGIN TRANSCODE #######
echo "Transcoding..."
while IFS= read -r line ## ITERATE THROUGH THE FILE LIST, LINE BY LINE
do
## INCREMENT THE INDEX COUNT
CURRENT_INDEX=$((CURRENT_INDEX+1))
CURRENT_INDEX_PCT=$(echo "scale=2; ${CURRENT_INDEX}/${FILE_COUNT}*100" | bc)
## GET THE ORIGINAL DIRECTORY PATH
FILE_DIR=$(dirname "${line}")
PROCESS_FILE_DIR="${FILE_DIR}/processed"
## create the processed file directory
mkdir -p "$PROCESS_FILE_DIR"
## USE SUBSTITUTION TO BUILD THE DESTINATION FILE NAME
filename=$(basename -- "$line")
extension="${filename##*.}"
filename="${filename%.*}"
NEW_FILE="${PROCESS_FILE_DIR}/${filename}.mp4"
## if the process is killed prematurely, delete the destination file in progress so that it is started over on next invocation
trap "rm \"${NEW_FILE}\"; exit" SIGINT
## TRANSCODE IF FILE DOESN'T ALREADY EXIST
if [ ! -f "$NEW_FILE" ]; then
## SET THE WINDOW TITLE ON OH-MY-ZSH
DISABLE_AUTO_TITLE="true"
echo -e "\033];Transcoding ${line}\007"
echo "transcoding ${line}"
echo "${line} -> ${NEW_FILE}" > "$CURRENT_CONVERSION"
## capture the source file's metadata and use it to calculate the destination parameters
eval $(ffprobe -v quiet -show_format -of flat=s=_ -show_entries stream=height,width,nb_frames,nb_streams,duration,codec_name,bit_rate,channels,display_aspect_ratio,codec_type,r_frame_rate,size "$line");
audio_stream_index=false
video_stream_index=false
min_video_bitrate=$((5 * 1024))
video_encode_codec="h264_videotoolbox" ## use h264_videotoolbox as the video codec to leverage the GPU. libx264 will use the CPU and will provide smaller files, but also longer encode times
video_filters=() ## starts out as an empty array and may never be populated if the source material doesn't require it
stereo_audio_bitrate=441
min_height=1080
audio_channels=false
input_maps=()
audio_processing=("-c:a:0 aac")
metadata=("-metadata:s:a:0 title=Stereo")
stream_count=${format_nb_streams}
## loop through the streams and identify the audio and video channels and properties
for stream in `seq 0 $((stream_count - 1))`;
do
codec_type_var="streams_stream_${stream}_codec_type"
codec_type=${!codec_type_var}
## if this is the first audio channel, update the mapping and capture the number of channels
if [[ $codec_type == "audio" && $audio_stream_index == false ]]; then
audio_stream_index=$stream
audio_channel_var="streams_stream_${stream}_channels"
audio_channels=${!audio_channel_var}
audio_bitrate_var="streams_stream_${stream}_bit_rate"
audio_bitrate=${!audio_bitrate_var}
detected_audio_bitrate=$audio_bitrate
## if no audio bitrate was detected, use the stereo audio bitrate
if [ $audio_bitrate == "N/A" ]; then
audio_bitrate=$stereo_audio_bitrate
else
audio_bitrate=$((audio_bitrate / 1000))
detected_audio_bitrate="${audio_bitrate}k"
fi
## let's make the audio stero
if [ "$audio_channels" -ne 2 ]; then
audio_processing+=("-ac:a:0 2")
fi
## for consistency, always map audio channel to 1 in destination file
input_maps[1]="-map 0:${audio_stream_index}"
fi
if [[ $codec_type == "video" && $video_stream_index == false ]]; then
video_stream_index=$stream
height_var="streams_stream_${video_stream_index}_height"
height=${!height_var}
aspect_ratio_var="streams_stream_${video_stream_index}_display_aspect_ratio"
aspect_ratio=${!aspect_ratio_var}
video_bitrate=${format_bit_rate}
video_bitrate=$((video_bitrate / 1000))
original_file_size=${format_size}
framerate_var="streams_stream_${video_stream_index}_r_frame_rate"
framerate=${!framerate_var}
## for consistency, always map video channel to 0 in destination file
input_maps[0]="-map 0:${video_stream_index}"
fi
done
# Increase the bitrate when necessary
detected_video_bitrate=$video_bitrate
if [ "$video_bitrate" -lt "$min_video_bitrate" ]; then
video_bitrate=$min_video_bitrate
fi
# Make sure the height is 1080 when the original video is NOT detected as being something other than 16:9 or 4:3
if [[ "$min_height" -ne "$height" && ($aspect_ratio == "16:9" || $aspect_ratio == "4:3" || $aspect_ratio == "N/A") ]]; then
video_filters+=("scale=-1:$min_height:flags=lanczos")
fi
## if the audio bitrate is lower than the stereo bitrate, increase it
if [ "$audio_bitrate" -lt "$stereo_audio_bitrate" ]; then
audio_bitrate=$stereo_audio_bitrate
fi
audio_processing+=("-b:a:0 ${audio_bitrate}k")
## convert command arrays into strings
input_maps=$( IFS=$' '; echo "${input_maps[*]}" )
audio_processing=$( IFS=$' '; echo "${audio_processing[*]}" )
metadata=$( IFS=$' '; echo "${metadata[*]}" )
if [ ${#video_filters[@]} -ne 0 ]; then
video_filters=$( IFS=$','; echo "${video_filters[*]}" )
video_filters="-vf $video_filters"
else
video_filters=""
fi
video_filters_debug="None"
if [ ! -z "$video_filters" ]; then
video_filters_debug=$video_filters
fi
#### DEBUG DETAILS ###
echo -e "\n#####"
echo "Count: ${CURRENT_INDEX} of ${FILE_COUNT} (${CURRENT_INDEX_PCT}%)"
echo "File: ${line}"
echo "Audio channels: $audio_channels"
echo "Audio Bitrate: ${audio_bitrate}k (used in conversion calculations)"
echo "Audio Stream Index: ${audio_stream_index}"
echo "Detected Audio Bitrate: ${detected_audio_bitrate} (results from ffprobe)"
echo "Video Bitrate: ${video_bitrate}k (used in conversion calculations)"
echo "Detected Video Bitrate: ${detected_video_bitrate}k (results from ffprobe)"
echo "Video height: $height"
echo "Video aspect ratio: $aspect_ratio"
echo "Video stream index: ${video_stream_index}"
echo "Calculated filters: $video_filters_debug"
echo "Calculated input map: $input_maps"
echo "Calculated audio processing: $audio_processing"
echo "Calculated metadata: $metadata"
echo -e "\n"
echo "### Command ###"
echo -e "ffmpeg-bar -i \"${line}\" \ \n ${input_maps} \ \n -c:v ${video_encode_codec} \ \n -b:v ${video_bitrate}k \ \n -pix_fmt yuv420p \ \n ${video_filters} \ \n -sn \ \n ${audio_processing} \ \n ${metadata} \ \n \"${NEW_FILE}\""
echo -e "#####\n"
## transcode
ffmpeg-bar -i "$line" `## input the discovered source file` \
$input_maps `## handles input mapping for multichannel audio` \
-c:v "$video_encode_codec" \
-b:v "$video_bitrate"k `## use the calculated video bit rate based on the source` \
-pix_fmt yuv420p `## force the pixel format to yuv420p for quicktime compatibility` \
$video_filters `## plugs in built out filters` \
-sn `## strip subtitles in favor of external files` \
$audio_processing `## handles audio mapping when source material is 5.1` \
$metadata `## adds metadata` \
"$NEW_FILE" < /dev/null ## output to new file name
eval $(ffprobe -v quiet -show_format -of flat=s=_ -show_entries stream=height,width,nb_frames,nb_streams,duration,codec_name,bit_rate,channels,display_aspect_ratio,codec_type,size "$NEW_FILE");
new_file_size=${format_size}
file_size_change=$(echo "scale=2; ${new_file_size}-${original_file_size}" | bc)
file_size_pct_change=$(echo "scale=2; ${file_size_change}/${original_file_size}*100" | bc)
file_size_change=$(echo "scale=2; ${file_size_change}/1000/1000" | bc)
#### DEBUG DETAILS ###
echo -e "\n#####"
echo "Complete"
echo "File size change: ${file_size_change}MB"
echo "File size pct change: ${file_size_pct_change}%"
echo -e "#####\n"
### END DEBUG ###
fi
done < "$FILELIST"