Skip to content

Emulator streaming#3211

Open
LoneAngelFayt wants to merge 2 commits into
rommapp:masterfrom
LoneAngelFayt:feat/pcsx2-streaming-v2
Open

Emulator streaming#3211
LoneAngelFayt wants to merge 2 commits into
rommapp:masterfrom
LoneAngelFayt:feat/pcsx2-streaming-v2

Conversation

@LoneAngelFayt

@LoneAngelFayt LoneAngelFayt commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a streaming framework to RomM that enables launching games directly from the RomM UI into a native emulator running in a separate container, with full session management, save state control, and volume control, all without leaving the browser.

The emulator runs in a linuxserver Docker container with a Selkies WebRTC stream. A small HTTP broker sidecar inside that container handles game launch, extraction, and I/O. RomM communicates with the broker via a new /api/streaming backend endpoint and displays the stream in an embedded player.

This initial PR covers four emulators. Additional integrations (PS3/rpcs3, etc.) will follow in separate PRs once the framework is established.

Supported emulators

Platform slug Emulator Save states Slots Autosave slot Archive extraction
ps2 PCSX2 9 Slot 10 ❌ direct file only
ngc, wii, wiiu Dolphin 7 Slot 8 ❌ direct file only
xbox xemu 9 Slot 10 ❌ direct file only
switch Eden - - ❌ direct file only

Architecture

RomM frontend
  └── StreamingStore (Pinia)
        ├── fetchConfig()       - loads container list from backend on app start
        ├── claimSession()      - POST /api/streaming/sessions
        ├── saveState/loadState - proxied to broker via backend
        ├── saveAndExit()       - save + release session
        └── setVolume/setMute   - PulseAudio control via broker

