Last active
May 31, 2023 09:56
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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