Skip to content

Instantly share code, notes, and snippets.

@robertkirkman
Last active August 7, 2024 18:35
Show Gist options
  • Save robertkirkman/753922262259486ec417e5ff8b5b924b to your computer and use it in GitHub Desktop.
Save robertkirkman/753922262259486ec417e5ff8b5b924b to your computer and use it in GitHub Desktop.
How to record or stream SteamOS 3.X Gaming Mode

How to record or stream SteamOS 3.X in gamescope (Gaming Mode)

Here is another method that uses Flatpak and obs-vkcapture, which don't meet my personal needs but are very likely to be useful for you. In the comments there there is also an obs-gstreamer method.

Here is another method for recording that has its own GUI for Gaming Mode built with Decky Loader.

Force SteamOS to behave very much like Arch Linux

Key phrase: very much like, not identical. Always treat this as more unstable than Manjaro and only expect support with it in my comments section here.

  1. Set a password (if you haven't already) and disable read-only rootfs (yes this means after updating SteamOS [not Steam Client] this will all be deleted and you will have to do all this again if you want to stream again)
passwd
sudo steamos-readonly disable
  1. (OPTIONAL) The on-Steam Deck OBS guide is written in a way that shouldn't require a separate device to actually execute any commands (the Sunshine guide requires a separate device for Moonlight), but for much less awkward and more efficient CLI activity, especially if you don't have a physical keyboard that's compatible with your Steam Deck, you can enable the SSH server to allow remotely connecting using your preferred keyboard-endowed device
sudo systemctl enable --now sshd
  1. Set up pacman. What you hope to see after sudo pacman -Syyu is "there is nothing to do", or at least very few upgrades available. That means that the repositories currently selected in your pacman.conf contain packages very close to the rootfs image that rauc last installed, and the fewer packages that differ from your rootfs' preinstalled packages you have to install, the less likely it is you will encounter severe issues.

WARNING: If something goes wrong, this can mess up your rootfs, but luckily there is a backup rootfs in every OFW Steam Deck that you can access with the ・・・ + ⏻ button combination at boot, from which you can recover if that happens.

  • On February 6th, 2024, the precompiled pacman package repository that Valve provides for us, steamdeck-packages.steamos.cloud, had 2 active repository sets, -3.5 and -main

  • steamos-select-branch -l had 5 branches, rel, rc, beta, bc and main

  • the rel branch had SteamOS 3.5.7 with build number 20231122.1, which was

    • 6 packages ahead and 20 packages behind the -3.5 repository set
    • 6 packages ahead and 44 packages behind the -main repository set
    • For this reason, I think the -3.5 repository set was the best choice for this branch
  • the rc, beta and bc branches ALL had SteamOS 3.5.14 with build number 20240202.1, which was

    • 0 packages ahead and 2 packages behind the -3.5 repository set
    • 3 packages ahead and 41 packages behind the -main repository set
    • For this reason, I think the -3.5 repository set was the best choice for these branches
  • the main branch had SteamOS 3.6 with build number 20240119.1000, which was

    • 34 packages ahead and 4 packages behind the -3.5 repository set
    • 0 packages ahead and 13 packages behind the -main repository set
    • For this reason, I think the -main repository set was the best choice for this branch
curl https://gist.githubusercontent.com/robertkirkman/753922262259486ec417e5ff8b5b924b/raw/9866b808cf82ccababcc066aafe2da6a51f5b453/pacman.conf | sudo tee /etc/pacman.conf
if [[ $(steamos-select-branch -c) == 'main' ]]; then sudo sed -i 's/3.5/main/g' /etc/pacman.conf; fi
sudo pacman-key --init
sudo pacman-key --populate
sudo pacman -Syyu
  1. Reinstall all corrupted packages that once contained header files before Valve deleted /usr/include

You might see a few errors generated by mkinitcpio during the post-transaction hooks phase. They are spurious and can be safely ignored.

sudo pacman -S $(pacman -Ql | grep -e include -e pkgconf | cut -d' ' -f1 | awk '!a[$0]++')
  1. Install some compiler toolchain packages. At this point you could now add as many pacman packages as you want and could fit into the 5 GB root partition, bearing in mind that even upstream Arch Linux itself has periodically had packages in their official repositories that break the system when installed, so all pacman users are expected to have experience with the packages they're installing, use caution, and prepare for the possibility of a bug.
sudo pacman --overwrite=/etc/ld.so.conf.d/fakeroot.conf -S base-devel meson cmake
  1. (OPTIONAL) Install your preferred terminal emulator (unless you prefer konsole which is preinstalled), Launch KDE Plasma Xorg (Desktop Mode), open Steam LIBRARY tab, and add it as a non-Steam game; here kitty is used as an example. At this time, I would also suggest you add a large number of dummy/duplicate non-Steam game entries for future use, because if you wish to remain in Gaming Mode, you will be able to change preexisting non-Steam game entries to add new shortcuts, which is otherwise impossible without switching to Desktop Mode.
paru -S kitty
  • Suggested kitty Launch Options in non-Steam game properties:
LD_PRELOAD= %command% --start-as=fullscreen

Sunshine

  1. Clone the repository of the AUR package for sunshine, which is maintained by the upstream developer, apply a patch of my own design that makes sunshine usable in Steam Deck's Gaming Mode, then build and install sunshine using makepkg
git clone https://aur.archlinux.org/sunshine.git
cd sunshine
(curl https://gist.githubusercontent.com/robertkirkman/753922262259486ec417e5ff8b5b924b/raw/2bb65f89ad3a0e0219c2a7f5116ed18fa2c0a8d0/sunshine-aur-steamdeck-gamingmode.patch && echo) | git apply -v
makepkg -si
  1. Enable DRM capture mode in sunshine

WARNING: setcap is a security risk and after running it, all users and software with executable permission for the affected binary can be considered effectively root-equivalent (in this context, they would definitely be able to see your screen for example). To remove setcap permissions from any binaries granted this, use sudo setcap -r /path/to/file.

sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))
  1. Create symlink to stub xrandr to work around Error: [xrandr --output HDMI-1 --mode 1920x1080] failed with code [1], then run sunshine:
