Repository navigation
Feat/scrcpy h264 stream - #431
Open
DaniilSmirnov wants to merge 6 commits into
Open
DaniilSmirnov wants to merge 6 commits into
DaniilSmirnov wants to merge 6 commits into
Conversation
--need-scrcpy started a scrcpy server from 2019 that modern Android does not run, with a broken path (__dirname in ESM), a console.log per chunk, a listener leaked per WebSocket client, and nothing in the UI to decode it. Device side: - runs the official scrcpy-server v5.0.1, video only (H.264); input stays on minitouch and STFService. It connects through localabstract:scrcpy_<scid> like minicap, no adb forward; - parses its stream (codec id, session packets, 12-byte frame headers) and relays each packet to the screen WebSocket clients as is; - a client that joins gets the session, config and packets since the last key frame; a client whose socket backs up skips to the next key frame instead of queueing delay; - rotation sends a new banner instead of a restart: scrcpy follows it; - falls back to minicap when the server is missing or cannot start; - --scrcpy-bit-rate sets the bit rate, --screen-frame-rate caps the fps. The server is not committed: scripts/fetch-scrcpy-server.mjs downloads it and checks the SHA-256 Homebrew pins for the release; the Docker build runs it. UI: the "start" banner carries codec "h264"; the screen store then decodes with WebCodecs and draws only the latest frame per animation frame. Browsers without WebCodecs get an explicit error.
The quality slider (10-80, minicap's JPEG quality) now sets the scrcpy bit rate: --scrcpy-bit-rate is the rate at 80, the top of the slider, and lower qualities scale it down, with a 200 kbit/s floor. scrcpy-server cannot change its bit rate while running, so a new quality restarts it. The same quality does not: auto quality sends it again every few seconds, and a restart freezes the screen for a second or two. A quality chosen while the server is about to start is used as is.
adbkit resolves push() only after the transfer has ended, so the 'end' listener attached afterwards never fired and the scrcpy resource never resolved. Every Android device stopped at that point of setup and stayed disconnected. waitForEnd() resolves for an already finished transfer.
The fake emitted 'end' after push() resolved, which hid the hang fixed in the previous commit. With the old code this test now times out.
Right after a session ends the device is cleaned up, and a scrcpy server started then took over 2 s to listen. The 2 s connect window ran out, and the device fell back to minicap for good. Wait up to 10 s.
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.
Stream Android screens as H.264 with scrcpy 5
--need-scrcpyexisted but could not work: it started a scrcpy server from 2019 (v1.0.20) that modern Android does not run,scrcpy.jsused__dirnamein an ES module, logged every chunk withconsole.log, leaked a listener per WebSocket client, and the UI had nothing to decode the stream.This PR makes it a working alternative to minicap: the device streams H.264 from the official scrcpy-server v5.0.1, and the browser decodes it with WebCodecs. minicap stays the default.
Device side
resources/scrcpy.jsruns scrcpy-server video-only (audio=false control=false video_codec=h264). Input still goes through minitouch and STFService. It connects throughlocalabstract:scrcpy_<scid>withopenLocal, like minicap, so noadb forwardport is needed.resources/util/scrcpy-stream.tsparses the v5 stream: codec id, session packets (new size on rotation), 12-byte frame headers with config/key-frame flags.plugins/screen/util/h264relay.tssends each packet to the screen WebSocket clients unchanged:bufferedAmount) skips to the next key frame instead of queueing delay.plugins/screen/stream.js: scrcpy is a second mode of the existingFrameProducerstate machine.startbanner instead of restarting, because scrcpy follows the rotation itself.--scrcpy-bit-rate(default 4 Mbit/s) is the bit rate at quality 80, the top of the slider, and lower values scale it down (minimum 200 kbit/s). Sending the same quality again does not restart the stream; auto quality does this every 5 s.--screen-frame-ratesetsmax_fps. Key frames come every 2 s so that new viewers and viewers that fell behind recover quickly.Server binary
The jar is no longer committed.
scripts/fetch-scrcpy-server.mjsdownloads the official release intovendor/scrcpy/and checks its SHA-256, which is the checksum Homebrew pins for the same release. The Docker build runs the script. Elsewhere, run it once by hand. Without the binary, devices use minicap.UI
startbanner hascodec: "h264"in scrcpy mode.DeviceScreenStorethen sends the packets toH264ScreenDecoder(WebCodecsVideoDecoder, Annex B with SPS/PPS sent with each key frame) and draws only the latest decoded frame on each animation frame.binaryType = 'arraybuffer'; JPEG frames are wrapped in aBlobas before.localhost.How to try
Testing
Known limitations