Naprawa zawieszania grabbera wideo (MS2109) na Linuksie w OBS

Problem: Tanie karty capture (np. na chipie MS2109) po około godzinie pracy gubią pakiety, wywołując błąd transferu URB (err = -19) oraz zawieszenie strumienia w OBS. Dodatkowo tradycyjny reset gubi powiązanie podsystemu audio.

Poniższy poradnik przedstawia kompleksowe rozwiązanie: globalną blokadę oszczędzania energii, trwałe wpięcie parametrów w GRUB oraz instalację autonomicznego watchdoga, który w razie awarii samoczynnie resetuje port USB i przywraca zarówno wideo, jak i dźwięk (PipeWire).

Krok 1: Znalezienie ID swojego urządzenia

Zanim przystąpisz do konfiguracji, musisz poznać identyfikator Vendor ID oraz Product ID swojego grabbera (np. popularny chip MS2109 ma zazwyczaj ID 345f:2131).

  1. Wepnij grabber do portu USB (zalecany jest dedykowany kontroler USB 3.0).
  2. Uruchom poniższe polecenie w terminalu:
lsusb

Wśród wyświetlonych linii poszukaj wpisu powiązanego z urządzeniem wideo lub capture, np.:
Bus 002 Device 002: ID 345f:2131 UltraSemi USB3 Video
Ciąg 345f:2131 to Twoje ID urządzenia, które należy podstawić w dalszych konfiguracjach.

Krok 2: Konfiguracja modułu jądra i trwałość w GRUB

1. Blokada w module jądra (usbcore)

Tworzymy plik konfiguracyjny modułu usbcore, aby globalnie wyłączyć oszczędzanie energii (autosuspend) dla kontrolerów USB.

sudo tee /etc/modprobe.d/usbcore.conf << 'EOF'
options usbcore autosuspend=-1
options usbcore.quirks=345f:2131:k
EOF

2. Trwałe wpięcie parametrów rozruchowych w GRUB (Opcjonalne, ale zalecane)

Aby mieć pewność, że jądro systemu od pierwszej sekundy rozruchu zignoruje zarządzanie energią LPM dla Twojego konkretnego grabbera, warto przekazać regułę bezpośrednio w parametrach startowych linuksa.

  • Otwórz plik konfiguracyjny programu rozruchowego w edytorze tekstowym:
sudo nano /etc/default/grub

Znajdź linijkę zaczynającą się od GRUB_CMDLINE_LINUX_DEFAULT="..." i dopisz wewnątrz cudzysłowów parametry usbcore.autosuspend=-1 oraz usbcore.quirks=TWOJE_ID:k (np. 345f:2131:k), oddzielając je spacjami od reszty opcji. Przykład:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash usbcore.autosuspend=-1 usbcore.quirks=345f:2131:k"

Zapisz plik (w edytorze nano: Ctrl + O, zatwierdź Enter, a następnie wyjdź przez Ctrl + X). Następnie zaktualizuj konfigurację programu GRUB w systemie:

sudo update-grub

3. Globalna usługa systemd wymuszająca tryb "on" dla portów

sudo tee /etc/systemd/system/usb-global-keepalive.service << 'EOF'
[Unit]
Description=Disable USB Autosuspend Globally for All Ports
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'for d in /sys/bus/usb/devices/*/power/control; do [ -f "$d" ] && echo on > "$d"; done; for s in /sys/bus/usb/devices/*/power/autosuspend; do [ -f "$s" ] && echo 0 > "$s"; done'
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
EOF

4. Aktywacja usługi keepalive

sudo systemctl daemon-reload
sudo systemctl enable --now usb-global-keepalive.service

Krok 3: Instalacja automatycznego Watchdoga (Wideo + Audio)

