Skip to content

add SFU#248

Open
ObiWahn wants to merge 15 commits into
screego:masterfrom
ObiWahn:master
Open

add SFU#248
ObiWahn wants to merge 15 commits into
screego:masterfrom
ObiWahn:master

Conversation

@ObiWahn

@ObiWahn ObiWahn commented Jun 18, 2026

Copy link
Copy Markdown

Hi,

I had the need for SFU and vibe coded a bit. It seems to be working fine, but that might be an illusion. Maybe we can use this as a base to come forward with a PR that can eventually land on master.

ObiWahn added 10 commits June 18, 2026 20:44
The v5 API changes RelayAddressGenerator to accept config structs instead
of (network, port) params, adds AllocateListener, and changes AuthHandler
to (*RequestAttributes) returning (userID, key, ok).

Eliminates the duplicate pion/turn/v4+v5 and pion/transport/v3+v4 pairs
that would otherwise coexist once pion/webrtc/v4 is added.
Signals to the browser whether to use SFU or P2P signalling for this
session. Old clients that ignore unknown JSON fields are unaffected.
Introduce SFUHost struct (per-sharer server-side WebRTC state) and a
SFUHosts map on Room keyed by sharer user ID, enabling multiple
simultaneous sharers in SFU mode.

Add ViewerPC to RoomSession for the server-side sendonly PC used when
forwarding a sharer's stream to a viewer. Add helper methods
sfuHostBySession, closeSFUHost, and closeAllSFUHosts for lifecycle
management.

Extend Rooms with a webrtcAPI field initialised once at startup when
SCREEGO_SFU_MODE=true, shared across all peer connections so codec
negotiation state is consistent. Call closeAllSFUHosts when a room is
torn down.
Add sfu.go with createHostPC/createViewerPC for the server-side WebRTC
PeerConnections. createHostPC opens a recvonly PC to receive the sharer's
stream; createViewerPC opens a sendonly PC per viewer and forwards the
shared track. PLI requests from viewers are forwarded to the sharer via
the main event loop to stay single-threaded.

Add event_sfu_internal.go with the four internal event types that pion
goroutines post into rooms.Incoming:
  SFUHostTrack        — track arrived from sharer; starts RTP forward
  SFUIceCandidate     — routes server ICE candidates to browser peers
  SFUSendPLI          — requests a keyframe from the sharer
  SFUConnectionFailed — tears down a host or viewer PC on failure
Wire SCREEGO_SFU_MODE into all eight signaling events:

  share       — creates SFUHost entry + pending list, calls createHostPC
  stopshare   — closes the SFUHost and its viewer sessions
  join        — late-joiner gets a ViewerPC if track is ready, otherwise
                added to the sharer's pending list
  hostoffer   — ignored in SFU mode (server originates all offers)
  hostice     — routes sharer browser ICE to the server host PC
  clientanswer — routes sharer answer to host PC; viewer answer to ViewerPC
  clientice   — routes viewer browser ICE to the server ViewerPC
  disconnected — closes SFUHost when a streaming user disconnects

P2P behaviour (SFUMode=false) is unchanged in every handler.
The server sets mode: "sfu" on hostsession messages when
SCREEGO_SFU_MODE=true so the browser can switch to upload-only
peer connection logic. Absent means p2p (existing behaviour).
When the server sends mode: "sfu" on a hostsession event, create an
upload-only RTCPeerConnection instead of calling hostSession(). Track
the session ID in hostSID so the hostoffer handler can identify the
sharer's PC and set the remote description (server's recvonly offer)
rather than routing through the viewer path.

Codec preference is applied after setRemoteDescription so the correct
transceiver direction is known. Viewer hostoffer handling is unchanged.

stopShare cleans up only the upload PC in SFU mode; in P2P mode it
closes all host PCs as before.
Standalone HTML page (no build step) that joins a room as a viewer,
logs every WebSocket message and ICE event, and displays the forwarded
stream. Useful for isolating server-side SFU issues from the main UI.

Placed in ui/public/ so vite copies it to build/ unchanged. Registered
in serve.go so it is reachable at /test-client.html in production too.
ObiWahn added 5 commits June 19, 2026 10:01
/test-client.html is now only registered when SCREEGO_TEST_CLIENT=true.
Defaults to false so the debug endpoint is off in production deployments.
Eye button in the control bar hides/shows the thumbnail strip.
When hidden with no stream selected nothing renders, saving GPU.

X button overlaid top-right deselects the current main stream.
An explicitDeselect ref prevents the auto-select effect from
immediately re-selecting after an explicit deselect.
Clients can subscribe to individual sharers on demand and unsubscribe
to release the connection and stop receiving RTP.

- Subscribe event: creates viewer PC (SFU) or P2P session on demand;
  defers to Pending if the SFU track has not arrived yet
- Unsubscribe event: closes viewer PC (SFU) or notifies sharer (P2P);
  removes from Pending if not yet connected
- sessionByPeers helper added to Room for session lookup by (host, client)
Eye off: unsubscribes all non-selected thumbnail streams and closes
their PCs. Eye on: re-subscribes any streaming users not yet present
in clientStreams. A useEffect auto-unsubscribes streams that arrive
while the strip is hidden. X button also unsubscribes the main stream
when the strip is off.

message.ts: add Subscribe and Unsubscribe outgoing message types.
useRoom.ts: add subscribeStream/unsubscribeStream; track session→peer_id
in clientPeerID ref so unsubscribe sends the correct sharer user ID.
Previously auth_mode=all only blocked room creation for unauthenticated
users; joining (and thus watching) was open to anyone. Now guests are
rejected on join with the same error as on create.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant