gallery_server_client_s190js/test_fn/prova_vod_anticipata.sh
2026-10-26 11:00:14 +01:00

374 lines
6.9 KiB
Bash
Executable file

#!/usr/bin/env bash
set -eu
PROJECT="/home/nvme/dockerdata/prove/s157js"
VIDEO="${1:-$PROJECT/public/photos/Fabio/original/2017Irlanda19-29ago/VID-20260920-WA0000.mp4}"
OUT="${2:-/tmp/prova-wait}"
PORT="${PORT:-9999}"
SEGMENT_TIMEOUT="${SEGMENT_TIMEOUT:-300}"
FFMPEG="/usr/local/bin/ffmpeg"
FFPROBE="/usr/local/bin/ffprobe"
WAIT_SERVER="$PROJECT/hls_cors_server_wait.py"
SERVER_PID=""
FFMPEG_PID=""
cleanup() {
echo
echo "Arresto dei processi..."
if [ -n "$FFMPEG_PID" ]; then
kill "$FFMPEG_PID" 2>/dev/null || true
fi
if [ -n "$SERVER_PID" ]; then
kill "$SERVER_PID" 2>/dev/null || true
fi
wait 2>/dev/null || true
}
trap cleanup EXIT INT TERM
if [ ! -f "$VIDEO" ]; then
echo "ERRORE: video non trovato:"
echo "$VIDEO"
exit 1
fi
if [ ! -f "$WAIT_SERVER" ]; then
echo "ERRORE: server con attesa non trovato:"
echo "$WAIT_SERVER"
echo
echo "Deve esistere hls_cors_server_wait.py nel progetto."
exit 1
fi
echo "=================================================="
echo "PROVA VOD ANTICIPATA"
echo "=================================================="
echo "Video: $VIDEO"
echo "Output: $OUT"
echo "Porta: $PORT"
echo "Timeout: $SEGMENT_TIMEOUT secondi"
echo
rm -rf "$OUT"
mkdir -p "$OUT"
echo "Avvio server HTTP con attesa dei segmenti..."
python3 "$WAIT_SERVER" \
"$OUT" \
--port "$PORT" \
--segment-timeout "$SEGMENT_TIMEOUT" &
SERVER_PID=$!
sleep 0.5
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "ERRORE: il server HTTP non è partito."
exit 1
fi
echo
echo "Avvio FFmpeg in modalità realtime..."
echo
"$FFMPEG" \
-hide_banner \
-loglevel warning \
-y \
-noautorotate \
-i "$VIDEO" \
-map 0:v:0 \
-map '0:a:0?' \
-vf "scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,format=yuv420p" \
-metadata:s:v:0 rotate=0 \
-c:v libx264 \
-profile:v high \
-level:v 4.1 \
-preset ultrafast \
-tune zerolatency \
-b:v 5000k \
-maxrate 6000k \
-bufsize 12000k \
-r 30 \
-g 90 \
-keyint_min 90 \
-sc_threshold 0 \
-c:a aac \
-b:a 160k \
-ac 2 \
-ar 48000 \
-f hls \
-hls_time 3 \
-hls_list_size 0 \
-hls_playlist_type event \
-hls_segment_type mpegts \
-hls_flags independent_segments \
-hls_segment_filename "$OUT/segment_%05d.ts" \
"$OUT/internal.m3u8" &
FFMPEG_PID=$!
echo "FFmpeg PID: $FFMPEG_PID"
echo
echo "Attendo la chiusura dei primi 3 segmenti..."
# La comparsa del segmento 3 indica che i segmenti 0, 1 e 2
# sono già stati chiusi da FFmpeg.
while [ ! -s "$OUT/segment_00003.ts" ]; do
if ! kill -0 "$FFMPEG_PID" 2>/dev/null; then
echo "ERRORE: FFmpeg è terminato prima di produrre 3 segmenti."
wait "$FFMPEG_PID" || true
exit 1
fi
COUNT="$(
find "$OUT" \
-maxdepth 1 \
-type f \
-name 'segment_*.ts' \
2>/dev/null \
| wc -l \
| tr -d ' '
)"
echo "Segmenti visibili: $COUNT"
sleep 0.2
done
echo
echo "Primi 3 segmenti completi."
echo "Creazione della playlist VOD anticipata..."
"$FFPROBE" \
-v error \
-show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 \
"$VIDEO" \
> "$OUT/.duration.txt"
python3 - "$OUT" <<'PY'
from pathlib import Path
import math
import sys
out = Path(sys.argv[1]).resolve()
duration_file = out / ".duration.txt"
playlist = out / "cast.m3u8"
temporary = out / "cast.m3u8.tmp"
duration = float(
duration_file.read_text(
encoding="utf-8"
).strip()
)
segment_duration = 3.0
total_segments = math.ceil(
duration / segment_duration
)
last_duration = (
duration -
segment_duration * (total_segments - 1)
)
if last_duration <= 0:
last_duration = segment_duration
lines = [
"#EXTM3U",
"#EXT-X-VERSION:6",
"#EXT-X-TARGETDURATION:3",
"#EXT-X-MEDIA-SEQUENCE:0",
"#EXT-X-START:TIME-OFFSET=0,PRECISE=YES",
"#EXT-X-PLAYLIST-TYPE:VOD",
"#EXT-X-INDEPENDENT-SEGMENTS",
]
for index in range(total_segments):
current_duration = (
segment_duration
if index < total_segments - 1
else last_duration
)
lines.append(
f"#EXTINF:{current_duration:.6f},"
)
lines.append(
f"segment_{index:05d}.ts"
)
lines.append("#EXT-X-ENDLIST")
temporary.write_text(
"\n".join(lines) + "\n",
encoding="utf-8",
)
temporary.replace(playlist)
real_segments = len(
list(out.glob("segment_*.ts"))
)
print()
print("Playlist VOD anticipata pubblicata")
print(f"File: {playlist}")
print(f"Durata: {duration:.6f} secondi")
print(f"Segmenti dichiarati: {total_segments}")
print(f"Segmenti già presenti: {real_segments}")
print(f"Ultimo EXTINF: {last_duration:.6f}")
PY
IP_ADDRESS="$(
hostname -I \
| awk '{print $1}'
)"
echo
echo "=================================================="
echo "PLAYLIST PRONTA"
echo "=================================================="
echo
echo "URL:"
echo "http://$IP_ADDRESS:$PORT/cast.m3u8"
echo
echo "Dal Mac esegui:"
echo
echo "catt -d 192.168.1.15 stop"
echo "catt -d 192.168.1.15 cast \\"
echo " \"http://$IP_ADDRESS:$PORT/cast.m3u8\""
echo
echo "Per provare un seek futuro:"
echo
echo "catt -d 192.168.1.15 seek 90"
echo
echo "Il server mostrerà WAIT e READY se il segmento"
echo "richiesto non è ancora disponibile."
echo "=================================================="
echo
# Attende il completamento della transcodifica.
set +e
wait "$FFMPEG_PID"
FFMPEG_STATUS=$?
set -e
FFMPEG_PID=""
if [ "$FFMPEG_STATUS" -eq 0 ]; then
echo
echo "FFmpeg ha completato la conversione."
echo "Pubblicazione della playlist reale definitiva..."
python3 - "$OUT" <<'PY'
from pathlib import Path
import sys
out = Path(sys.argv[1]).resolve()
internal = out / "internal.m3u8"
public = out / "cast.m3u8"
temporary = out / "cast.m3u8.final.tmp"
if not internal.is_file():
raise SystemExit(
f"ERRORE: playlist interna mancante: {internal}"
)
content = internal.read_text(
encoding="utf-8"
)
if "#EXT-X-ENDLIST" not in content:
raise SystemExit(
"ERRORE: la playlist interna non contiene ENDLIST."
)
content = content.replace(
"#EXT-X-PLAYLIST-TYPE:EVENT",
"#EXT-X-PLAYLIST-TYPE:VOD",
)
start_marker = (
"#EXT-X-START:TIME-OFFSET=0,PRECISE=YES"
)
if start_marker not in content:
lines = content.splitlines()
version_index = next(
(
index
for index, line in enumerate(lines)
if line.startswith("#EXT-X-VERSION:")
),
0,
)
lines.insert(
version_index + 1,
start_marker,
)
content = "\n".join(lines) + "\n"
elif not content.endswith("\n"):
content += "\n"
temporary.write_text(
content,
encoding="utf-8",
)
temporary.replace(public)
print(f"Playlist finale pubblicata: {public}")
PY
else
echo
echo "ERRORE: FFmpeg è terminato con codice $FFMPEG_STATUS"
fi
echo
echo "Il server HTTP resta attivo."
echo "Premi Ctrl+C per terminare la prova."
wait "$SERVER_PID"