diff --git a/README.md b/README.md index d8b422f..fdb9638 100644 --- a/README.md +++ b/README.md @@ -408,8 +408,9 @@ Where the time comes from, best first: start and the sidecar records drift over the file. **Then the band is cropped off**: the sweep re-encodes the stream (libx264 CRF 18, frame timing preserved, audio copied) so the published file is the game image + audio + the `tmcd` track and nothing else; the raw with the band is deleted (`KEEP_RAW_STREAMS=1` keeps it in `.src/`). - Streams without the band are remuxed losslessly. Cost: a 3 h 1440p60 night is a few hours of CPU on the - E-2136; the sweep is niced. OBS: dock → *Timecode band* → + Streams without the band are remuxed losslessly. Cost: every re-encode (streams and clips) runs x264 on + `ENCODE_THREADS` threads (2, set in `systemd/normalize-sweep.service`) so Jellyfin and live viewers stay + responsive; a 3 h 1440p60 night therefore takes most of the next day to publish. OBS: dock → *Timecode band* → "Add band + clock source" (canvas grows by W/160 rows, a locked browser source `Vanguard Clock` → `https://live.BASE/dock/clock/` sits in the band; only while not streaming). Go Live: the "Timecode band" checkbox (default on). 2. **Server arrival time** (no band) — microseconds in the recording filename. Within ~0.1–0.3 s per streamer diff --git a/bin/normalize-sweep.sh b/bin/normalize-sweep.sh index f2ea375..43cd9df 100755 --- a/bin/normalize-sweep.sh +++ b/bin/normalize-sweep.sh @@ -29,6 +29,9 @@ FILL_GAPS_MS="${FILL_GAPS_MS:-0}" # Band removal re-encodes every stream that carried the timecode band. The raw (with band) is deleted after a # successful encode; KEEP_RAW_STREAMS=1 keeps it in the hidden .src/ instead (doubles disk per night). KEEP_RAW_STREAMS="${KEEP_RAW_STREAMS:-0}" +# Encoder threads for every re-encode (clips and streams). 2 leaves the box responsive for Jellyfin/live viewers; +# conversions take longer. systemd Environment=ENCODE_THREADS=... to change. +ENCODE_THREADS="${ENCODE_THREADS:-2}" MAX_W=2560; MAX_H=1440; MAX_FPS=60 exec 9>"${LOCK_FILE:-/run/lock/normalize-sweep.lock}" flock -n 9 || exit 0 @@ -166,7 +169,7 @@ PYEOF if (( live )) && [[ $reason != *audio=* ]]; then aargs=(-c:a copy); fi nice -n 15 ionice -c 3 ffmpeg -nostdin -hide_banner -loglevel error -y -i "$src" \ -map 0:v:0 -map '0:a?' \ - -c:v libx264 -preset veryfast -crf 18 -maxrate 30M -bufsize 60M -pix_fmt yuv420p -profile:v high \ + -c:v libx264 -preset veryfast -crf 18 -maxrate 30M -bufsize 60M -pix_fmt yuv420p -profile:v high -threads "$ENCODE_THREADS" \ -vf "$vf" -fps_mode "$fpsmode" "${fpsargs[@]}" \ "${aargs[@]}" \ "${tcargs[@]}" -movflags +faststart -f mp4 "$tmp" diff --git a/bin/vgdeband.sh b/bin/vgdeband.sh index 115447d..9f8c03d 100755 --- a/bin/vgdeband.sh +++ b/bin/vgdeband.sh @@ -27,7 +27,7 @@ print(d.get("source","none"), 1 if d.get("band_removed") else 0, d.get("timecode log "deband $f (${w}px wide -> crop ${band} rows; tc=$tc)" nice -n 15 ionice -c 3 ffmpeg -nostdin -hide_banner -loglevel error -y -i "$f" \ -map 0:v:0 -map '0:a?' \ - -c:v libx264 -preset veryfast -crf 18 -maxrate 30M -bufsize 60M -pix_fmt yuv420p -profile:v high \ + -c:v libx264 -preset veryfast -crf 18 -maxrate 30M -bufsize 60M -pix_fmt yuv420p -profile:v high -threads "${ENCODE_THREADS:-2}" \ -vf "crop=iw:ih-${band}:0:0" -fps_mode passthrough -c:a copy \ "${tcargs[@]}" -movflags +faststart -f mp4 "$tmp" rc=$?; d1=$(dur "$f"); d2=$(dur "$tmp") diff --git a/systemd/normalize-sweep.service b/systemd/normalize-sweep.service index 4c9c0a4..f1c0543 100644 --- a/systemd/normalize-sweep.service +++ b/systemd/normalize-sweep.service @@ -8,4 +8,5 @@ Type=oneshot ExecStart=/usr/local/bin/normalize-sweep.sh Nice=15 IOSchedulingClass=idle +Environment=ENCODE_THREADS=2 # x264 threads per conversion; keeps Jellyfin and the live portal responsive (default 2) # Environment=FILL_GAPS_MS=1000 # opt-in: CFR re-encode streams whose dropped-frame holes total >= 1 s (hours of CPU on this box)