#!/bin/bash
# =============================================================================
# COMPRESSION ROAD-TRIP — STRATÉGIE HYBRIDE + ENCODAGE PARALLÈLE
# Source : GoPro Hero 9, rushs en HEVC 1080p
#
# Sorties (noms de fichiers INCHANGÉS par rapport à l'ancien script) :
# .mp4 1080p natif, HEVC/H.265, tag hvc1
# → gros fichier, là où les 40 % d'économie comptent
# _mobile.mp4 720p, H.264, tag avc1, profil High
# → fichier chargé par défaut, se lit sur TOUS
# les navigateurs sans exception
#
# PARALLÉLISME
# Deux niveaux, tous deux actifs par défaut :
# 1. Les deux paliers d'un même rush sont indépendants → lancés ensemble.
# 2. JOBS rushs traités simultanément (pool géré par xargs -P).
# Le nombre de threads par encodeur est borné pour ne pas surcharger :
# x265 sature vite (scaling sublinéaire au-delà de ~6-8 threads), donc
# plusieurs encodages courts valent mieux qu'un seul très parallélisé.
#
# Variables d'environnement :
# JOBS=N nombre de rushs en parallèle
# (défaut : cœurs performance / 4, minimum 1)
# THREADS=N threads par encodeur (défaut : cœurs / JOBS)
# ENCODER=cpu (défaut) libx265 + libx264 — meilleure qualité/octet
# ENCODER=vt VideoToolbox — ~10x plus rapide sur Apple Silicon
# (⚠️ le parallélisme n'y sert à rien : le bloc média
# de la puce est une ressource fixe → JOBS=1 forcé)
# PRESET=medium preset CPU : ultrafast…placebo
# CRF_HEVC=26 qualité du 1080p HEVC (échelle x265)
# CRF_H264=25 qualité du 720p H.264 (échelle x264 — PAS la même !)
# VTQ_HD=60 VTQ_SD=58 qualité VideoToolbox (1-100, plus haut = meilleur)
# FPS_MAX=60 plafonne le framerate (un rush 30p reste en 30p,
# un rush 120p/240p est ramené à 60)
# FORCE=1 réencode même si le fichier de sortie existe
#
# Exemples :
# ./compress.sh mon-voyage
# JOBS=4 ./compress.sh mon-voyage
# JOBS=1 ./compress.sh mon-voyage # séquentiel + barre de progression
# ENCODER=vt ./compress.sh mon-voyage
# =============================================================================
set -uo pipefail
shopt -s nocaseglob nullglob
# --- 0. MODE INTERNE ---------------------------------------------------------
# Quand le script est rappelé par xargs pour traiter UN rush, il arrive ici.
if [ "${1:-}" = "--encode-one" ]; then
INTERNAL=1
BASE_DIR="${RT_BASE_DIR:-.}"
TARGET="${2:?--encode-one attend un fichier}"
else
INTERNAL=0
BASE_DIR="${1:-.}"
fi
SOURCE_DIR="$BASE_DIR/rushs"
DEST_DIR="$BASE_DIR/videos"
# --- 1. CONFIGURATION --------------------------------------------------------
ENCODER="${ENCODER:-cpu}"
PRESET="${PRESET:-medium}"
CRF_HEVC="${CRF_HEVC:-26}"
CRF_H264="${CRF_H264:-25}"
VTQ_HD="${VTQ_HD:-60}"
VTQ_SD="${VTQ_SD:-58}"
FPS_MAX="${FPS_MAX:-60}"
FORCE="${FORCE:-0}"
# Détection des cœurs. Sur Apple Silicon on ne compte que les cœurs
# PERFORMANCE : les cœurs d'efficience sont si lents qu'ils deviennent des
# traînards dans un pool x265 et pénalisent l'encodage entier.
detect_cores() {
local c=""
c=$(sysctl -n hw.perflevel0.logicalcpu 2>/dev/null); [ -n "$c" ] && { echo "$c"; return; }
c=$(sysctl -n hw.logicalcpu 2>/dev/null); [ -n "$c" ] && { echo "$c"; return; }
c=$(nproc 2>/dev/null); [ -n "$c" ] && { echo "$c"; return; }
echo 4
}
CORES="${CORES:-$(detect_cores)}"
# En VideoToolbox, le parallélisme ne sert à rien (un seul moteur d'encodage
# matériel sur les puces M de base) — sauf si l'utilisateur force JOBS.
JOBS_EXPLICIT=0
[ -n "${JOBS+x}" ] && JOBS_EXPLICIT=1
if [ "$ENCODER" = "vt" ] && [ "$JOBS_EXPLICIT" = "0" ]; then
JOBS=1
else
JOBS="${JOBS:-$(( CORES / 4 ))}"
fi
[ "$JOBS" -lt 1 ] 2>/dev/null && JOBS=1
[ -z "$JOBS" ] && JOBS=1
THREADS="${THREADS:-$(( CORES / JOBS ))}"
[ "$THREADS" -lt 2 ] 2>/dev/null && THREADS=2
# Le palier 720p H.264 est 3 à 5× plus rapide que le 1080p HEVC : on lui donne
# moitié moins de threads. Il finit largement avant, et la fenêtre pendant
# laquelle les deux paliers tournent ensemble ne surcharge pas la machine.
THREADS_SD=$(( THREADS / 2 ))
[ "$THREADS_SD" -lt 2 ] && THREADS_SD=2
# --- 2. FONCTION D'ENCODAGE --------------------------------------------------
# encode