@9b9387/android-stream-scrcpy 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -85
- package/dist/backend/server/options.d.ts +3 -1
- package/dist/backend/server/options.js +1 -0
- package/dist/backend/server/server-command.js +2 -1
- package/dist/protocol/v4_0/server-options.d.ts +1 -0
- package/dist/protocol/v4_0/server-options.js +26 -17
- package/package.json +3 -39
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -4,8 +4,6 @@ TypeScript scrcpy client library for Node.js and Electron. It starts `scrcpy-ser
|
|
|
4
4
|
|
|
5
5
|
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.
|
|
6
6
|
|
|
7
|
-
> Runtime note: the root package is Node-only. Use it from a Node.js process, an Electron main process, a Next.js Node runtime route/server, or another long-lived backend process. Do not import the root package from browser code, Next.js Client Components, or Edge Runtime handlers.
|
|
8
|
-
|
|
9
7
|
## Project Structure
|
|
10
8
|
|
|
11
9
|
```text
|
|
@@ -47,10 +45,6 @@ node-lib/
|
|
|
47
45
|
scrcpy-stream-service.ts
|
|
48
46
|
types.ts
|
|
49
47
|
index.ts
|
|
50
|
-
snapshot/
|
|
51
|
-
ffmpeg-snapshot-cache.ts
|
|
52
|
-
types.ts
|
|
53
|
-
index.ts
|
|
54
48
|
websocket/
|
|
55
49
|
binary-packet.ts
|
|
56
50
|
control-json.ts
|
|
@@ -65,19 +59,18 @@ node-lib/
|
|
|
65
59
|
- `src/protocol/`: scrcpy wire protocol implementation. `core/` contains shared binary helpers and protocol interfaces; `registry.ts` selects a protocol adapter by version; `v4_0/` contains scrcpy 4.0 frame, control, device, codec, and server-option logic.
|
|
66
60
|
- `src/backend/`: adbkit-only device integration. `adb/` wraps adbkit operations, `io/` contains socket reading utilities, `server/` builds and normalizes scrcpy server launch details, and `scrcpy-backend.ts` orchestrates the streaming connection.
|
|
67
61
|
- `src/service/`: public stream service. It manages state, caches decoder config/session snapshots, and exposes `for await...of` media packet subscriptions.
|
|
68
|
-
- `src/snapshot/`: optional Node-side screenshot cache. It pipes H.264 packets into ffmpeg and stores the latest JPEG frame.
|
|
69
62
|
- `src/websocket/`: optional `ws` bridge for browser clients. It is exported from the `./websocket` subpath and is not part of the root API.
|
|
70
63
|
- `examples/`: runnable examples kept outside the library build.
|
|
71
64
|
|
|
72
|
-
## Install
|
|
73
|
-
|
|
74
|
-
Install the package in your application:
|
|
65
|
+
## Install And Build
|
|
75
66
|
|
|
76
67
|
```bash
|
|
77
|
-
|
|
68
|
+
cd node-lib
|
|
69
|
+
npm install
|
|
70
|
+
npm run build
|
|
78
71
|
```
|
|
79
72
|
|
|
80
|
-
Core runtime dependency is `@devicefarmer/adbkit`. The library does not call the `adb` system command directly.
|
|
73
|
+
Core runtime dependency is only `@devicefarmer/adbkit`. The library does not call the `adb` system command directly.
|
|
81
74
|
|
|
82
75
|
If your application uses the optional WebSocket bridge, install `ws` in the application:
|
|
83
76
|
|
|
@@ -85,25 +78,6 @@ If your application uses the optional WebSocket bridge, install `ws` in the appl
|
|
|
85
78
|
npm install ws
|
|
86
79
|
```
|
|
87
80
|
|
|
88
|
-
If your application uses the optional screenshot cache, make sure `ffmpeg` is available on `PATH`, or pass `ffmpegPath` to `FfmpegSnapshotCache`.
|
|
89
|
-
|
|
90
|
-
## Build From Source
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
cd node-lib
|
|
94
|
-
npm install
|
|
95
|
-
npm run build
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Package Entrypoints
|
|
99
|
-
|
|
100
|
-
- `@9b9387/android-stream-scrcpy`: Node-only service/backend API.
|
|
101
|
-
- `@9b9387/android-stream-scrcpy/protocol`: protocol adapters and types. This is the safest entrypoint for code that only needs scrcpy protocol constants, codecs, and message types.
|
|
102
|
-
- `@9b9387/android-stream-scrcpy/websocket`: optional Node-only `ws` bridge.
|
|
103
|
-
- `@9b9387/android-stream-scrcpy/snapshot`: optional Node-only ffmpeg screenshot cache.
|
|
104
|
-
|
|
105
|
-
The package is ESM-only and requires Node.js 20 or newer.
|
|
106
|
-
|
|
107
81
|
## Run Examples
|
|
108
82
|
|
|
109
83
|
Run the console stream demo:
|
|
@@ -267,34 +241,6 @@ The first implementation supports H.264 input and JPEG output. The configured
|
|
|
267
241
|
`fps` limits how often ffmpeg emits JPEG frames; ffmpeg still receives the
|
|
268
242
|
continuous H.264 stream so inter-frame decoding remains correct.
|
|
269
243
|
|
|
270
|
-
## Next.js And Electron Usage
|
|
271
|
-
|
|
272
|
-
### Next.js
|
|
273
|
-
|
|
274
|
-
Use this package only from the server side of a self-hosted or long-lived Node.js runtime:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
export const runtime = "nodejs";
|
|
278
|
-
|
|
279
|
-
import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
Do not import the root package from Client Components, browser bundles, Middleware,
|
|
283
|
-
or Edge Runtime route handlers. The backend opens ADB sockets and long-lived media
|
|
284
|
-
streams, so a custom Node.js server or separate local daemon is usually a better
|
|
285
|
-
fit than a short-lived serverless function.
|
|
286
|
-
|
|
287
|
-
### Electron
|
|
288
|
-
|
|
289
|
-
Create and manage `ScrcpyStreamService` in the main process. Renderer processes
|
|
290
|
-
should communicate with the main process through IPC or connect to the optional
|
|
291
|
-
WebSocket bridge.
|
|
292
|
-
|
|
293
|
-
When packaging Electron apps, make sure `assets/scrcpy-server-v4.0.jar` is copied
|
|
294
|
-
as a runtime resource. If your packager moves or packs assets into an archive,
|
|
295
|
-
pass an explicit `serverJarPath` to `ScrcpyStreamService`. If screenshot caching
|
|
296
|
-
is enabled, also ship `ffmpeg` yourself or pass `ffmpegPath`.
|
|
297
|
-
|
|
298
244
|
## Protocol Versioning
|
|
299
245
|
|
|
300
246
|
`protocolVersion` defaults to `"4.0"`:
|
|
@@ -320,32 +266,6 @@ npm run lint
|
|
|
320
266
|
npm run typecheck
|
|
321
267
|
npm test
|
|
322
268
|
npm run build
|
|
323
|
-
npm pack --dry-run
|
|
324
269
|
```
|
|
325
270
|
|
|
326
271
|
End-to-end streaming requires a connected Android device. Unit tests cover protocol serialization/parsing, backend option normalization, subscriber queue behavior, and WebSocket packet encoding.
|
|
327
|
-
|
|
328
|
-
## Publish
|
|
329
|
-
|
|
330
|
-
Before publishing, make sure you are logged in to the npm account that owns the
|
|
331
|
-
`@9b9387` scope:
|
|
332
|
-
|
|
333
|
-
```bash
|
|
334
|
-
npm whoami
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
Run the release checks and inspect the tarball contents:
|
|
338
|
-
|
|
339
|
-
```bash
|
|
340
|
-
npm run typecheck
|
|
341
|
-
npm run test
|
|
342
|
-
npm run lint
|
|
343
|
-
npm run build
|
|
344
|
-
npm pack --dry-run
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
Publish the public scoped package:
|
|
348
|
-
|
|
349
|
-
```bash
|
|
350
|
-
npm publish --access public
|
|
351
|
-
```
|
|
@@ -13,14 +13,16 @@ export interface ScrcpyBackendOptions {
|
|
|
13
13
|
control?: boolean;
|
|
14
14
|
videoCodec?: VideoCodec;
|
|
15
15
|
audioCodec?: AudioCodec;
|
|
16
|
+
videoEncoder?: string;
|
|
16
17
|
connectionTimeoutMs?: number;
|
|
17
18
|
deployTimeoutMs?: number;
|
|
18
19
|
pushTimeoutMs?: number;
|
|
19
20
|
serverJarPath?: string;
|
|
20
21
|
}
|
|
21
|
-
export interface NormalizedScrcpyBackendOptions extends Required<Omit<ScrcpyBackendOptions, "serverJarPath">> {
|
|
22
|
+
export interface NormalizedScrcpyBackendOptions extends Required<Omit<ScrcpyBackendOptions, "serverJarPath" | "videoEncoder">> {
|
|
22
23
|
protocol: ScrcpyProtocol;
|
|
23
24
|
socketName: string;
|
|
24
25
|
serverJarPath: string;
|
|
26
|
+
videoEncoder?: string;
|
|
25
27
|
}
|
|
26
28
|
export declare function normalizeBackendOptions(options?: ScrcpyBackendOptions): NormalizedScrcpyBackendOptions;
|
|
@@ -22,6 +22,7 @@ export function normalizeBackendOptions(options = {}) {
|
|
|
22
22
|
control: options.control !== false,
|
|
23
23
|
videoCodec: options.videoCodec ?? VideoCodec.H264,
|
|
24
24
|
audioCodec: options.audioCodec ?? AudioCodec.OPUS,
|
|
25
|
+
videoEncoder: options.videoEncoder,
|
|
25
26
|
connectionTimeoutMs: options.connectionTimeoutMs ?? 8000,
|
|
26
27
|
deployTimeoutMs: options.deployTimeoutMs ?? 5000,
|
|
27
28
|
pushTimeoutMs: options.pushTimeoutMs ?? 60000,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function getDeviceServerPath(serverVersion) {
|
|
2
|
-
return
|
|
2
|
+
return "/data/local/tmp/scrcpy-server.jar";
|
|
3
3
|
}
|
|
4
4
|
export function buildServerCommand(deviceServerPath, options) {
|
|
5
5
|
const args = [
|
|
@@ -14,6 +14,7 @@ export function buildServerCommand(deviceServerPath, options) {
|
|
|
14
14
|
control: options.control,
|
|
15
15
|
videoCodec: options.videoCodec,
|
|
16
16
|
audioCodec: options.audioCodec,
|
|
17
|
+
videoEncoder: options.videoEncoder,
|
|
17
18
|
maxSize: options.maxSize,
|
|
18
19
|
maxFps: options.maxFps,
|
|
19
20
|
videoBitRate: options.videoBitRate,
|
|
@@ -11,28 +11,37 @@ export function getSocketName(scid = DEFAULT_SCRCPY_SCID) {
|
|
|
11
11
|
}
|
|
12
12
|
export function buildServerOptions(input) {
|
|
13
13
|
const scid = input.scid ?? DEFAULT_SCRCPY_SCID;
|
|
14
|
-
|
|
14
|
+
const opts = [
|
|
15
15
|
SCRCPY_4_0_SERVER_VERSION,
|
|
16
|
+
`scid=${(scid >>> 0).toString(16).padStart(8, "0")}`,
|
|
16
17
|
`log_level=${input.logLevel ?? "info"}`,
|
|
17
18
|
"tunnel_forward=true",
|
|
18
|
-
`scid=${scid.toString(16)}`,
|
|
19
19
|
`video=${input.video}`,
|
|
20
20
|
`audio=${input.audio}`,
|
|
21
21
|
`control=${input.control}`,
|
|
22
|
-
`video_codec=${input.videoCodec}`,
|
|
23
|
-
`audio_codec=${input.audioCodec}`,
|
|
24
|
-
`max_size=${input.maxSize}`,
|
|
25
|
-
`max_fps=${input.maxFps}`,
|
|
26
|
-
`video_bit_rate=${input.videoBitRate}`,
|
|
27
|
-
`audio_bit_rate=${input.audioBitRate}`,
|
|
28
|
-
"send_device_meta=true",
|
|
29
|
-
"send_frame_meta=true",
|
|
30
|
-
"send_dummy_byte=true",
|
|
31
|
-
"send_stream_meta=true",
|
|
32
|
-
"cleanup=true",
|
|
33
|
-
"stay_awake=false",
|
|
34
|
-
"show_touches=false",
|
|
35
|
-
"power_off_on_close=false",
|
|
36
|
-
"clipboard_autosync=false",
|
|
37
22
|
];
|
|
23
|
+
if (input.videoCodec) {
|
|
24
|
+
opts.push(`video_codec=${input.videoCodec}`);
|
|
25
|
+
}
|
|
26
|
+
if (input.audioCodec) {
|
|
27
|
+
opts.push(`audio_codec=${input.audioCodec}`);
|
|
28
|
+
}
|
|
29
|
+
if (input.videoEncoder) {
|
|
30
|
+
opts.push(`video_encoder=${input.videoEncoder}`);
|
|
31
|
+
}
|
|
32
|
+
if (input.maxSize > 0) {
|
|
33
|
+
opts.push(`max_size=${input.maxSize}`);
|
|
34
|
+
}
|
|
35
|
+
if (input.maxFps > 0) {
|
|
36
|
+
opts.push(`max_fps=${input.maxFps}`);
|
|
37
|
+
}
|
|
38
|
+
if (input.videoBitRate > 0 && input.videoBitRate !== 8000000) {
|
|
39
|
+
opts.push(`video_bit_rate=${input.videoBitRate}`);
|
|
40
|
+
}
|
|
41
|
+
if (input.audioBitRate > 0 && input.audioBitRate !== 128000) {
|
|
42
|
+
opts.push(`audio_bit_rate=${input.audioBitRate}`);
|
|
43
|
+
}
|
|
44
|
+
// Other options like cleanup, send_device_meta, etc. use server-side defaults
|
|
45
|
+
// to keep the command line short and avoid Samsung-specific crashes.
|
|
46
|
+
return opts;
|
|
38
47
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@9b9387/android-stream-scrcpy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Versioned scrcpy protocol client for Node.js/Electron",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
6
|
"exports": {
|
|
9
7
|
".": {
|
|
10
8
|
"import": "./dist/index.js",
|
|
11
9
|
"types": "./dist/index.d.ts"
|
|
12
10
|
},
|
|
13
|
-
"./protocol": {
|
|
14
|
-
"import": "./dist/protocol/index.js",
|
|
15
|
-
"types": "./dist/protocol/index.d.ts"
|
|
16
|
-
},
|
|
17
11
|
"./websocket": {
|
|
18
12
|
"import": "./dist/websocket/index.js",
|
|
19
13
|
"types": "./dist/websocket/index.d.ts"
|
|
@@ -21,18 +15,12 @@
|
|
|
21
15
|
"./snapshot": {
|
|
22
16
|
"import": "./dist/snapshot/index.js",
|
|
23
17
|
"types": "./dist/snapshot/index.d.ts"
|
|
24
|
-
}
|
|
25
|
-
"./package.json": "./package.json"
|
|
18
|
+
}
|
|
26
19
|
},
|
|
27
20
|
"files": [
|
|
28
21
|
"dist",
|
|
29
|
-
"assets"
|
|
30
|
-
"README.md",
|
|
31
|
-
"LICENSE"
|
|
22
|
+
"assets"
|
|
32
23
|
],
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=20"
|
|
35
|
-
},
|
|
36
24
|
"scripts": {
|
|
37
25
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
38
26
|
"build": "npm run clean && tsc",
|
|
@@ -40,33 +28,9 @@
|
|
|
40
28
|
"test": "vitest run",
|
|
41
29
|
"lint": "prettier --check .",
|
|
42
30
|
"format": "prettier --write .",
|
|
43
|
-
"prepack": "npm run build",
|
|
44
|
-
"prepublishOnly": "npm run typecheck && npm run test && npm run lint && npm run build",
|
|
45
31
|
"example:demo": "npm run build && node --loader ts-node/esm examples/demo.ts",
|
|
46
32
|
"example:server": "npm run build && node --loader ts-node/esm examples/server.ts"
|
|
47
33
|
},
|
|
48
|
-
"keywords": [
|
|
49
|
-
"scrcpy",
|
|
50
|
-
"android",
|
|
51
|
-
"adb",
|
|
52
|
-
"streaming",
|
|
53
|
-
"electron",
|
|
54
|
-
"nextjs",
|
|
55
|
-
"typescript"
|
|
56
|
-
],
|
|
57
|
-
"license": "MIT",
|
|
58
|
-
"repository": {
|
|
59
|
-
"type": "git",
|
|
60
|
-
"url": "git+ssh://git@github.com/9b9387/android-stream.git",
|
|
61
|
-
"directory": "node-lib"
|
|
62
|
-
},
|
|
63
|
-
"bugs": {
|
|
64
|
-
"url": "https://github.com/9b9387/android-stream/issues"
|
|
65
|
-
},
|
|
66
|
-
"homepage": "https://github.com/9b9387/android-stream/tree/main/node-lib#readme",
|
|
67
|
-
"publishConfig": {
|
|
68
|
-
"access": "public"
|
|
69
|
-
},
|
|
70
34
|
"dependencies": {
|
|
71
35
|
"@devicefarmer/adbkit": "^3.2.3"
|
|
72
36
|
},
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 9b9387
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|