diff --git a/Makefile b/Makefile index 2465cd0..4ad29fc 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ stream: mtx stream-stop: -@pkill -f "obs --profile filmabend" 2>/dev/null + -@scripts/ton.sh ab @echo "OBS beendet." # ── Datenbank hin und her (lokal ausführen) ─────────────────────────────────── diff --git a/scripts/stream.sh b/scripts/stream.sh index a4ab344..c1d1957 100755 --- a/scripts/stream.sh +++ b/scripts/stream.sh @@ -28,6 +28,8 @@ if ! timeout 3 bash -c "/dev/null; then exit 1 fi +scripts/ton.sh auf + mkdir -p "$P" "$BASIS/scenes" # Sendeziel jedes Mal neu schreiben — es hängt am Argument @@ -56,16 +58,18 @@ BaseCY=1080 OutputCX=1920 OutputCY=1080 FPSType=0 -FPSCommon=30 +FPSCommon=60 [Output] Mode=Simple [SimpleOutput] -VBitrate=6000 +VBitrate=9000 ABitrate=160 StreamEncoder=x264 Preset=veryfast +UseAdvanced=true +x264Settings=keyint=60 min-keyint=60 scenecut=0 bframes=0 [Audio] SampleRate=48000 @@ -92,7 +96,7 @@ if [ ! -f "$BASIS/scenes/$PROFIL.json" ]; then { "id": "pulse_output_capture", "name": "Ton", - "settings": {}, + "settings": {"device_id": "filmabend.monitor"}, "versioned_id": "pulse_output_capture" }, { diff --git a/scripts/ton.sh b/scripts/ton.sh new file mode 100755 index 0000000..f8a47d9 --- /dev/null +++ b/scripts/ton.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Legt den Tonweg für den Stream an und räumt ihn wieder ab. +# +# Bluetooth-Ausgaben haben eine eigene, ungenaue Uhr und laufen mit 44,1 kHz. +# Nimmt OBS deren Monitor auf, wandert der Ton mit der Zeit vom Bild weg — +# nach fünf Minuten sind es Sekunden. Eine Null-Senke hängt an der Systemuhr +# und liefert 48 kHz, also genau das, was der Encoder erwartet. +# +# ton.sh auf Senke anlegen, zum Standard machen, Mithören einrichten +# ton.sh ab Standard zurückstellen, Senke entfernen +set -euo pipefail + +SENKE="filmabend" +MERKER="/tmp/filmabend-vorheriger-ausgang" + +# wpctl zeigt Beschreibungen, keine Knotennamen — die ID kommt aus pw-dump +id_von() { + pw-dump | python3 -c " +import json, sys +name = sys.argv[1] +for o in json.load(sys.stdin): + if o.get('info', {}).get('props', {}).get('node.name') == name: + print(o['id']); break +" "$1" +} + +name_des_standards() { + wpctl inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null | + sed -n 's/.*node\.name = "\([^"]*\)".*/\1/p' | head -1 +} + +case "${1:-auf}" in +auf) + # Zuerst lesen: WirePlumber macht eine frisch angelegte Senke sofort + # zum Standard, danach wäre der alte Ausgang nicht mehr zu erfahren + VORHER="$(name_des_standards)" + [ -n "$VORHER" ] && [ "$VORHER" != "$SENKE" ] && echo "$VORHER" > "$MERKER" + ZURUECK="$(cat "$MERKER" 2>/dev/null || true)" + + if [ -z "$ZURUECK" ]; then + echo "Kein voriger Ausgang bekannt — bitte einmal 'scripts/ton.sh ab' laufen lassen." + exit 1 + fi + + if [ -z "$(id_von "$SENKE")" ]; then + pw-cli create-node adapter "{ + factory.name=support.null-audio-sink + node.name=$SENKE node.description=Filmabend + media.class=Audio/Sink object.linger=true + audio.rate=48000 audio.position=[FL,FR] + }" > /dev/null + sleep 1 + fi + + # Damit du selbst weiter über dein gewohntes Gerät hörst + if ! pgrep -f "node.name=filmabend-mithoeren" > /dev/null; then + nohup pw-loopback -m '[FL FR]' \ + --capture-props="stream.capture.sink=true target.object=$SENKE node.name=filmabend-mithoeren" \ + --playback-props="target.object=$ZURUECK node.name=filmabend-ausgabe" \ + > /dev/null 2>&1 & + sleep 1 + fi + + # Ab jetzt landet aller Ton in der Senke — und von dort bei dir und im Stream + wpctl set-default "$(id_von "$SENKE")" + echo "Ton läuft über die Senke Filmabend, du hörst weiter über $ZURUECK." + ;; + +ab) + ZURUECK="$(cat "$MERKER" 2>/dev/null || true)" + if [ -n "$ZURUECK" ]; then + ID="$(id_von "$ZURUECK")" + [ -n "$ID" ] && wpctl set-default "$ID" + fi + pkill -f "node.name=filmabend-mithoeren" 2>/dev/null || true + ID="$(id_von "$SENKE")" + [ -n "$ID" ] && pw-cli destroy "$ID" > /dev/null 2>&1 || true + rm -f "$MERKER" + echo "Ton zurück auf ${ZURUECK:-dein Gerät}." + ;; + +*) + echo "Aufruf: ton.sh auf|ab" + exit 1 + ;; +esac