Skip to content

Instantly share code, notes, and snippets.

@Cygon
Last active May 31, 2023 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cygon/d3de69076fad8f63d5c2804319c8da4c to your computer and use it in GitHub Desktop.
Save Cygon/d3de69076fad8f63d5c2804319c8da4c to your computer and use it in GitHub Desktop.
Shell script that encodes a movie to HEVC using Intel's lesser known SVT-HEVC encoder
#!/bin/sh
# File from which the video is taken
inputFile=$1
# File to which the HEVC-encoded video bitstream will be written
hevcFile=${inputFile%.*}.hevctranscoded.hevc
# File to which the final generated output video will be written
outputFile=${inputFile%.*}.hevctranscoded.mkv
# This should output the number of video frames
# ...but it doesn't work
#
#ffprobe \
# -v error \
# -select_streams v:0 \
# -count_frames \
# -show_entries stream=nb_read_packets \
# -of csv=p=0 \
# -i "$inputFile"
#
#
# This is slower and it does work. But careful, ffmpeg does not
# print a line on every frame but after 1 second or so elapsed,
# so perhaps the count can be off by a few frames?
#
#ffmpeg \
# -i "$inputFile" \
# -vcodec copy \
# -f rawvideo \
# -y \
# /dev/null
#
#
# This seems to agree with ffmpeg and may be more reliable:
#
#mediainfo \
# --fullscan \
# "$inputFile" \
# | grep -i "frame count"
#
# Number of frames to encode
#
# Check: can this be left out ot automated somehow?
# For now, I'll set it to a ridiculously low value so it becomes
# obvious if the parameter is forgotten without spending too much time.
: ${frameCount:=100}
# Dimensions of the video
: ${videoWidth:=1920}
: ${videoHeight:=1080}
# Bits per second to use for the video and audio streams
#
# Suggested for stereo movies | Suggested for 5.1 movies
# --------------------------------|--------------------------------
# 768 = 672 video + 96 audio
# 1024 = 928 video + 96 audio
# 1536 = 1408 video + 128 audio 1536 = 1280 video + 256 audio
# 2048 = 1920 video + 128 audio 2048 = 1792 video + 256 audio
# 2560 = 2432 video + 128 audio 2560 = 2152 video + 384 audio
# 3072 = 2944 video + 128 audio 3072 = 2688 video + 384 audio
# 4096 = 3968 video + 128 audio 4096 = 3712 video + 384 audio
# 5120 = 4992 video + 128 audio 5120 = 4736 video + 384 audio
# 6144 = 5670 video + 384 audio
#: ${videoBitRate:=2359296}
: ${videoBitRate:=2574187}
# Tell HEVC-SVT about the frame rate of the encoded movie.
#
# This is pretty much pointless because the encoder will mess up so badly
# that, at the VERY latest, after 1 minute the video is too far out of sync
# to even associate spoken dialogue with its character...
#
: ${videoFrameRateNum:=24000}
: ${videoFrameRateDenom:=1001}
# Whether to do a 10-bit encode
#
# Ten bit encodes are claimed to avoid banding. I've yet to see any
# any banding in a 'veryslow' encode, but it also doesn't increase
# bit rate requirements by that much, so I default to yes.
#
: ${tenBit:=false}
# Whether to sharpen the video
#
: ${sharpenVideo:=true}
# ----------------------------------------------------------------------------
ffmpegParameters=()
svtParameters=()
if $sharpenVideo ; then
echo Sharpening enabled
ffmpegParameters+=(-filter:v "smartblur=lr=2.0:ls=-0.5:lt=-3.5:cr=1.0:cs=-0.25:ct=-1.5")
fi
# We use the 'main' rather than the 'high' tier. The tier does
# not enable any advanced encoding technology, it merely switches
# between two lanes of allowed bitrates. For out goals,
# the 'main' tier is more than enough and isn't outright rejected
# by cheap TVs.
#
if $tenBit ; then
echo 10-bit encode
ffmpegParameters+=(-pix_fmt yuv420p10le)
svtParameters+=(-profile 2)
svtParameters+=(-bit-depth 10)
else
echo 8-bit encode
ffmpegParameters+=(-pix_fmt yuv420p)
svtParameters+=(-profile 1)
svtParameters+=(-bit-depth 8)
fi
# ----------------------------------------------------------------------------
# Perform the actual transcode, letting ffmpeg output fullYUV frames into
# stdout, which is then piped into the SvtHevcEncApp executable
#
ffmpeg \
-hide_banner \
-i "$inputFile" \
-nostdin \
"${ffmpegParameters[@]}" \
-f rawvideo \
- | SvtHevcEncApp \
-i stdin \
-n $frameCount \
-w $videoWidth \
-h $videoHeight \
-rc 1 \
-tbr $videoBitRate \
-fps-num $videoFrameRateNum \
-fps-denom $videoFrameRateDenom \
"${svtParameters[@]}" \
-encMode 0 \
-tier main \
-b "$hevcFile"
# ----------------------------------------------------------------------------
# For some reason, the generated .hevc file, when merged via mkvmerge or
# mkvtoolnix, will not have the correct frame rate. This may be a bug in
# the mkv tools, but in either case, to fix it we just need to generate
# an .mkv file with correct frame rate and we're good...
#
mkvmergeParameters=()
mkvmergeParameters+=(--default-duration 0:${videoFrameRateNum}/${videoFrameRateDenom}fps)
mkvmergeParameters+=(--fix-bitstream-timing-information 0)
mkvmerge \
"${mkvmergeParameters[@]}" \
"$hevcFile" \
-o "$outputFile"
# ----------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment