Skip to content

Instantly share code, notes, and snippets.

@leporel
Last active March 6, 2024 20:53
Show Gist options
  • Save leporel/333ed93cd7181efd2eafbaec7ea72485 to your computer and use it in GitHub Desktop.
Save leporel/333ed93cd7181efd2eafbaec7ea72485 to your computer and use it in GitHub Desktop.
Work with HDR10 and HDR10+
#!/bin/bash
if ! command -v mediainfo --version &> /dev/null
then
echo 'Additions tools is not installed.' >&2
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-24_all.deb \
&& dpkg -i repo-mediaarea_1.0-24_all.deb \
&& apt-get update \
&& apt-get -y install mediainfo
rm repo-mediaarea_1.0-24_all.deb
fi
if ! command -v mkvmerge --version &> /dev/null
then
wget -O /usr/share/keyrings/gpg-pub-moritzbunkus.gpg https://mkvtoolnix.download/gpg-pub-moritzbunkus.gpg \
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/gpg-pub-moritzbunkus.gpg] https://mkvtoolnix.download/ubuntu/ jammy main" >> /etc/apt/sources.list.d/mkvtoolnix.list \
&& echo "deb-src [arch=amd64 signed-by=/usr/share/keyrings/gpg-pub-moritzbunkus.gpg] https://mkvtoolnix.download/ubuntu/ jammy main" >> /etc/apt/sources.list.d/mkvtoolnix.list \
&& apt-get update \
&& apt-get -y install mkvtoolnix
fi
if [ ! -f "hdr10plus_tool" ]; then
curl -LO "https://github.com/quietvoid/hdr10plus_tool/releases/download/1.6.0/hdr10plus_tool-1.6.0-x86_64-unknown-linux-musl.tar.gz"
tar -xzvf "./hdr10plus_tool-1.6.0-x86_64-unknown-linux-musl.tar.gz"
rm ./hdr10plus_tool-1.6.0-x86_64-unknown-linux-musl.tar.gz
fi
#!/bin/bash
Help()
{
# Display Help
echo "Encodes to the specified CRF while preserving HDR metadata, can downscale to 1080."
echo "Requires ffprobe, mkvpropedit, mediainfo, hdr10plus_tool."
echo
echo "Syntax: $0 [-q <15-40>] [-p <h265 preset>] [-s]"
echo "$0 -q 21 -p medium -s"
echo "options:"
echo "q CRF betwin 15 and 40"
echo "s scale to 1080p"
echo "p preset, default slow"
echo
exit
}
crf=0
SCALE=""
preset="slow"
[ $# -eq 0 ] && Help
while getopts "q:sp:" o; do
case "${o}" in
q)
if [ ${OPTARG} -gt 14 ]; then
crf=${OPTARG}
else
echo "CRF too small"
exit 0
fi
;;
s)
SCALE=("-vf" "scale=-1:1080") # дальше костыль еще по коду
;;
p)
preset=${OPTARG}
;;
\?) # incorrect option
echo "Error: Invalid option"
Help
;;
esac
done
shift $((OPTIND-1))
if [ "$crf" -lt 14 ]; then
echo "CRF not selected"
exit 0
fi
echo "CRF = $crf"
echo "Scale = ${SCALE[@]}"
echo "Preset = $preset"
log_file=`date +"%Y%m%dT%H%M"`
touch ./$log_file.log
exec &> >(tee -a "./$log_file.log")
# Pre-defined RGB & WP values for BT.2020
BT2020_xW=0.3127
BT2020_yW=0.3290
BT2020_xR=0.708
BT2020_yR=0.292
BT2020_xG=0.17
BT2020_yG=0.797
BT2020_xB=0.131
BT2020_yB=0.046
# Pre-defined RGB & WP values for P3-D65
P3D65_xW=0.3127
P3D65_yW=0.3290
P3D65_xR=0.680
P3D65_yR=0.320
P3D65_xG=0.265
P3D65_yG=0.690
P3D65_xB=0.150
P3D65_yB=0.060
shopt -s globstar
for i in ./**/*.*; do
echo "ITER = $i"
ext=${i##*.}
if [ "$ext" == "ts" -o "$ext" == "MP4" -o "$ext" == "mp4" -o "$ext" == "avi" -o "$ext" == "mkv" ]; then
if [[ "$i" == *"_k."* ]] || [ -f "${i%.*}_k.mkv" ]; then
echo "File found! Skip";
continue
fi
if [ -f "$i" ]; then
COLORS=$(ffprobe -show_streams -v error "$i" |egrep "^color_transfer|^color_space=|^color_primaries=" |head -3)
for C in $COLORS; do
if [[ "$C" = "color_space="* ]]; then
COLORSPACE=${C##*=}
elif [[ "$C" = "color_transfer="* ]]; then
COLORTRANSFER=${C##*=}
elif [[ "$C" = "color_primaries="* ]]; then
COLORPRIMARIES=${C##*=}
fi
done
colourSpace=$(mediainfo --Inform="Video;%MasteringDisplay_ColorPrimaries%" "$i")
height=`ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 "$i" | cut -d 'x' -f 1`
SCALEF=${SCALE[@]}
if [[ $height -gt 1700 ]] && [[ ! -z "$SCALE" ]]; then
SCALEF=("-vf" "zscale=-1:$(($height / 2)):filter=spline36")
fi
echo " COLORSPACE = $COLORSPACE ($colourSpace)
COLORTRANSFER = $COLORTRANSFER
COLORPRIMARIES = $COLORPRIMARIES"
if [ "${COLORSPACE}" = "bt2020nc" ] && [ "${COLORTRANSFER}" = "smpte2084" ] && [ "${COLORPRIMARIES}" = "bt2020" ]; then
enc_opts="aq-mode=2:colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc"
hdr10plus=$(ffmpeg -loglevel panic -i "$i" -c:v copy -vbsf hevc_mp4toannexb -f hevc - | ./hdr10plus_tool --verify extract - 2>&1)
if [[ $hdr10plus == *"File doesn't contain dynamic metadata"* ]]; then
echo "File doesn't contain HDR10+ dynamic metadata"
elif [[ $hdr10plus == *"Dynamic HDR10+ metadata detected"* ]]; then
hdr10plus_file="${i%.*}_metadata.json"
ffmpeg -i "$i" -c:v copy -vbsf hevc_mp4toannexb -f hevc - | ./hdr10plus_tool extract -o "$hdr10plus_file" -
echo "Dynamic HDR10+ metadata detected. Metadata saved to $hdr10plus_file"
enc_opts="aq-mode=2:colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc:hdr10=1:dhdr10-info=$hdr10plus_file"
else
echo "Unknown hdr10plus_tool output: $hdr10plus"
fi
ffmpeg -stats_period 3 -i "$i" ${SCALEF[@]} -max_muxing_queue_size 2064 -map 0 -c:a copy -c:s copy -c:v libx265 -preset "$preset" -crf "$crf" -profile:v main10 -pix_fmt yuv420p10le \
-x265-params "$enc_opts" "${i%.*}_k.mkv" || exit 1;
# Extract values from source video
colourSpace=$(mediainfo --Inform="Video;%MasteringDisplay_ColorPrimaries%" "$i")
luminance=$(mediainfo --Inform="Video;%MasteringDisplay_Luminance%" "$i")
maxCLL=$(mediainfo --Inform="Video;%MaxCLL%" "$i")
maxFALL=$(mediainfo --Inform="Video;%MaxFALL%" "$i")
# Print results from MediaInfo
echo "Colour Space: $colourSpace"
echo "Luminance: $luminance"
echo "MaxCLL: $maxCLL"
echo "MaxFALL: $maxFALL"
# Change IFS to split strings on spaces
IFS_old="$IFS"
IFS=' '
# Extract max and min lumanances
read -ra luminances <<< "$luminance"
minLuminance=${luminances[1]}
maxLuminance=${luminances[4]}
# Extract MaxCLL
read -ra maxcll <<< "$maxCLL"
maxCLL_noUnit=${maxcll[0]}
# Extract MaxFALL
read -ra maxfall <<< "$maxFALL"
maxFALL_noUnit=${maxfall[0]}
# Reset IFS
IFS=$IFS_old
# Print extracted values
echo "Minimum luminance: $minLuminance"
echo "Maximum luminance: $maxLuminance"
echo "MaxCLL: $maxCLL_noUnit"
echo "MaxFALL: $maxFALL_noUnit"
# Edit second input based on values extracted from MediaInfo
if [ "$colourSpace" == "BT.2020" ]; then
mkvpropedit "${i%.*}_k.mkv" --edit track:v1 --set chromaticity-coordinates-red-x="$BT2020_xR" \
--set chromaticity-coordinates-red-y="$BT2020_yR" \
--set chromaticity-coordinates-green-x="$BT2020_xG" \
--set chromaticity-coordinates-green-y="$BT2020_yG" \
--set chromaticity-coordinates-blue-x="$BT2020_xB" \
--set chromaticity-coordinates-blue-y="$BT2020_yB" \
--set white-coordinates-x="$BT2020_xW" \
--set white-coordinates-y="$BT2020_yW" \
--set max-luminance="$maxLuminance" \
--set min-luminance="$minLuminance" \
--set max-content-light="$maxCLL_noUnit" \
--set max-frame-light="$maxFALL_noUnit"
elif [ "$colourSpace" == "Display P3" ]; then
mkvpropedit "${i%.*}_k.mkv" --edit track:v1 --set chromaticity-coordinates-red-x="$P3D65_xR" \
--set chromaticity-coordinates-red-y="$P3D65_yR" \
--set chromaticity-coordinates-green-x="$P3D65_xG" \
--set chromaticity-coordinates-green-y="$P3D65_yG" \
--set chromaticity-coordinates-blue-x="$P3D65_xB" \
--set chromaticity-coordinates-blue-y="$P3D65_yB" \
--set white-coordinates-x="$P3D65_xW" \
--set white-coordinates-y="$P3D65_yW" \
--set max-luminance="$maxLuminance" \
--set min-luminance="$minLuminance" \
--set max-content-light="$maxCLL_noUnit" \
--set max-frame-light="$maxFALL_noUnit"
else
echo "Unknown colour space: $colourSpace"
fi
else
echo '"$i" SDR file, no need to convert!'
fi
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment