scripts/bisect-mpv.sh
187 Zeilen · 7.7 KB · Shell
| 1 | |
| 2 | # =========================================================================== |
| 3 | # Findet heraus, WELCHE mpv-Option (oder welcher Ablauf) einen Haenger |
| 4 | # ausloest. Statt zu raten wird jede Variante gemessen. |
| 5 | # |
| 6 | # Aufruf: sudo /opt/tvwall/scripts/bisect-mpv.sh /var/lib/tvwall/media/<video> |
| 7 | # |
| 8 | # Der Bildschirm flackert waehrenddessen - das ist normal, jede Variante |
| 9 | # spielt acht Sekunden. Der Dienst wird angehalten und danach neu gestartet. |
| 10 | # =========================================================================== |
| 11 | set -uo pipefail |
| 12 | |
| 13 | CONF="/etc/tvwall/tvwall.conf" |
| 14 | LIMIT=25 # Sekunden, nach denen eine Variante als "haengt" gilt |
| 15 | PLAYSEC=8 # so lange soll jede Variante spielen |
| 16 | |
| 17 | hdr() { printf '\n\033[1;36m── %s ──────────────────────────\033[0m\n' "$*"; } |
| 18 | ok() { printf ' \033[1;32mLAEUFT\033[0m %-46s %ss\n' "$1" "$2"; } |
| 19 | bad() { printf ' \033[1;31mHAENGT\033[0m %-46s Abbruch nach %ss\n' "$1" "$2"; } |
| 20 | odd() { printf ' \033[1;33mFEHLER\033[0m %-46s Code %s\n' "$1" "$2"; } |
| 21 | note() { printf ' %s\n' "$*"; } |
| 22 | die() { printf '\033[1;31mFehler:\033[0m %s\n' "$*" >&2; exit 1; } |
| 23 | |
| 24 | [[ $EUID -eq 0 ]] || die "bitte mit sudo starten" |
| 25 | |
| 26 | VIDEO="${1:-}" |
| 27 | [[ -n "$VIDEO" && -f "$VIDEO" ]] || die "Bitte eine Videodatei angeben: |
| 28 | sudo $0 /var/lib/tvwall/media/<datei>.mp4" |
| 29 | |
| 30 | RUN_USER="tvwall"; STATE_DIR="/var/lib/tvwall"; DISPLAY_MODE="auto" |
| 31 | [[ -f "$CONF" ]] && { set -a; . "$CONF" 2>/dev/null; set +a; } |
| 32 | RUN_USER="${RUN_USER:-tvwall}"; STATE_DIR="${STATE_DIR:-/var/lib/tvwall}" |
| 33 | RUN_UID="$(id -u "$RUN_USER" 2>/dev/null)" || die "Benutzer $RUN_USER gibt es nicht" |
| 34 | |
| 35 | # --- Umgebung wie beim Dienst --------------------------------------------- |
| 36 | ENV_ARGS=(XDG_RUNTIME_DIR="/run/user/$RUN_UID") |
| 37 | VO_ARGS=(--vo=gpu) |
| 38 | if ! ls "/run/user/$RUN_UID"/wayland-* >/dev/null 2>&1 && [[ -z "${DISPLAY:-}" ]]; then |
| 39 | VO_ARGS=(--vo=gpu --gpu-context=drm) # kein Desktop -> direkt auf den Schirm |
| 40 | fi |
| 41 | |
| 42 | # Die Optionen, die der Dienst setzt (ohne --idle/--loop, sonst endet nichts). |
| 43 | SERVICE_ARGS=( |
| 44 | --fullscreen --force-window=yes --keep-open=no |
| 45 | --image-display-duration=2 |
| 46 | --no-osc --osd-level=0 --cursor-autohide=always |
| 47 | --no-input-default-bindings --input-vo-keyboard=no |
| 48 | --stop-screensaver=yes --no-input-terminal |
| 49 | "${VO_ARGS[@]}" |
| 50 | ) |
| 51 | |
| 52 | # --- Testmaterial ---------------------------------------------------------- |
| 53 | IMAGE="$(find "$STATE_DIR/media" -maxdepth 1 -type f \ |
| 54 | \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \) | sort | head -1)" |
| 55 | TMP="$(mktemp -d)" |
| 56 | trap 'rm -rf "$TMP"' EXIT |
| 57 | printf '%s\n' "$VIDEO" > "$TMP/nur-video.m3u" |
| 58 | if [[ -n "$IMAGE" ]]; then |
| 59 | printf '%s\n%s\n' "$IMAGE" "$VIDEO" > "$TMP/bild-video.m3u" |
| 60 | fi |
| 61 | chmod -R a+rX "$TMP" |
| 62 | |
| 63 | LOGDIR="$TMP/logs"; mkdir -p "$LOGDIR" |
| 64 | FAILED=() |
| 65 | declare -A RESULT |
| 66 | |
| 67 | # Ein Durchlauf. LEN=0 laesst die Datei komplett laufen (ohne --length). |
| 68 | attempt() { |
| 69 | local log="$1" limit="$2" len="$3"; shift 3 |
| 70 | local extra=() |
| 71 | [[ "$len" != "0" ]] && extra=(--length="$len") |
| 72 | sudo -u "$RUN_USER" env "${ENV_ARGS[@]}" \ |
| 73 | timeout -k 3 "$limit" mpv --no-config --msg-level=all=error "${extra[@]}" "$@" \ |
| 74 | >"$log" 2>&1 |
| 75 | } |
| 76 | |
| 77 | run_as() { |
| 78 | local id="$1" label="$2" limit="$3" len="$4"; shift 4 |
| 79 | local log="$LOGDIR/$id.log" |
| 80 | local start rc dur |
| 81 | start=$(date +%s) |
| 82 | attempt "$log" "$limit" "$len" "$@" |
| 83 | rc=$? |
| 84 | dur=$(( $(date +%s) - start )) |
| 85 | |
| 86 | # Fehlschlaege einmal wiederholen: der Compositor verweigert zwischen zwei |
| 87 | # Laeufen gelegentlich kurz die Verbindung - das ist kein echter Befund. |
| 88 | if [[ $rc -ne 0 ]]; then |
| 89 | sleep 3 |
| 90 | attempt "$log" "$limit" "$len" "$@" |
| 91 | local rc2=$? |
| 92 | if [[ $rc2 -eq 0 ]]; then |
| 93 | RESULT[$id]=ok |
| 94 | printf ' \033[1;32mLAEUFT\033[0m %-46s %ss (erster Versuch einmalig gescheitert)\n' "$id $label" "$dur" |
| 95 | return |
| 96 | fi |
| 97 | rc=$rc2 |
| 98 | fi |
| 99 | |
| 100 | case $rc in |
| 101 | 0) RESULT[$id]=ok; ok "$id $label" "$dur" ;; |
| 102 | 124|137) RESULT[$id]=hang; bad "$id $label" "$dur"; FAILED+=("$id $label|$log") ;; |
| 103 | *) RESULT[$id]=err; odd "$id $label" "$rc"; FAILED+=("$id $label|$log") ;; |
| 104 | esac |
| 105 | sleep 2 |
| 106 | } |
| 107 | |
| 108 | run() { run_as "$1" "$2" "$LIMIT" "$PLAYSEC" "${@:3}"; } |
| 109 | |
| 110 | hdr "Vorbereitung" |
| 111 | note "Datei: $(basename "$VIDEO")" |
| 112 | note "Benutzer: $RUN_USER (uid $RUN_UID)" |
| 113 | note "Ausgabe: ${VO_ARGS[*]}" |
| 114 | [[ -n "$IMAGE" ]] && note "Bild: $(basename "$IMAGE")" || note "Bild: keines gefunden - Uebergangstest entfaellt" |
| 115 | systemctl stop tvwall 2>/dev/null && note "Dienst angehalten" |
| 116 | sleep 1 |
| 117 | |
| 118 | hdr "Ist die Datei ueberhaupt vollstaendig?" |
| 119 | if command -v ffmpeg >/dev/null; then |
| 120 | FFOUT="$(ffmpeg -v error -i "$VIDEO" -f null - 2>&1)" |
| 121 | if [[ -z "$FFOUT" ]]; then |
| 122 | printf ' \033[1;32mIN ORDNUNG\033[0m komplett dekodierbar\n' |
| 123 | else |
| 124 | printf ' \033[1;31mBESCHAEDIGT\033[0m ffmpeg meldet beim Durchlesen:\n' |
| 125 | echo "$FFOUT" | head -6 | sed 's/^/ /' |
| 126 | note "-> Dann hilft kein Einstellen: die Datei bitte neu hochladen." |
| 127 | fi |
| 128 | note "Groesse: $(stat -c %s "$VIDEO" 2>/dev/null) Bytes, md5: $(md5sum "$VIDEO" | cut -d" " -f1)" |
| 129 | fi |
| 130 | |
| 131 | hdr "Einzeldatei" |
| 132 | run 1 "nur --fullscreen (mpv-Standard)" --fullscreen "$VIDEO" |
| 133 | run 2 "Dienst-Optionen" "${SERVICE_ARGS[@]}" "$VIDEO" |
| 134 | run 3 "Dienst-Optionen + --hwdec=auto-safe" "${SERVICE_ARGS[@]}" --hwdec=auto-safe "$VIDEO" |
| 135 | run 4 "Dienst-Optionen + --no-audio" "${SERVICE_ARGS[@]}" --no-audio "$VIDEO" |
| 136 | run 5 "Dienst-Optionen + --vo=gpu-next" "${SERVICE_ARGS[@]/--vo=gpu/--vo=gpu-next}" "$VIDEO" |
| 137 | |
| 138 | hdr "Als Playliste (so macht es der Dienst)" |
| 139 | run 6 "Playliste mit nur dem Video" "${SERVICE_ARGS[@]}" --playlist="$TMP/nur-video.m3u" |
| 140 | if [[ -n "$IMAGE" ]]; then |
| 141 | run 7 "Playliste Bild -> Video" "${SERVICE_ARGS[@]}" --playlist="$TMP/bild-video.m3u" |
| 142 | run 8 "Bild -> Video, ohne --force-window" "${SERVICE_ARGS[@]/--force-window=yes/--force-window=no}" \ |
| 143 | --playlist="$TMP/bild-video.m3u" |
| 144 | run 9 "Bild -> Video, mit --gpu-context=auto" "${SERVICE_ARGS[@]}" --gpu-context=auto \ |
| 145 | --playlist="$TMP/bild-video.m3u" |
| 146 | # Ohne --length, damit ausgeschlossen ist, dass diese Testoption stoert. |
| 147 | run_as 10 "Bild -> Video, komplett bis zum Ende" 120 0 \ |
| 148 | "${SERVICE_ARGS[@]}" --playlist="$TMP/bild-video.m3u" |
| 149 | fi |
| 150 | |
| 151 | hdr "Ergebnis" |
| 152 | r() { echo "${RESULT[$1]:-}"; } |
| 153 | |
| 154 | if [[ ${#FAILED[@]} -gt 0 ]]; then |
| 155 | note "Haengen geblieben:" |
| 156 | for entry in "${FAILED[@]}"; do |
| 157 | printf ' %s\n' "${entry%%|*}" |
| 158 | tail -5 "${entry##*|}" 2>/dev/null | sed 's/^/ /' |
| 159 | done |
| 160 | echo |
| 161 | fi |
| 162 | |
| 163 | note "Deutung:" |
| 164 | if [[ "$(r 1)" != "ok" ]]; then |
| 165 | note " Schon der nackte mpv-Aufruf haengt. Dann liegt es an der Datei oder" |
| 166 | note " am Grafiktreiber, nicht an den Einstellungen dieses Werkzeugs." |
| 167 | elif [[ "$(r 2)" != "ok" ]]; then |
| 168 | note " Der nackte Aufruf laeuft, die Dienst-Optionen nicht. Die Varianten 3-5" |
| 169 | note " zeigen, welche Option es ist." |
| 170 | elif [[ -n "$IMAGE" && "$(r 7)" != "ok" && "$(r 6)" == "ok" ]]; then |
| 171 | note " Die Einzeldatei laeuft, der Wechsel Bild -> Video nicht." |
| 172 | if [[ "$(r 10)" == "ok" ]]; then |
| 173 | note " Variante 10 (ohne --length) laeuft aber durch - dann stoert nur die" |
| 174 | note " Testoption --length, und der Dienst selbst ist nicht betroffen." |
| 175 | else |
| 176 | note " Auch ohne Testoptionen (Variante 10) scheitert es - echter Befund." |
| 177 | note " Meldet ffmpeg oben \"BESCHAEDIGT\", ist die Datei die Ursache." |
| 178 | fi |
| 179 | [[ "$(r 8)" == "ok" ]] && note " --force-window=no behebt es -> genau das laesst sich einstellen." |
| 180 | [[ "$(r 9)" == "ok" ]] && note " --gpu-context=auto behebt es -> genau das laesst sich einstellen." |
| 181 | elif [[ ${#FAILED[@]} -eq 0 ]]; then |
| 182 | note " Keine Variante haengt. Dann liegt es nicht an mpv, sondern am" |
| 183 | note " Zusammenspiel mit dem Dienst - bitte 'journalctl -u tvwall -n 60' schicken." |
| 184 | fi |
| 185 | |
| 186 | systemctl start tvwall 2>/dev/null |
| 187 | hdr "Dienst wieder gestartet" |