Last active
March 6, 2024 20:53
-
-
Save leporel/333ed93cd7181efd2eafbaec7ea72485 to your computer and use it in GitHub Desktop.
Work with HDR10 and HDR10+
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/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 |
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/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