ln -s /bin/true xrandr
export PATH=$(pwd):$PATH
sunshine &
  1. Make sure the Steam Deck is connected to Wi-Fi or Ethernet, and identify its local IP address for use in the following steps
ip a
  1. Now move to the LAN device you would like to connect to the Steam Deck's Sunshine from and install Moonlight and a browser capable of accessing Sunshine's PIN entry box, for example epiphany or ungoogled-chromium; example command for an Arch Linux device with yay

Unfortunately, OBS cannot record the moonlight-qt window in gamescope without adding the additional overhead of the OBS section below this section, and epiphany is bugged and does not work in gamescope, so both of these softwares can only be effectively used to connect to Steam Deck Gaming Mode Sunshine from a separate device.

yay -S moonlight-qt epiphany
  1. Launch your browser, connect to the Sunshine web server on the Steam Deck where you replace 192.168.12.146 with your Steam Deck's local IP address, accept all warnings (which occur on every default copy of Sunshine) and create a username and password for administration of Sunshine, re-enter the new credentials to log in, then navigate to the "PIN Pairing" entry box
epiphany https://192.168.12.146:47990
  1. Launch Moonlight and add the Steam Deck's IP address as a new device, then type the PIN provided by Moonlight into the PIN box in the browser window

Sometimes Moonlight will fail to authenticate the first time even if the PIN is correct, just keep trying until you can click on the Steam Deck's entry and see the "Desktop" button.

moonlight
  1. Click the Steam Deck's entry in Moonlight and the "Desktop" button, you can now see the entire Steam Deck screen and almost every menu and game, with a few exceptions - sometimes the onscreen keyboard and Quick Settings menu are invisible in Moonlight, but overall, Sunshine configured this way is capable of capturing much more, more reliably, than any other software. You can also capture the moonlight-qt window using OBS, which works better than the OBS guide below as long as you have this other system to run Moonlight on. Sometimes, sunshine might crash or Moonlight might spontaneously disconnect; if that happens, just restart both and everything will start working again.

