@9b9387/android-stream-scrcpy
v0.1.1
Published
Versioned scrcpy protocol client for Node.js/Electron
Readme
@9b9387/android-stream-scrcpy
TypeScript scrcpy client library for Node.js and Electron. It starts scrcpy-server through @devicefarmer/adbkit, reads the video/audio/control sockets, and exposes media packets as Web Standards binary types (Uint8Array).
The bundled protocol implementation targets scrcpy 4.0. Protocol code is versioned so future versions, such as 4.1, can be added side by side without rewriting the backend or service layers.
Project Structure
node-lib/
assets/
scrcpy-server-v4.0.jar
examples/
demo.ts
server.ts
src/
backend/
adb/
scrcpy-adb-client.ts
io/
buffered-stream-reader.ts
server/
options.ts
server-command.ts
socket-name.ts
scrcpy-backend.ts
index.ts
protocol/
core/
binary.ts
types.ts
registry.ts
v4_0/
codecs.ts
control-message.ts
control-message-types.ts
device-message.ts
frame-header.ts
server-options.ts
string-payload.ts
index.ts
index.ts
service/
packet-queue-subscriber.ts
scrcpy-stream-service.ts
types.ts
index.ts
websocket/
binary-packet.ts
control-json.ts
scrcpy-websocket-bridge.ts
types.ts
index.ts
index.tsModule Responsibilities
src/protocol/: scrcpy wire protocol implementation.core/contains shared binary helpers and protocol interfaces;registry.tsselects a protocol adapter by version;v4_0/contains scrcpy 4.0 frame, control, device, codec, and server-option logic.src/backend/: adbkit-only device integration.adb/wraps adbkit operations,io/contains socket reading utilities,server/builds and normalizes scrcpy server launch details, andscrcpy-backend.tsorchestrates the streaming connection.src/service/: public stream service. It manages state, caches decoder config/session snapshots, and exposesfor await...ofmedia packet subscriptions.src/websocket/: optionalwsbridge for browser clients. It is exported from the./websocketsubpath and is not part of the root API.examples/: runnable examples kept outside the library build.
Install And Build
cd node-lib
npm install
npm run buildCore runtime dependency is only @devicefarmer/adbkit. The library does not call the adb system command directly.
If your application uses the optional WebSocket bridge, install ws in the application:
npm install wsRun Examples
Run the console stream demo:
npm run example:demoRun the browser demo server:
npm run example:serverThe server serves the existing demo page from the repository root:
../web_demo/scrcpy.htmlThen open:
http://localhost:8000You can also request the page directly:
http://localhost:8000/scrcpy.htmlChoose a specific connected Android device by serial:
ANDROID_SERIAL=<device-serial> npm run example:serverADB_SERIAL is also supported for compatibility with existing local workflows.
Enable the optional Node-side screenshot cache for the browser demo:
SNAPSHOT_ENABLED=1 SNAPSHOT_FPS=2 SNAPSHOT_JPEG_QUALITY=85 npm run example:serverThis requires ffmpeg on PATH by default. Set FFMPEG_PATH=/path/to/ffmpeg
if the binary lives elsewhere. When enabled, the demo exposes:
http://localhost:8000/screenshot.jpgThe scrcpy.html page also shows a 截图 button that downloads the latest
cached JPEG. The cache is produced from the scrcpy H.264 stream in Node; it does
not call ADB screencap and does not require a browser canvas.
Integrate The Core Library
import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
const service = new ScrcpyStreamService({
protocolVersion: "4.0",
maxSize: 1080,
video: true,
audio: true,
control: true,
});
const meta = await service.start();
console.log(`Connected to ${meta.deviceName}`);
for await (const packet of service.subscribe()) {
// packet.kind: "video" | "audio" | "session"
// packet.payload: Uint8Array
if (packet.kind === "video") {
// Feed H.264/H.265/AV1 bytes into your decoder.
}
}Stop streaming when your application shuts down:
service.stop();Send Control Messages
import {
ControlMessageType,
KEY_ACTION_DOWN,
ScrcpyStreamService,
} from "@9b9387/android-stream-scrcpy";
const service = new ScrcpyStreamService();
await service.start();
service.sendControlMessage({
type: ControlMessageType.INJECT_KEYCODE,
action: KEY_ACTION_DOWN,
keycode: 26,
});The scrcpy 4.0 adapter includes key, text, touch, scroll, clipboard, UHID, app start, display power, camera, and display resize control messages.
Integrate The WebSocket Bridge
The WebSocket bridge is optional and imported from a subpath:
import { createServer } from "node:http";
import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
import { ScrcpyWebSocketBridge } from "@9b9387/android-stream-scrcpy/websocket";
const service = new ScrcpyStreamService({ protocolVersion: "4.0" });
const server = createServer();
const bridge = new ScrcpyWebSocketBridge(service, { server });
await service.start();
server.listen(8000);
process.on("SIGINT", () => {
bridge.close();
service.stop();
server.close();
});The bridge sends an initial JSON init message, then binary media packets with a 16-byte header followed by the media payload.
Integrate The Screenshot Cache
The screenshot cache is optional and imported from the ./snapshot subpath:
import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
import { FfmpegSnapshotCache } from "@9b9387/android-stream-scrcpy/snapshot";
const service = new ScrcpyStreamService({ videoCodec: "h264" });
const snapshots = new FfmpegSnapshotCache(service, {
enabled: true,
fps: 2,
quality: 85,
});
await service.start();
snapshots.start();
const latest = snapshots.latest();
if (latest) {
// latest.contentType === "image/jpeg"
// latest.data is a Buffer containing JPEG bytes.
}The first implementation supports H.264 input and JPEG output. The configured
fps limits how often ffmpeg emits JPEG frames; ffmpeg still receives the
continuous H.264 stream so inter-frame decoding remains correct.
Protocol Versioning
protocolVersion defaults to "4.0":
const service = new ScrcpyStreamService({ protocolVersion: "4.0" });To add a future scrcpy version:
- Add a new adapter under
src/protocol/. - Register it in
src/protocol/registry.ts. - Add the matching
scrcpy-serverjar toassets/. - Add fixed-byte protocol tests for changed behavior.
The backend depends on the protocol interface, so frame parsing, control serialization, server options, and socket naming can evolve per version.
Development Commands
npm run format
npm run lint
npm run typecheck
npm test
npm run buildEnd-to-end streaming requires a connected Android device. Unit tests cover protocol serialization/parsing, backend option normalization, subscriber queue behavior, and WebSocket packet encoding.
