mzenc/metric_encoder.sh

118 lines
6 KiB
Bash
Raw Permalink Normal View History

2024-07-01 22:08:22 +00:00
#!/usr/bin/env bash
#
# MIT License
#
# Copyright (c) 2024 Mitsudori
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Use: ssim_optimizer.sh input.ext output.ext (to compare) bitrate_in_kbps (or none).
# The extension can be what ffmpeg was compiled with.
# The script will not work with downscale\upscale encode unless under comparative function (not implemented yet).
# Does not work with interlaced source yet.
# Depends: Bash, FFmpeg, FFprobe, Gnu AWK, Grep, bc, sed.
# Matroska may broke the results, because sometimes when FFmpeg encodes Matroska, inserts a frame plus.
# If no video correlation as param, the script will made a encode themselve.
# The execution under cygwin in Windows should not work for RAMdisk mounting, as you should do it manually.
# Not tested on Mac, BSD.
constantssim=0.979889
constantvmaf=96
qcmp=0.50
crf=16
ext=`echo "$1" | cut -d'.' -f2`
safeparams="-me_method hex -refs 8 -bf 8 -b_strategy 2 -trellis 2 -aq-mode 2 -x264-params partitions=all"
killparms="-me_method umh -me_range 32 -refs 16 -bf 16 -b_strategy 1 -trellis 2 -aq-mode 2 -x264-params partitions=all:subme=9:no-dct-decimate=1:no-fast-pskip=1"
if [ "$ext" = "yuv" ]; then
read -p "Enter size in widthxheight for YUV input." ryuv;
yuvin="-s $ryuv -r 25 -pix_fmt yuv420p";
fi
if [ -z "$1" ]; then
printf "Arguments: ssim_optimizer.sh 'reference.mp4' 'correlation.mp4' 'initial_bitrate_int'\n\t\t\t\tAt least the reference needs to be insert." ; exit
fi
if [ -z "$3" ] && [ ! -n "$2" ]; then
read -p "Proceed to determine qcomp and qpfile (more precise encode)? (y/N)" q1;
if [[ $q1 == [yY] || $q1 == [yY][eE][sS] ]]; then
qcmp=`ffmpeg -i $1 -c:v libx264 -loglevel debug -qp 0 -g 1 -preset superfast -f null -y /dev/null 2>&1 | grep -oP "(?<=size=)\d+(?= b)" | gawk '!_[$0]++{z[++c]=$0}{l[NR]=$0}END{asort(z);for (a in l) for (b in z) if (l[a] == z[b]) {print b; break}}' | gawk '{q=$0;z[NR]=q;sum+=q}END{m=sum/NR;for (a in z) stdp+=(z[a]-m)**2;std=sqrt(stdp/NR); print std/m}'`;
if [ -n "$yuvin" ]; then
echo "QPfile function does not apply to yuv input."; else
ffprobe $1 -show_frames -print_format csv -show_entries stream=pict_type | gawk -F"," '$19=="I"{print $20 " " $19 " -1" }' > qpfile_bruto.txt;
telos=`gawk 'END{ print NR; }' qpfile_bruto.txt;`
fi
if [ $qcmp -gt 0.58 ]; then crf=$((crf-4)); fi
fi
fi
if [ -n "$1" ] && [ -n "$2" ] ; then
probe="-select_streams v -show_entries stream=width -of default=noprint_wrappers=1";
a=`ffprobe -v error $probe "$2" | grep -Eo '[0-9]{1,4}' -`; b=`ffprobe -v error $probe "$1" | grep -Eo '[0-9]{1,4}' -`
if [ "$a" != "$b" ]; then
c=`ffprobe -v error $probe "$1" | grep -Eo '[0-9]{1,4}' -`
parsedssim=`ffmpeg -hide_banner -i "$2" -i "$1" -filter_complex "[0:v]scale=$b:$c[v0r];[1:v][v0r] ssim" -f null - 2>&1 | grep -oP "(?<=All:).*(?= )"`
if (( $(echo "$parsedssim < $constantssim" |bc -l) )); then
printf "\nYour correlation video is not optimized, with a round SSIM value of $parsedssim\n";
else
printf "\nYour correlation video is optimized, with a round SSIM value of $parsedssim\n";
fi
else
parsedssim=`ffmpeg -hide_banner -i $1 -i $2 -lavfi ssim -f null - 2>&1 | grep -oP "(?<=All:).*(?= )"`;
if (( $(echo "$parsedssim < $constantssim" |bc -l) )) ; then
printf "\nYour correlation video is not SSIM optimized, with a round SSIM value of $parsedssim";
else
printf "\nYour video is optimized, with a round SSIM value of $parsedssim";
fi
parsedvmaf=`ffmpeg -hide_banner -i $1 -i $2 -lavfi libvmaf -f null - 2>&1 | grep -oP "(?<=score: ).*"`;
if (( $(echo "$parsedvmaf < $constantvmaf" |bc -l) )) ; then
printf "\nYour correlation video is not VMAF optimized, with a round VMAF value of $parsedvmaf";
else
printf "\nYour video is VMAF optimized, with a round VMAF value of $parsedvmaf";
fi
exit
fi
fi
encode_function()
{
read -p "Procede to encode?" q2;
if [[ $q2 == [yY] || $q2 == [yY][eE][sS] ]]; then
fps=`ffprobe -hide_banner $1 2>&1 | grep -oP "(?<= )\d+(\.\d+)*(?= fps)" | gawk '{printf("%d\n",$1 + 0.5)}'`
gop=$((10*$fps));
scc=$((3*$fps));
# for ((i=1;i<=telos;i++)); do
# flag=$((flag+1));
# aframe`gawk -v var="$flag" 'NR==var{ print $1; }' qpfile_bruto.txt`
# flag=$((flag+1));
# zframe`gawk -v var="$flag" 'NR==var{ print $1; }' qpfile_bruto.txt`
# done;
# gawk 'NR==1{ print $1; }' qpfile_bruto.txt
if [ $qcmp -gt 0.58 ]; then
brate=`ffmpeg -i $1 -an -c:v libx264 -fastfirstpass 0 -pass 1 -qcomp $qcmp -g $gop -sc_threshold $scc -rc-lookahead $(($scc+$fps)) -crf $crf $killparms -f null /dev/null | grep -oP "(?<=kb/s:).*"`
ffmpeg -i $1 -an -c:v libx264 -pass 2 -qcomp $qcmp -g $gop -sc_threshold $scc -rc-lookahead $(($scc+$fps)) -b:v "$brate"k -y encode.mp4
else
brate=`ffmpeg -i $1 -an -c:v libx264 -fastfirstpass 0 -pass 1 -qcomp $qcmp -g $gop -sc_threshold $scc -rc-lookahead $(($scc+$fps)) -crf $crf $safeparams -f null /dev/null | grep -oP "(?<=kb/s:).*"`
ffmpeg -i $1 -an -c:v libx264 -pass 2 -qcomp $qcmp -g $gop -sc_threshold $scc -rc-lookahead $(($scc+$fps)) -b:v "$brate"k -y encode.mp4
fi
else
exit;
fi
}
encode_function