If you use Sunshine+Moonlight for streaming-based netplay (someone playing on Steam Deck while someone else plays on another device), you might want to configure advanced audio settings (such as making sure the person using Moonlight can hear the game audio, but not themselves through your VOIP software like Discord). Steam Deck contains two distinct digital sound controllers that behave almost the exact same way their desktop PC counterparts do, which makes such advanced audio settings both possible and nearly identical to how they would be on a desktop Arch Linux PC. Therefore, for now I consider such settings outside the scope of this guide, but if you struggle with audio settings, let me know and I will consider expanding this to cover that common audio use case.

OBS

  1. Install OBS
paru -S obs-studio
  1. If you do not have any unused dummy shortcuts to populate with OBS, launch KDE Plasma Xorg (Desktop Mode), open Steam and add OBS to Library as a non-Steam game, then right click on it, click Properties, and place the following text exactly as shown in the Launch Options box:
LD_PRELOAD= QT_QPA_PLATFORM=xcb %command%
  • There are three possible capture sources for OBS that currently work in gamescope. obs-kmsgrab used to work, and does still work in Xorg as a test, but I think it would need more updates to work with present-day gamescope:

OBS with FFmpeg

Unfortunately, this is a very messy method that encodes twice, stutters a lot, and sometimes looks green.

  1. Install FFmpeg

WARNING: setcap is a security risk and after running it, all users and software with executable permission for the affected binary can be considered effectively root-equivalent (in this context, they would definitely be able to see your screen for example). To remove setcap permissions from any binaries granted this, use sudo setcap -r /path/to/file.

paru -S ffmpeg
sudo setcap cap_sys_admin+ep $(readlink -f $(which ffmpeg))
  1. First create a new Scene in OBS, then add a new Media Source to the Scene and uncheck Local File. Then add the following and click OK
  • Input: udp://localhost:4444

  • Input Format: udp

  1. Switch to gamescope, navigate to Library and launch your terminal emulator, then execute this command; ffmpeg will crash anytime you leave the Steam client window by selecting a game or non-Steam app, and this should automatically restart it
while true; do ffmpeg -thread_queue_size 512 -framerate 60 -device /dev/dri/card1 \
     -f kmsgrab -i - -vaapi_device /dev/dri/renderD128 \
     -vf 'hwmap=derive_device=vaapi,scale_vaapi=format=nv12' \
     -c:v h264_vaapi -bf 1 -f mpegts udp://localhost:4444; done
  • You will also need to bind Ctrl+C to actually be able stop this capture with the least difficulty; navigate to "Controller settings" for your terminal emulator's non-Steam game entry and bind buttons to these keys, as pictured below. It is very possible that there is a better way of controlling ffmpeg without an external keyboard than this, but unfortunately I haven't found that way yet.

image

  1. Navigate to Library, launch OBS and start your stream or recording now. ffmpeg will often crash when you switch windows, but wait for a few seconds each time you do. The loop will automatically reset ffmpeg, and anything you can look at should be captured. When you are done, navigate to OBS and stop your stream or recording, then navigate to your terminal emulator and hold down your Ctrl+C keybind for a few seconds to kill the ffmpeg loop

Sometimes, ffmpeg will only record a small rectangular portion of the screen and the rest will be black. I don't know exactly why this happens and I haven't found any way to consistently prevent it, but it doesn't happen every single time, so if the source looks incomplete in OBS, kill the loop and try running it again until it looks acceptable.

Secret Section

Build and Install AUR package: suyu-dev-qt6-git

I've been doing this in private this whole time but wanted to document it somewhere. This changes all the time. Sometimes you'll have to edit lots of dependency packages, but sometimes when SteamOS is very close to upstream Arch Linux, it's not necessary to edit anything. This is a snapshot of the steps that were necessary in March 2024.

  1. Make some space if the 5GB root partition is too full to do this. If you need locales other than English, you would need to change the locale here to yours and also edit /etc/locale.conf.
