Skip to content

Instantly share code, notes, and snippets.

@brlodi
Created October 13, 2021 02:16
Show Gist options
  • Save brlodi/67e151c81e20f4fc4c0a55757897d87c to your computer and use it in GitHub Desktop.
Save brlodi/67e151c81e20f4fc4c0a55757897d87c to your computer and use it in GitHub Desktop.
When Blue Iris writes exported clips that switch between the substream and main stream, they're just streams of frames with varying resolutions. Most video players don't like that, because they expect all frames of a video to be the same size. This intelligently reencodes such videos *only if needed* so they can be consumed by other software.
function bi_fix_multires
function _get_frames_info -a f field_name
ffprobe -hide_banner -skip_frame nokey -show_frames -show_entries frame=$field_name $f 2>/dev/null | awk -F'=' "/$field_name/{print \$2}"
end
function _get_stream_info -a f field_name
ffprobe -hide_banner -show_entries stream=$field_name $f 2>/dev/null | awk -F'=' "/$field_name/{print \$2}"
end
set --local files (du $argv | sort -n | awk '{print $2}')
for f in $files
echo -n "Checking $f..."
set --local of (string split -r -f 1 -m 1 . $f).fixed.(string split -r -f 2 -m 1 . $f)
# Extracting this data is very slow, but still orders of magnitude faster
# than unnecessarily transcoding a large video
set --local codec (_get_stream_info $f codec_name)
set --local codec_tag (_get_stream_info $f codec_tag_string)
set --local frame_rate (_get_stream_info $f r_frame_rate)
set --local frame_heights (_get_frames_info $f height | sort -unr)
set --local frame_widths (_get_frames_info $f width | sort -unr)
set --local target_height $frame_heights[1] # maximum detected frame height
set --local target_width (math "ceil($target_height * 16 / 9)")
# echo "\n detected widths: ["(string join ", " $frame_widths)"], detected heights: ["(string join ", " $frame_heights)"], target resolution = "$target_width"x"$target_height
# If there's only a single frame height in the file we check that it's
# actually a 16x9 image. If both are true, we skip reencoding the video.
if test \( (count $frame_heights) -eq 1 \) -a \( $target_width -eq $frame_widths[1] \)
# We don't need to reencode, but while we're here we can swap the HEVC tag
# to the one macOS and iOS can natively recognize. This is just metadata
# so it's essentially free to fix performance-wise.
if test \( $codec = "hevc" \) -a \( $codec_tag != "hvc1" \)
echo -n " fixing HEVC tags..."
ffmpeg -hide_banner -loglevel error -hwaccel auto -i "$f" -c:v copy -c:a copy -tag:v hvc1 $of
echo -e " done!\n"
else
echo -e " nothing to fix!\n"
end
else
# Otherwise, reencode the file to be the proper & consistent size and
# aspect ratio so standard programs can read it easily
echo " re-rendering to $of"
ffmpeg -hide_banner -loglevel error -stats -hwaccel auto -i "$f" -vf "scale=$target_width:$target_height" -c:a copy -c:v libx265 -x265-params log-level=none -crf 24 -tag:v hvc1 $of
end
end
# Clean up "private" functions
functions -e _get_frames_info
functions -e _get_stream_info
end
bi_fix_multires ./test/*.mp4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment