Refactor: Per-device WebSocket streams to fix backpressure + cross-browser codec support#125
Merged
RoiArthurB merged 5 commits intodevfrom Mar 6, 2026
Merged
Refactor: Per-device WebSocket streams to fix backpressure + cross-browser codec support#125RoiArthurB merged 5 commits intodevfrom
RoiArthurB merged 5 commits intodevfrom
Conversation
…ix backpressure
Each Android device now gets a dedicated WebSocket at /stream/:deviceIp (IP only,
port stripped for stability across ADB reconnects). All packets (config then data)
flow in order on the same socket, eliminating config/data ordering races.
The existing ws('/*') becomes a lightweight control channel that only carries codec
negotiation and stream_available announcements — no video data. Clients open a
per-device socket when they receive an announcement, and reconnect automatically
after 1s on close. The upgrade handler validates against activeStreams dynamically
so there is no hardcoded stream count. Each stream gets an independent 8 MB
backpressure budget with safeSend() dropping frames per-stream instead of globally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Null out `existing.onmessage` before closing the old device socket so
queued h265 packets in the receive buffer don't feed a newly created
h264 decoder, eliminating the `Invalid data at h265SearchConfiguration` crash
- Add `streamIsH265` map to detect mid-stream codec changes and reset
decoder state when a config packet arrives with a different codec
- Remove stale canvas element via `querySelector('canvas')?.remove()`
before appending the new one in PlayerScreenCanvas, so the DOM never
holds two overlapping canvases after a stream restart
…codec switch Client (VideoStreamManager.tsx): - Pass hardwareAcceleration: "prefer-software" to WebCodecsVideoDecoder for h264 so Firefox uses its software decoder (OpenH264) instead of failing on the hardware WebCodecs path, fixing "DOMException: The given encoding is not supported" Server (ScrcpyServer.ts): - Remove TinyH264Decoder videoCodecOptions restriction — encoding profile/level is now left to the Android encoder since decoding is done by WebCodecs in the browser (not TinyH264), removing an unnecessary Baseline-only constraint - Clear activeStreams + scrcpyClientsByIp immediately when a codec switch starts so control clients reconnecting during the transition get no stale stream_available announcements - Guard exited handlers with scrcpyClientsByIp.get(streamIp) === client check so a departing client's async exit callback never wipes the new client's registration, which was the root cause of stream_available being lost after a codec switch
- Cleaning some weird vibe-coded left-over - Simplify un-necessary `myself` in the backend - Entirely remove TinyH264 dependencies - All fully managed by upgraded WebCodecs - Rebuilt package-lock without chained dependencies - Remove old comments
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Pull Request
Checklist
Description
Problem
Streaming 6 concurrent video streams over a single shared WebSocket caused backpressure overflow
Additionally, h265→h264 fallback (tested with Firefox on Linux) caused crashes, canvas duplication and other visual glitches.
What changed
Architecture —
ScrcpyServer.ts/stream/:ip— each stream gets an independent 8 MB backpressure budgetws('/*')is now a lightweight control channel only: codec negotiation (client→server) and stream_available announcements (server→client)serial.split(':')[0]), stable across ADB reconnects and port changesStability fixes —
ScrcpyServer.tsscrcpyClientsByIp.get(ip) === clientbefore cleaning up, preventing an old client's async exit from wiping a new client's registration (root cause of stream_available being lost after codec switch)activeStreamsis cleared immediately at codec switch start so reconnecting clients don't receive stale announcements during the transitionClient —
VideoStreamManager.tsx/stream/:ipWebSocket per device on stream_available, with 1s auto-reconnectonmessageis nulled before replacement, preventing queued packets from the old codec from feeding the new decoderhardwareAcceleration: "prefer-software"for H264 enables Firefox's software decoder (OpenH264) and avoids DOMException: The given encoding is not supported on LinuxClient —
PlayerScreenCanvas.tsxquerySelector('canvas')?.remove()before appendChild ensures the stale canvas from a previous decoder is removed from the DOM on stream restartBEFORE MERGING !!
Need to be fully tested before merging !! :