mise à jours HEVC pour 1080p
This commit is contained in:
Executable
+177
@@ -0,0 +1,177 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# CALIBRATION CRF — HEVC
|
||||
#
|
||||
# Encode un même extrait de rush à plusieurs CRF et mesure la qualité
|
||||
# objective (VMAF si dispo, sinon SSIM) + la taille obtenue.
|
||||
# Objectif : choisir CRF_HD / CRF_SD sur TES images plutôt qu'au doigt mouillé.
|
||||
#
|
||||
# Usage :
|
||||
# ./bench_crf.sh rushs/GH011182.MP4 [start_sec] [duree_sec]
|
||||
#
|
||||
# Variables :
|
||||
# CODEC=hevc palier 1080p HEVC (défaut)
|
||||
# CODEC=h264 palier 720p H.264
|
||||
# HEIGHT=1080 hauteur cible du test (1080 ou 720)
|
||||
# CRFS="22 24 26 28 30 32"
|
||||
# PRESET=medium
|
||||
# ENCODER=cpu|vt (en mode vt, CRFS est interprété comme des valeurs -q:v)
|
||||
# KEEP=1 conserve les fichiers encodés pour comparaison visuelle
|
||||
#
|
||||
# Les deux paliers du site se calibrent séparément — les échelles de CRF
|
||||
# de x264 et x265 ne sont PAS comparables :
|
||||
# ./bench_crf.sh rushs/GH011182.MP4 30 20 # 1080p HEVC
|
||||
# CODEC=h264 HEIGHT=720 CRFS="20 22 24 26" ./bench_crf.sh rushs/GH011182.MP4 30 20
|
||||
#
|
||||
# Choisis un extrait REPRÉSENTATIF : du mouvement, de la végétation, du
|
||||
# ciel dégradé. Un plan fixe sur un mur donnera un CRF trop optimiste.
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SRC="${1:?Usage: ./bench_crf.sh <fichier_rush> [start_sec] [duree_sec]}"
|
||||
START="${2:-30}"
|
||||
DUR="${3:-20}"
|
||||
|
||||
CODEC="${CODEC:-hevc}"
|
||||
HEIGHT="${HEIGHT:-1080}"
|
||||
if [ "$CODEC" = "h264" ]; then
|
||||
CRFS="${CRFS:-18 20 22 24 26 28}"
|
||||
else
|
||||
CRFS="${CRFS:-22 24 26 28 30 32}"
|
||||
fi
|
||||
PRESET="${PRESET:-medium}"
|
||||
ENCODER="${ENCODER:-cpu}"
|
||||
KEEP="${KEEP:-0}"
|
||||
|
||||
[ -f "$SRC" ] || { echo "❌ Fichier introuvable : $SRC"; exit 1; }
|
||||
|
||||
WORK="$(mktemp -d "${TMPDIR:-/tmp}/bench_crf.XXXXXX")"
|
||||
cleanup() { [ "$KEEP" = "1" ] || rm -rf "$WORK"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
# --- Détection de la métrique disponible -------------------------------------
|
||||
FILTERS_LIST="$(ffmpeg -hide_banner -filters 2>/dev/null)"
|
||||
if printf '%s' "$FILTERS_LIST" | grep -q "libvmaf"; then
|
||||
METRIC="vmaf"
|
||||
else
|
||||
METRIC="ssim"
|
||||
echo "ℹ️ libvmaf absent de ton ffmpeg → repli sur SSIM."
|
||||
echo " (VMAF est bien plus parlant : brew install ffmpeg l'inclut normalement)"
|
||||
fi
|
||||
|
||||
# --- Référence : extrait copié sans réencodage -------------------------------
|
||||
REF="$WORK/ref.mp4"
|
||||
echo "✂️ Extraction de la référence (${DUR}s à partir de ${START}s)…"
|
||||
ffmpeg -nostdin -hide_banner -loglevel error -y \
|
||||
-ss "$START" -i "$SRC" -t "$DUR" \
|
||||
-map 0:v:0 -c copy -tag:v hvc1 "$REF" 2>/dev/null
|
||||
|
||||
if [ ! -s "$REF" ]; then
|
||||
echo "⚠️ Copie directe impossible, réencodage sans perte visible de la référence…"
|
||||
ffmpeg -nostdin -hide_banner -loglevel error -y \
|
||||
-ss "$START" -i "$SRC" -t "$DUR" \
|
||||
-map 0:v:0 -c:v libx265 -preset ultrafast -x265-params lossless=1 \
|
||||
-tag:v hvc1 "$REF"
|
||||
fi
|
||||
|
||||
# Si on teste une hauteur inférieure à la source, la référence de comparaison
|
||||
# doit être à la MÊME définition que le test : on fabrique une référence scalée.
|
||||
SRC_H=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$REF" | head -1)
|
||||
REF_CMP="$REF"
|
||||
if [ "$SRC_H" != "$HEIGHT" ]; then
|
||||
REF_CMP="$WORK/ref_${HEIGHT}.mp4"
|
||||
echo "📐 Référence de comparaison rescalée en ${HEIGHT}p…"
|
||||
ffmpeg -nostdin -hide_banner -loglevel error -y -i "$REF" \
|
||||
-vf "scale=-2:${HEIGHT}:flags=lanczos" \
|
||||
-c:v libx265 -preset ultrafast -x265-params lossless=1 -tag:v hvc1 "$REF_CMP"
|
||||
fi
|
||||
|
||||
REF_DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$REF" | cut -d. -f1)
|
||||
REF_DUR=${REF_DUR:-$DUR}
|
||||
|
||||
echo
|
||||
echo "🎯 Palier testé : ${HEIGHT}p en $(echo "$CODEC" | tr a-z A-Z)"
|
||||
printf "%-6s %10s %12s %10s %10s\n" "CRF" "Taille" "Débit" "$(echo "$METRIC" | tr a-z A-Z)" "Encodage"
|
||||
printf "%s\n" "--------------------------------------------------------"
|
||||
|
||||
BEST_LINE=""
|
||||
for crf in $CRFS; do
|
||||
OUT="$WORK/test_crf${crf}.mp4"
|
||||
|
||||
if [ "$CODEC" = "h264" ]; then
|
||||
if [ "$ENCODER" = "vt" ]; then
|
||||
VOPTS=(-c:v h264_videotoolbox -profile:v high -q:v "$crf" -allow_sw 1 -realtime 0)
|
||||
else
|
||||
VOPTS=(-c:v libx264 -preset "$PRESET" -profile:v high -level 4.0 -crf "$crf"
|
||||
-g 120 -keyint_min 24)
|
||||
fi
|
||||
else
|
||||
if [ "$ENCODER" = "vt" ]; then
|
||||
VOPTS=(-c:v hevc_videotoolbox -profile:v main -q:v "$crf" -allow_sw 1 -realtime 0 -tag:v hvc1)
|
||||
else
|
||||
VOPTS=(-c:v libx265 -preset "$PRESET" -profile:v main -crf "$crf"
|
||||
-x265-params "keyint=120:min-keyint=24:aq-mode=3" -tag:v hvc1)
|
||||
fi
|
||||
fi
|
||||
|
||||
T0=$(date +%s)
|
||||
ffmpeg -nostdin -hide_banner -loglevel error -y -i "$REF" \
|
||||
-map 0:v:0 "${VOPTS[@]}" \
|
||||
-vf "scale=-2:${HEIGHT}:flags=lanczos" \
|
||||
-pix_fmt yuv420p \
|
||||
-color_primaries bt709 -color_trc bt709 -colorspace bt709 \
|
||||
-an "$OUT" 2>/dev/null
|
||||
T1=$(date +%s)
|
||||
ENC_TIME=$((T1 - T0))
|
||||
|
||||
[ -s "$OUT" ] || { printf "%-6s %10s\n" "$crf" "ÉCHEC"; continue; }
|
||||
|
||||
BYTES=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT")
|
||||
MB=$(awk -v b="$BYTES" 'BEGIN{printf "%.1f", b/1048576}')
|
||||
KBPS=$(awk -v b="$BYTES" -v d="$REF_DUR" 'BEGIN{printf "%.0f", b*8/1000/d}')
|
||||
|
||||
# --- Mesure de qualité ---
|
||||
if [ "$METRIC" = "vmaf" ]; then
|
||||
SCORE=$(ffmpeg -nostdin -hide_banner -loglevel info -i "$OUT" -i "$REF_CMP" \
|
||||
-lavfi "[0:v]setpts=PTS-STARTPTS[d];[1:v]setpts=PTS-STARTPTS[r];[d][r]libvmaf" \
|
||||
-f null - 2>&1 | grep -oE "VMAF score: [0-9.]+" | tail -1 | grep -oE "[0-9.]+")
|
||||
SCORE=$(awk -v s="${SCORE:-0}" 'BEGIN{printf "%.2f", s}')
|
||||
else
|
||||
SCORE=$(ffmpeg -nostdin -hide_banner -loglevel info -i "$OUT" -i "$REF_CMP" \
|
||||
-lavfi "[0:v]setpts=PTS-STARTPTS[d];[1:v]setpts=PTS-STARTPTS[r];[d][r]ssim" \
|
||||
-f null - 2>&1 | grep -oE "All:[0-9.]+" | tail -1 | cut -d: -f2)
|
||||
SCORE=$(awk -v s="${SCORE:-0}" 'BEGIN{printf "%.4f", s}')
|
||||
fi
|
||||
|
||||
printf "%-6s %9s M %9s k %10s %8s s\n" "$crf" "$MB" "$KBPS" "$SCORE" "$ENC_TIME"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "📖 Comment lire ce tableau"
|
||||
if [ "$METRIC" = "vmaf" ]; then
|
||||
cat <<'EOF'
|
||||
VMAF ≥ 95 : indiscernable de la source à distance normale
|
||||
VMAF 90-95 : excellent, cible raisonnable pour un site de voyage
|
||||
VMAF 85-90 : bon, quelques artefacts visibles en plein écran
|
||||
VMAF < 85 : dégradation nette sur les scènes chargées
|
||||
→ Prends le CRF le plus ÉLEVÉ qui reste au-dessus de ton seuil.
|
||||
Le gain de taille entre deux CRF consécutifs s'écrase vite ;
|
||||
dès que la courbe de qualité plonge, tu es allé trop loin.
|
||||
EOF
|
||||
else
|
||||
cat <<'EOF'
|
||||
SSIM ≥ 0.98 : très bon
|
||||
SSIM 0.96-0.98 : correct pour du web
|
||||
SSIM < 0.95 : dégradation visible
|
||||
→ Prends le CRF le plus ÉLEVÉ qui reste au-dessus de ton seuil.
|
||||
⚠️ SSIM est moins fiable que VMAF sur des images très texturées
|
||||
(feuillage, eau, gravier) : vérifie AUSSI à l'œil avec KEEP=1.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$KEEP" = "1" ]; then
|
||||
echo
|
||||
echo "📁 Fichiers conservés dans : $WORK"
|
||||
echo " Compare à l'œil, en plein écran, sur les plans les plus chargés."
|
||||
fi
|
||||
Reference in New Issue
Block a user