Aby system samoczynnie radził sobie z awarią bez konieczności fizycznego wypinania kabla, zainstalujemy inteligentny skrypt monitorujący logi jądra (dmesg). W przypadku wystąpienia błędu zresetuje on port oraz automatycznie zrestartuje stos audio PipeWire dla użytkownika.

Utworzenie i uruchomienie skryptu instalacyjnego

Wklej poniższe polecenia w terminalu, aby utworzyć i uruchomić instalator:

cat << 'EOF' > install-watchdog.sh
#!/bin/bash

if [ "$EUID" -ne 0 ]; then
  echo "Uruchom skrypt z uprawnieniami administratora: sudo ./install-watchdog.sh"
  exit 1
fi

REAL_USER=${SUDO_USER:-$USER}
if [ "$REAL_USER" = "root" ]; then
  echo "Błąd: Nie udało się określić nazwy zwykłego użytkownika."
  exit 1
fi

echo "Wykryto użytkownika: $REAL_USER"
echo "Tworzenie skryptu monitorującego..."

cat << EOB > /usr/local/bin/usb-grabber-watchdog.sh
#!/bin/bash

TARGET_ID="345f:2131"
TARGET_USER="$REAL_USER"

echo "Uruchomiono watchdog dla grabbera \$TARGET_ID..."

stdbuf -oL dmesg -w | while read -r line; do
    if echo "\$line" | grep -qE "usb.*(345f:2131|err = -19|reset SuperSpeed)"; then
        echo "[\$(date)] Wykryto awarię grabbera! Resetowanie portu USB..."

        for dev in /sys/bus/usb/devices/*; do
            if [ -f "\$dev/idVendor" ] && [ -f "\$dev/idProduct" ]; then
                vid=\$(cat "\$dev/idVendor" 2>/dev/null)
                pid=\$(cat "\$dev/idProduct" 2>/dev/null)
                if [ "\$vid\$pid" = "345f2131" ]; then
                    devpath=\$(basename "\$dev")
                    echo "Znaleziono urządzenie na porcie: \$devpath. Resetuję..."

                    if [ -f "\$dev/authorized" ]; then
                        echo 0 > "\$dev/authorized"
                        sleep 1
                        echo 1 > "\$dev/authorized"

                        sleep 2

                        echo "Odświeżanie stosu PipeWire/WirePlumber dla \$TARGET_USER..."
                        USER_UID=\$(id -u "\$TARGET_USER" 2>/dev/null)
                        if [ -n "\$USER_UID" ]; then
                            su - "\$TARGET_USER" -c "XDG_RUNTIME_DIR=/run/user/\$USER_UID systemctl --user restart pipewire wireplumber" 2>/dev/null || true
                        fi
                    fi
                fi
            fi
        done
    fi
done
EOB

chmod +x /usr/local/bin/usb-grabber-watchdog.sh

cat << 'EOB' > /etc/systemd/system/usb-grabber-watchdog.service
[Unit]
Description=Auto-reset watchdog for USB Video Grabber (MS2109 Video & Audio)
After=multi-user.target

[Service]
ExecStart=/usr/local/bin/usb-grabber-watchdog.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOB

systemctl daemon-reload
systemctl enable --now usb-grabber-watchdog.service
echo "Instalacja zakończona sukcesem!"
EOF

chmod +x install-watchdog.sh
sudo ./install-watchdog.sh

Krok 4: Weryfikacja działania usługi

Możesz w każdej chwili sprawdzić status watchdoga poleceniem:

sudo systemctl status usb-grabber-watchdog.service

Cofnięcie zmian / Deinstalacja

sudo systemctl disable --now usb-grabber-watchdog.service
sudo rm /etc/systemd/system/usb-grabber-watchdog.service
sudo rm /usr/local/bin/usb-grabber-watchdog.sh
sudo systemctl disable --now usb-global-keepalive.service
sudo rm /etc/systemd/system/usb-global-keepalive.service
sudo rm /etc/modprobe.d/usbcore.conf
sudo systemctl daemon-reload