RomM backend  (/api/streaming/*)
  ├── GET  /config              - returns enabled flag + container list
  ├── POST /sessions            - claims a session, proxies launch to broker
  ├── POST /sessions/{platform}/save-state
  ├── POST /sessions/{platform}/load-state
  ├── POST /sessions/{platform}/save-and-exit
  ├── POST /sessions/{platform}/volume
  ├── POST /sessions/{platform}/mute
  └── DELETE /sessions/{platform} - releases session

Emulator container (linuxserver + broker sidecar)
  ├── POST /launch              - kill current game, launch the requested ROM
  ├── GET  /status              - current session state
  ├── POST /save-state          - hotkey inject, poll for write confirmation
  ├── POST /load-state          - hotkey inject (fire-and-forget)
  ├── POST /save-and-exit
  ├── POST /volume              - pactl set-sink-volume
  └── POST /mute

Configuration (config.yml)

streaming:
  enabled: true
  containers:
    - platform: ps2
      host: https://192.168.1.51:3001       # Selkies WebRTC UI (browser-facing, must be HTTPS)
      broker_host: http://192.168.1.51:8000 # Broker API (server-to-container, HTTP ok)
      label: PCSX2
    - platform: ngc
      host: https://192.168.1.51:3002
      broker_host: http://192.168.1.51:8001
      label: Dolphin

Each emulator is its own container entry. Multiple platforms can share a container (e.g. ngc/wii/wiiu all pointing at the same Dolphin instance) or use separate ones. The host is the URL the browser loads the stream from; broker_host is the server-side URL RomM uses to send commands.

Two optional env vars: BROKER_SECRET authenticates broker API calls (set on both the RomM container and the emulator container), and STREAMING_SAVE_TIMEOUT raises the save-and-exit wait for brokers that save slowly (default 45s).

Changes

Backend

  • backend/endpoints/streaming.py - new router: session management, broker proxy, config endpoint. Sessions are claimed by ROM id (the filesystem path is derived server-side from the database, never taken from the client), stored in Redis via atomic SET NX (multi-worker safe), and bound to the claiming user; only the owner or an admin can control or release one.
  • backend/tests/endpoints/test_streaming.py - 19 endpoint tests: auth scopes, claim races, ownership, broker failure paths, force-release.
  • backend/config/config_manager.py - STREAMING_ENABLED + STREAMING_CONTAINERS config fields
  • backend/main.py - registers streaming router

Frontend

  • frontend/src/stores/streaming.ts - Pinia store: config, session lifecycle, per-platform capabilities
  • frontend/src/views/Player/Stream/Player.vue - streaming player view: Selkies iframe, loading overlay, save state controls, volume/mute
  • frontend/src/components/common/Game/PlayBtn.vue - "Play on [label]" button appears when a streaming container is configured for the platform
  • frontend/src/layouts/Main.vue - streaming config fetched on app start
  • frontend/src/plugins/router.ts - rom/:rom/stream route

Config

  • examples/config.example.yml - streaming config example with comments
  • env.template - BROKER_SECRET and STREAMING_SAVE_TIMEOUT variables

The player view follows the existing v1 player pattern (same shape as the EmulatorJS/Ruffle views). The route also registers a v2 named view via v2For, so v2 users get the standard "not ready yet" fallback; a native v2 player is planned as a follow-up once this framework lands.

Broker containers

Each emulator has a companion Docker mod repo with the broker sidecar (stdlib-only Python, no extra dependencies):

Each repo includes a docker-compose.yml showing the full setup.

UX flow

  1. User clicks Play on PCSX2 (button appears only when a container is configured for that platform)
  2. RomM claims a session; the broker kills any running game and launches the requested ROM
  3. Loading overlay shows a spinner while the emulator starts
  4. Once running: stream fills the player; save state controls and volume appear in the toolbar
  5. Save & Exit saves state then returns to library; session is released for other users

AI assistance disclosure

This PR was developed with substantial AI assistance (Claude Code). I designed the architecture, made the design decisions, and tested everything against real emulator containers on my own hardware; the majority of the code was written by the AI under my direction and review, and the branch went through multiple AI-assisted review passes before submission.

Checklist
Please check all that apply.

  • I've tested the changes locally
  • I've updated relevant comments
  • I've assigned reviewers for this PR
  • I've added unit tests that cover the changes

Screenshots (if applicable)

@LoneAngelFayt

Copy link
Copy Markdown
Contributor Author

here is the broker that goes into the pcsx2 container if you want to take a look at the api inside

https://github.com/LoneAngelFayt/pcsx2-romm-integration

@rommapp rommapp deleted a comment from LoneAngelFayt Apr 7, 2026
@LoneAngelFayt LoneAngelFayt marked this pull request as ready for review April 10, 2026 03:01
@rommapp rommapp deleted a comment from webysther Apr 11, 2026
@LoneAngelFayt LoneAngelFayt changed the title Feat/emulator streaming Feat: emulator streaming -- streaming framework and pcsx2 integration Apr 11, 2026
@LoneAngelFayt LoneAngelFayt changed the title Feat: emulator streaming -- streaming framework and pcsx2 integration feat: emulator streaming -- streaming framework and pcsx2 integration Apr 12, 2026
@LoneAngelFayt

Copy link
Copy Markdown
Contributor Author

I've finished setting up the integration for dolphin as well, I'm going to split it out into a different PR for each integration

There is thankfully minimal changes but I turn some hardcoding into variables

Once you get done looking through this I'll adapt the others and put them in as I have them completed

@LoneAngelFayt LoneAngelFayt force-pushed the feat/pcsx2-streaming-v2 branch from 9c8da66 to 6a0e5ff Compare April 28, 2026 14:53
@LoneAngelFayt LoneAngelFayt changed the title feat: emulator streaming -- streaming framework and pcsx2 integration add emulator streaming Apr 28, 2026
@LoneAngelFayt

Copy link
Copy Markdown
Contributor Author

@gantoine I changed my mind, and just added all the emulators I've gotten working along with their respective changes.

I recruited Claude to help make my PR description a little better as well. Should outline everything I've touched and how it all works.

@gantoine gantoine added the on-hold Pending further research or blocked by another issue label Apr 29, 2026
@gantoine gantoine self-requested a review April 29, 2026 12:33
@gantoine gantoine changed the title add emulator streaming Emulator streaming May 27, 2026
@LoneAngelFayt LoneAngelFayt force-pushed the feat/pcsx2-streaming-v2 branch from b44b7dc to 6818507 Compare June 13, 2026 16:13
Add /api/streaming endpoints for session claim/release, ROM launch,
save states, volume control, and save-and-exit against per-platform
emulator broker containers. Sessions are stored in Redis with atomic
SET NX claims, owner-bound, and admin force-releasable. Config comes
from config.yml (streaming.enabled + containers list) with a
STREAMING_SAVE_TIMEOUT env override for slow-saving brokers.

Includes 19 endpoint tests covering auth scopes, claim races,
ownership, broker failure paths, and force-release.
Add a Pinia streaming store (config fetch, session lifecycle, broker
controls), a v1 player view at rom/:rom/stream with save state and
volume controls, and a cast button on PlayBtn for platforms with a
configured streaming container. The route registers a v2 named view
via v2For so the v2 fallback screen shows until a v2 player lands.
@LoneAngelFayt LoneAngelFayt force-pushed the feat/pcsx2-streaming-v2 branch from 6818507 to d94c5b6 Compare July 8, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

on-hold Pending further research or blocked by another issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants