From cf689648040876edf48a53433855549e1ee76b16 Mon Sep 17 00:00:00 2001 From: Jason DeTiberus Date: Wed, 4 Dec 2024 12:16:19 -0500 Subject: [PATCH] move debug scripts to just and include wolf in beardy build --- .../apps/wolf/scripts/debugging/steam-test.sh | 14 + files/apps/wolf/scripts/debugging/wolf-api.sh | 1 + files/apps/wolf/scripts/debugging/wolf.sh | 23 ++ files/apps/wolf/system/etc/cfg/config.toml | 304 ++++++++++++++++++ .../etc/containers/systemd/wolf.container | 58 ++++ files/apps/wolf/system/etc/sysconfig/wolf | 11 + .../system/etc/udev/rules.d/85-wolf.rules | 14 + 7 files changed, 425 insertions(+) create mode 100644 files/apps/wolf/scripts/debugging/steam-test.sh create mode 100644 files/apps/wolf/scripts/debugging/wolf-api.sh create mode 100644 files/apps/wolf/scripts/debugging/wolf.sh create mode 100644 files/apps/wolf/system/etc/cfg/config.toml create mode 100644 files/apps/wolf/system/etc/containers/systemd/wolf.container create mode 100644 files/apps/wolf/system/etc/sysconfig/wolf create mode 100644 files/apps/wolf/system/etc/udev/rules.d/85-wolf.rules diff --git a/files/apps/wolf/scripts/debugging/steam-test.sh b/files/apps/wolf/scripts/debugging/steam-test.sh new file mode 100644 index 0000000..9257c7a --- /dev/null +++ b/files/apps/wolf/scripts/debugging/steam-test.sh @@ -0,0 +1,14 @@ +sudo podman run --rm -it \ +--device=/dev/dri/renderD128 \ +--device=/dev/dri/card1 \ +-ipc=host \ +-privileged \ +-cap-add=ALL \ +-security-opt seccomp=unconfined \ +-e XDG_RUNTIME_DIR=/tmp \ +-v ${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}:/tmp/${WAYLAND_DISPLAY}:rw \ +-e XDG_SESSION_TYPE=wayland \ +-e WAYLAND_DISPLAY=${WAYLAND_DISPLAY} \ +-e RUN_SWAY=true \ +-v /tmp/SteamGOWData:/home/retro/ \ +ghcr.io/games-on-whales/steam:fix-steam-mesa diff --git a/files/apps/wolf/scripts/debugging/wolf-api.sh b/files/apps/wolf/scripts/debugging/wolf-api.sh new file mode 100644 index 0000000..de4fc88 --- /dev/null +++ b/files/apps/wolf/scripts/debugging/wolf-api.sh @@ -0,0 +1 @@ +curl --unix-socket /var/run/wolf/wolf.sock http://localhost/api/v1/sessions | jq \ No newline at end of file diff --git a/files/apps/wolf/scripts/debugging/wolf.sh b/files/apps/wolf/scripts/debugging/wolf.sh new file mode 100644 index 0000000..b87deab --- /dev/null +++ b/files/apps/wolf/scripts/debugging/wolf.sh @@ -0,0 +1,23 @@ +sudo podman stop WolfPulseAudio || true +sudo podman rm WolfPulseAudio || true +sudo podman run --rm \ + --name wolf \ + --privileged \ + --network=host \ + --ipc=host \ + --cap-add=ALL \ + --device-cgroup-rule "c 13:* rmw" \ + --device /dev/dri \ + --device /dev/uinput \ + --device /dev/uhid \ + -v /tmp/sockets:/tmp/sockets:rw \ + -v /etc/wolf:/etc/wolf:rw \ + -v /run/podman/podman.sock:/var/run/docker.sock:ro \ + -v /dev/input:/dev/input:ro \ + -v /run/udev:/run/udev:rw \ + --security-opt seccomp=unconfined \ + ghcr.io/games-on-whales/wolf:stable + + +# -e WOLF_RENDER_NODE=/dev/dri/renderD129 \ +# -e WOLF_ENCODER_NODE=/dev/dri/renderD129 \ diff --git a/files/apps/wolf/system/etc/cfg/config.toml b/files/apps/wolf/system/etc/cfg/config.toml new file mode 100644 index 0000000..49ae8f1 --- /dev/null +++ b/files/apps/wolf/system/etc/cfg/config.toml @@ -0,0 +1,304 @@ +# TODO reconcile with upstream default config: https://github.com/games-on-whales/wolf/blob/stable/src/moonlight-server/state/default/config.v4.toml + +config_version = 4 +hostname = 'Wolf' +paired_clients = [] + +[gstreamer.audio] +default_audio_params = 'queue max-size-buffers=3 leaky=downstream ! audiorate ! audioconvert' +default_opus_encoder = 'opusenc bitrate={bitrate} bitrate-type=cbr frame-size={packet_duration} bandwidth=fullband audio-type=restricted-lowdelay max-payload-size=1400' +default_sink = '''rtpmoonlightpay_audio name=moonlight_pay packet_duration={packet_duration} encrypt={encrypt} aes_key="{aes_key}" aes_iv="{aes_iv}" ! +udpsink bind-port={host_port} host={client_ip} port={client_port} sync=true''' +default_source = 'interpipesrc listen-to={session_id}_audio is-live=true stream-sync=restart-ts max-bytes=0 max-buffers=3 block=false' + +[gstreamer.video] +default_sink = '''rtpmoonlightpay_video name=moonlight_pay payload_size={payload_size} fec_percentage={fec_percentage} min_required_fec_packets={min_required_fec_packets} ! +udpsink bind-port={host_port} host={client_ip} port={client_port} sync=true''' +default_source = 'interpipesrc listen-to={session_id}_video is-live=true stream-sync=restart-ts max-bytes=0 max-buffers=3 block=false' + +[gstreamer.video.defaults.nvcodec] +video_params = '''queue ! +cudaupload ! +cudaconvertscale ! +video/x-raw(memory:CUDAMemory), width={width}, height={height}, chroma-site={color_range}, format=NV12, colorimetry={color_space}, pixel-aspect-ratio=1/1''' + +[gstreamer.video.defaults.qsv] +video_params = '''queue ! +videoconvertscale ! +video/x-raw, chroma-site={color_range}, width={width}, height={height}, format=NV12, colorimetry={color_space}''' + +[gstreamer.video.defaults.vaapi] +video_params = '''queue ! +videoconvertscale ! +video/x-raw, chroma-site={color_range}, width={width}, height={height}, format=NV12, colorimetry={color_space}''' + +[[gstreamer.video.av1_encoders]] +check_elements = ['nvav1enc', 'cudaconvertscale', 'cudaupload'] +encoder_pipeline = '''nvav1enc gop-size=-1 bitrate={bitrate} rc-mode=cbr zerolatency=true preset=p1 tune=ultra-low-latency multi-pass=two-pass-quarter ! +av1parse ! +video/x-av1, stream-format=obu-stream, alignment=frame, profile=main''' +plugin_name = 'nvcodec' + +[[gstreamer.video.av1_encoders]] +check_elements = ['qsvav1enc', 'videoconvertscale'] +encoder_pipeline = '''qsvav1enc gop-size=0 ref-frames=1 bitrate={bitrate} rate-control=cbr low-latency=1 target-usage=6 ! +av1parse ! +video/x-av1, stream-format=obu-stream, alignment=frame, profile=main''' +plugin_name = 'qsv' + +[[gstreamer.video.av1_encoders]] +check_elements = ['vaav1enc', 'videoconvertscale'] +encoder_pipeline = '''vaav1enc gop-size=0 ref-frames=1 bitrate={bitrate} rate-control=cbr ! +av1parse ! +video/x-av1, stream-format=obu-stream, alignment=frame, profile=main''' +plugin_name = 'vaapi' + +[[gstreamer.video.av1_encoders]] +check_elements = ['vaav1lpenc', 'videoconvertscale'] +encoder_pipeline = '''vaav1lpenc gop-size=0 ref-frames=1 bitrate={bitrate} rate-control=cbr ! +av1parse ! +video/x-av1, stream-format=obu-stream, alignment=frame, profile=main''' +plugin_name = 'vaapi' + +[[gstreamer.video.av1_encoders]] +check_elements = ['av1enc'] +encoder_pipeline = '''av1enc usage-profile=realtime end-usage=vbr target-bitrate={bitrate} ! +av1parse ! +video/x-av1, stream-format=obu-stream, alignment=frame, profile=main''' +plugin_name = 'aom' +video_params = '''videoconvertscale ! +videorate ! +video/x-raw, width={width}, height={height}, framerate={fps}/1, format=I420, +chroma-site={color_range}, colorimetry={color_space}''' + +[[gstreamer.video.h264_encoders]] +check_elements = ['nvh264enc', 'cudaconvertscale', 'cudaupload'] +encoder_pipeline = '''nvh264enc preset=low-latency-hq zerolatency=true gop-size=0 rc-mode=cbr-ld-hq bitrate={bitrate} aud=false ! +h264parse ! +video/x-h264, profile=main, stream-format=byte-stream''' +plugin_name = 'nvcodec' + +[[gstreamer.video.h264_encoders]] +check_elements = ['qsvh264enc', 'videoconvertscale'] +encoder_pipeline = '''qsvh264enc b-frames=0 gop-size=0 idr-interval=1 ref-frames=1 bitrate={bitrate} rate-control=cbr target-usage=6 ! +h264parse ! +video/x-h264, profile=main, stream-format=byte-stream''' +plugin_name = 'qsv' + +[[gstreamer.video.h264_encoders]] +check_elements = ['vah264lpenc', 'videoconvertscale'] +encoder_pipeline = '''vah264lpenc aud=false b-frames=0 ref-frames=1 num-slices={slices_per_frame} bitrate={bitrate} target-usage=6 ! +h264parse ! +video/x-h264, profile=main, stream-format=byte-stream''' +plugin_name = 'vaapi' + +[[gstreamer.video.h264_encoders]] +check_elements = ['vah264enc', 'videoconvertscale'] +encoder_pipeline = '''vah264enc aud=false b-frames=0 ref-frames=1 num-slices={slices_per_frame} bitrate={bitrate} target-usage=6 ! +h264parse ! +video/x-h264, profile=main, stream-format=byte-stream''' +plugin_name = 'vaapi' + +[[gstreamer.video.h264_encoders]] +check_elements = ['x264enc'] +encoder_pipeline = '''x264enc pass=qual tune=zerolatency speed-preset=superfast b-adapt=false bframes=0 ref=1 +sliced-threads=true threads={slices_per_frame} option-string="slices={slices_per_frame}:keyint=infinite:open-gop=0" +b-adapt=false bitrate={bitrate} aud=false ! +video/x-h264, profile=high, stream-format=byte-stream''' +plugin_name = 'x264' +video_params = '''videoconvertscale ! +videorate ! +video/x-raw, width={width}, height={height}, framerate={fps}/1, format=I420, +chroma-site={color_range}, colorimetry={color_space}''' + +[[gstreamer.video.hevc_encoders]] +check_elements = ['nvh265enc', 'cudaconvertscale', 'cudaupload'] +encoder_pipeline = '''nvh265enc gop-size=-1 bitrate={bitrate} aud=false rc-mode=cbr zerolatency=true preset=p1 tune=ultra-low-latency multi-pass=two-pass-quarter ! +h265parse ! +video/x-h265, profile=main, stream-format=byte-stream''' +plugin_name = 'nvcodec' + +[[gstreamer.video.hevc_encoders]] +check_elements = ['qsvh265enc', 'videoconvertscale'] +encoder_pipeline = '''qsvh265enc b-frames=0 gop-size=0 idr-interval=1 ref-frames=1 bitrate={bitrate} rate-control=cbr low-latency=1 target-usage=6 ! +h265parse ! +video/x-h265, profile=main, stream-format=byte-stream''' +plugin_name = 'qsv' + +[[gstreamer.video.hevc_encoders]] +check_elements = ['vah265lpenc', 'videoconvertscale'] +encoder_pipeline = '''vah265lpenc aud=false b-frames=0 ref-frames=1 num-slices={slices_per_frame} bitrate={bitrate} mbbrc=1 rate-control=cbr target-usage=6 ! +h265parse ! +video/x-h265, profile=main, stream-format=byte-stream''' +plugin_name = 'vaapi' + +[[gstreamer.video.hevc_encoders]] +check_elements = ['vah265enc', 'videoconvertscale'] +encoder_pipeline = '''vah265enc aud=false b-frames=0 ref-frames=1 num-slices={slices_per_frame} bitrate={bitrate} mbbrc=1 rate-control=cbr target-usage=6 ! +h265parse ! +video/x-h265, profile=main, stream-format=byte-stream''' +plugin_name = 'vaapi' + +[[gstreamer.video.hevc_encoders]] +check_elements = ['x265enc'] +encoder_pipeline = '''x265enc tune=zerolatency speed-preset=superfast bitrate={bitrate} +option-string="info=0:keyint=-1:qp=28:repeat-headers=1:slices={slices_per_frame}:aud=0:annexb=1:log-level=3:open-gop=0:bframes=0:intra-refresh=0" ! +video/x-h265, profile=main, stream-format=byte-stream''' +plugin_name = 'x265' +video_params = '''videoconvertscale ! +videorate ! +video/x-raw, width={width}, height={height}, framerate={fps}/1, format=I420, +chroma-site={color_range}, colorimetry={color_space}''' + +[[apps]] +start_virtual_compositor = true +# TODO: fix use of Privileged +title = 'Firefox' + +[apps.runner] +base_create_json = '''{ + "HostConfig": { + "IpcMode": "host", + "Privileged": true, + "CapAdd": ["NET_RAW", "MKNOD", "NET_ADMIN"], + "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] + } +} +''' +devices = [] +env = [ + 'RUN_SWAY=1', + 'MOZ_ENABLE_WAYLAND=1', + 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*', +] +image = 'ghcr.io/games-on-whales/firefox:edge' +mounts = [] +name = 'WolfFirefox' +ports = [] +type = 'docker' + +[[apps]] +start_virtual_compositor = true +# TODO: fix use of Privileged +title = 'RetroArch' + +[apps.runner] +base_create_json = '''{ + "HostConfig": { + "IpcMode": "host", + "CapAdd": ["NET_RAW", "MKNOD", "NET_ADMIN", "SYS_ADMIN", "SYS_NICE"], + "Privileged": true, + "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] + } +} +''' +devices = [] +env = [ + 'RUN_SWAY=true', + 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*', +] +image = 'ghcr.io/games-on-whales/retroarch:edge' +mounts = [] +name = 'WolfRetroarch' +ports = [] +type = 'docker' + +[[apps]] +start_virtual_compositor = true +# TODO: fix use of Privileged +title = 'Steam' + +[apps.runner] +base_create_json = '''{ + "HostConfig": { + "IpcMode": "host", + "CapAdd": ["SYS_ADMIN", "SYS_NICE", "SYS_PTRACE", "NET_RAW", "MKNOD", "NET_ADMIN", "ALL"], + "SecurityOpt": ["seccomp=unconfined", "apparmor=unconfined"], + "Ulimits": [{"Name":"nofile", "Hard":10240, "Soft":10240}], + "Privileged": true, + "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] + } +} +''' +devices = [] +env = [ + 'PROTON_LOG=1', + 'RUN_SWAY=true', + 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*', + # 'STEAM_STARTUP_FLAGS=-steamos3 -clientbeta publicbeta' +] +image = 'ghcr.io/games-on-whales/steam:fix-steam-mesa' +mounts = [] +name = 'WolfSteam' +ports = [] +type = 'docker' +# render_node = "/dev/dri/renderD129" + +[[apps]] +start_virtual_compositor = true +# TODO: fix use of Privileged +title = 'Pegasus' + +[apps.runner] +base_create_json = '''{ + "HostConfig": { + "IpcMode": "host", + "CapAdd": ["NET_RAW", "MKNOD", "NET_ADMIN", "SYS_ADMIN", "SYS_NICE"], + "Privileged": true, + "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] + } +} +''' +devices = [] +env = [ + 'RUN_SWAY=1', + 'GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*', +] +image = 'ghcr.io/games-on-whales/pegasus:edge' +mounts = [] +name = 'WolfPegasus' +ports = [] +type = 'docker' + +[[apps]] +start_virtual_compositor = true +# TODO: fix use of Privileged +title = 'Lutris' + +[apps.runner] +base_create_json = '''{ + "HostConfig": { + "IpcMode": "host", + "CapAdd": ["NET_RAW", "MKNOD", "NET_ADMIN", "SYS_ADMIN", "SYS_NICE"], + "Privileged": true, + "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] + } +} +''' +devices = [] +env = [ + 'RUN_SWAY=1', + 'GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia* /var/lutris/', +] +image = 'ghcr.io/games-on-whales/lutris:edge' +mounts = ['lutris:/var/lutris/:rw'] +name = 'WolfLutris' +ports = [] +type = 'docker' + +[[apps]] +start_audio_server = false +start_virtual_compositor = false +title = 'Test ball' + +[apps.audio] +source = 'audiotestsrc wave=ticks is-live=true' + +[apps.runner] +run_cmd = "sh -c \"while :; do echo 'running...'; sleep 10; done\"" +type = 'process' + +[apps.video] +source = '''videotestsrc pattern=ball flip=true is-live=true ! +video/x-raw, framerate={fps}/1 +''' diff --git a/files/apps/wolf/system/etc/containers/systemd/wolf.container b/files/apps/wolf/system/etc/containers/systemd/wolf.container new file mode 100644 index 0000000..d9acb49 --- /dev/null +++ b/files/apps/wolf/system/etc/containers/systemd/wolf.container @@ -0,0 +1,58 @@ +# wolf.container +# modified from: https://github.com/games-on-whales/wolf/pull/92/files +[Unit] +Description=Podman Wolf Gamestreaming + +[Service] +Environment=PATH=/usr/bin +TimeoutStartSec=900 + +# TODO: Look into using systemd-tmpfiles here: +# https://wiki.archlinux.org/title/Systemd#systemd-tmpfiles_-_temporary_filesd +# look into config directive/setting for /var/run abstraction +ExecStartPre=-mkdir -p %T/sockets +ExecStartPre=-mkdir -p /var/run/wolf +ExecStartPre=-podman rm --force WolfPulseAudio +Restart=on-failure +RestartSec=5 +StartLimitBurst=5 + +[Install] +WantedBy=multi-user.target + +[Container] +# Avoid needing to use systemctl daemon-reload for trivial env var changes +# /etc/sysconfig is the default on Fedora based systems, /etc/default on ubuntu +# TODO: is there a better way to genericize this, or maybe just dump it into /etc/wolf? +EnvironmentFile=/etc/sysconfig/wolf + +Image=ghcr.io/games-on-whales/wolf:stable +AutoUpdate=registry + +HostName=%N # TODO: is this the hostname we actually want? + +# Figure out what is actually really required here +#AddCapability=CAP_SYS_PTRACE +#AddCapability=CAP_NET_ADMIN +AddCapability=ALL + +Network=host + +# TODO: figure out tightening back down security +SecurityLabelDisable=true +# same as adding '--security-opt seccomp=unconfined' to PodmanArgs +SeccompProfile=unconfined + +PodmanArgs=--ipc=host --device-cgroup-rule "c 13:* rmw" + +AddDevice=/dev/dri +AddDevice=/dev/uinput +AddDevice=/dev/uhid + +Volume=/dev/input:/dev/input:ro +Volume=/dev/:/dev/:ro +Volume=/run/udev:/run/udev:ro +Volume=%T/sockets:/tmp/sockets:rw +Volume=%t/podman/podman.sock:/var/run/docker.sock:ro +Volume=/etc/wolf:/etc/wolf:rw +Volume=/var/run/wolf:/var/run/wolf:rw diff --git a/files/apps/wolf/system/etc/sysconfig/wolf b/files/apps/wolf/system/etc/sysconfig/wolf new file mode 100644 index 0000000..5b37ee0 --- /dev/null +++ b/files/apps/wolf/system/etc/sysconfig/wolf @@ -0,0 +1,11 @@ +# Environment variables for Podman Wolf Gamestreaming + +# Temporary debugging vars +#WOLF_LOG_LEVEL=DEBUG +#WOLF_STOP_CONTAINER_ON_EXIT=true +#WOLF_RENDER_NODE=SOFTWARE + +# TODO: figure out how to propagate TZ to created containers +TZ=America/New_York + +WOLF_SOCKET_PATH=/var/run/wolf/wolf.sock diff --git a/files/apps/wolf/system/etc/udev/rules.d/85-wolf.rules b/files/apps/wolf/system/etc/udev/rules.d/85-wolf.rules new file mode 100644 index 0000000..baf2c11 --- /dev/null +++ b/files/apps/wolf/system/etc/udev/rules.d/85-wolf.rules @@ -0,0 +1,14 @@ +# Allows Wolf to access /dev/uinput +KERNEL=="uinput", SUBSYSTEM=="misc", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput" + +# Allows Wolf to access /dev/uhid +KERNEL=="uhid", TAG+="uaccess" + +# Move virtual keyboard and mouse into a different seat +SUBSYSTEMS=="input", ATTRS{id/vendor}=="ab00", MODE="0660", GROUP="input", ENV{ID_SEAT}="seat9" + +# Joypads +SUBSYSTEMS=="input", ATTRS{name}=="Wolf X-Box One (virtual) pad", MODE="0660", GROUP="input" +SUBSYSTEMS=="input", ATTRS{name}=="Wolf PS5 (virtual) pad", MODE="0660", GROUP="input" +SUBSYSTEMS=="input", ATTRS{name}=="Wolf gamepad (virtual) motion sensors", MODE="0660", GROUP="input" +SUBSYSTEMS=="input", ATTRS{name}=="Wolf Nintendo (virtual) pad", MODE="0660", GROUP="input"