sudo rm -rf /usr/share/{man,doc,gtk-doc}
sudo mv /usr/share/locale/locale.alias .
sudo rm -rf /usr/share/locale/*
sudo mv locale.alias /usr/share/locale/
echo 'es_US.UTF-8 UTF-8' | sudo tee /etc/locale.gen
sudo locale-gen
  1. Install SteamOS-specific dependencies and build Suyu. Accept all prompts.
paru -S vulkan-headers-git
paru -S suyu-dev-qt6-git

Special thanks to people who helped me with various parts of this guide: scaled#4479 logan2611#2351 kamae#7499 2zd9R#9421 and to the developers of all the software involved

#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir = /
DBPath = /usr/lib/holo/pacmandb/
#CacheDir = /var/cache/pacman/pkg/
#LogFile = /var/log/pacman.log
#GPGDir = /etc/pacman.d/gnupg/
#HookDir = /etc/pacman.d/hooks/
HoldPkg = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg =
#IgnoreGroup =
#NoUpgrade =
#NoExtract =
# Misc options
#UseSyslog
Color
#TotalDownload
# We cannot check disk space from within a chroot environment
CheckSpace
#VerbosePkgLists
ParallelDownloads = 10
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required
# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.
#
# REPOSITORIES
# - can be defined here or included from another file
# - pacman will search repositories in the order defined here
# - local/custom mirrors can be added here or in separate files
# - repositories listed first will take precedence when packages
# have identical names, regardless of version number
# - URLs will have $repo replaced by the name of the current repo
# - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
# [repo-name]
# Server = ServerName
# Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#
# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.
#[testing]
#Include = /etc/pacman.d/mirrorlist
[jupiter-3.5]
Include = /etc/pacman.d/mirrorlist
[holo-3.5]
Include = /etc/pacman.d/mirrorlist
[core-3.5]
Include = /etc/pacman.d/mirrorlist
[extra-3.5]
Include = /etc/pacman.d/mirrorlist
#[community-testing]
#Include = /etc/pacman.d/mirrorlist
[community-3.5]
Include = /etc/pacman.d/mirrorlist
[multilib-3.5]
Include = /etc/pacman.d/mirrorlist
# An example of a custom package repository. See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs
diff --git a/PKGBUILD b/PKGBUILD
index 0d16677..b1c19e8 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -76,7 +76,9 @@ build() {
-Wno-dev \
-D CMAKE_INSTALL_PREFIX=/usr \
-D SUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
- -D SUNSHINE_ASSETS_DIR="share/sunshine"
+ -D SUNSHINE_ASSETS_DIR="share/sunshine" \
+ -D SUNSHINE_ENABLE_TRAY=OFF \
+ -D SUNSHINE_REQUIRE_TRAY=OFF
make -C build
}
@robertkirkman
Copy link
Author

In case this is not widely known, there is a decky loader plugin already that supports video recording

Thanks I added a link to that too. The truth is, while this guide was the first ever example of recording the Gaming Mode screen immediately after SteamOS 3 released in 2022, it evolved into primarily:

  • my personal disabled-read-only technique
  • specifically Sunshine Stream for SteamOS Gaming Mode, which is the streaming method I usually use because of its game netplay (gamepad input) and remote desktop (keyboard and mouse input) features, and which is open source unlike Steam Remote Play

While that was happening, many other people created their own methods to suit their needs, including the one you mentioned, and I think it might be good for me to just link some of them this way so that people can easily find the method that has all the features they need.

@rondhi
Copy link

rondhi commented Aug 23, 2023

@robertkirkman Hey thanks for linking my guide! I haven't updated my guide in a while so I don't know if it still works, but in the comments on my gist, someone came up with an easy way to capture the screen using the flatpack OBS Studio and gstreamer obs plugin. It's super easy to set up since you don't even need to touch the command line.

@s0th1s
Copy link

s0th1s commented Mar 3, 2024

I just wanted to clarify what the setcap command is for "(which sunshine )". What is it supposed to be set to?

  1. Enable DRM capture mode in sunshine
    sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))

@robertkirkman
Copy link
Author

robertkirkman commented Mar 4, 2024

I just wanted to clarify what the setcap command is for "(which sunshine )". What is it supposed to be set to?

2. Enable DRM capture mode in sunshine
   sudo setcap cap_sys_admin+p (readlink−f(which sunshine))

If I understand your question correctly: for me, that command is exactly equivalent to this one:

sudo setcap cap_sys_admin+p /usr/bin/sunshine-0.21.0

If you try to use this version of the command and it says /usr/bin/sunshine-0.21.0 is not found, then you would have a different version of sunshine from the one this page currently gives, and would need to figure out a way to apply the same operation to your version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment