@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 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
- npm install @9b9387/android-stream-scrcpy
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 `/data/local/tmp/scrcpy-server-v${serverVersion}.jar`;
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,
@@ -10,6 +10,7 @@ export interface ScrcpyServerOptionInput {
10
10
  control: boolean;
11
11
  videoCodec: VideoCodec;
12
12
  audioCodec: AudioCodec;
13
+ videoEncoder?: string;
13
14
  maxSize: number;
14
15
  maxFps: number;
15
16
  videoBitRate: number;
@@ -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
- return [
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.0",
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.