@yume-chan/scrcpy 0.0.11 → 0.0.14
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/CHANGELOG.json +30 -0
- package/CHANGELOG.md +20 -1
- package/README.md +1 -0
- package/bin/scrcpy-server +0 -0
- package/bin/version.d.ts +1 -1
- package/bin/version.js +1 -1
- package/esm/client.d.ts +2 -2
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +4 -4
- package/esm/client.js.map +1 -1
- package/esm/connection.d.ts +4 -4
- package/esm/connection.d.ts.map +1 -1
- package/esm/connection.js +25 -11
- package/esm/connection.js.map +1 -1
- package/esm/decoder/web-codecs/index.d.ts.map +1 -1
- package/esm/decoder/web-codecs/index.js +2 -1
- package/esm/decoder/web-codecs/index.js.map +1 -1
- package/esm/options/1_16/index.d.ts +3 -3
- package/esm/options/1_16/index.d.ts.map +1 -1
- package/esm/options/1_16/index.js +55 -65
- package/esm/options/1_16/index.js.map +1 -1
- package/esm/options/1_23.d.ts +12 -0
- package/esm/options/1_23.d.ts.map +1 -0
- package/esm/options/1_23.js +31 -0
- package/esm/options/1_23.js.map +1 -0
- package/esm/options/1_24.d.ts +9 -0
- package/esm/options/1_24.d.ts.map +1 -0
- package/esm/options/1_24.js +10 -0
- package/esm/options/1_24.js.map +1 -0
- package/esm/options/common.d.ts +4 -2
- package/esm/options/common.d.ts.map +1 -1
- package/esm/options/index.d.ts +2 -0
- package/esm/options/index.d.ts.map +1 -1
- package/esm/options/index.js +2 -0
- package/esm/options/index.js.map +1 -1
- package/esm/push-server.d.ts +2 -2
- package/esm/push-server.d.ts.map +1 -1
- package/esm/push-server.js +4 -6
- package/esm/push-server.js.map +1 -1
- package/esm/utils.d.ts +0 -2
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +0 -8
- package/esm/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/client.ts +5 -5
- package/src/connection.ts +40 -15
- package/src/decoder/web-codecs/index.ts +2 -1
- package/src/options/1_16/index.ts +26 -33
- package/src/options/1_23.ts +44 -0
- package/src/options/1_24.ts +18 -0
- package/src/options/common.ts +4 -2
- package/src/options/index.ts +2 -0
- package/src/push-server.ts +6 -8
- package/src/utils.ts +0 -11
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ScrcpyOptions1_23, type ScrcpyOptionsInit1_23 } from './1_23.js';
|
|
2
|
+
export interface ScrcpyOptionsInit1_24 extends ScrcpyOptionsInit1_23 {
|
|
3
|
+
powerOn: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare class ScrcpyOptions1_24<T extends ScrcpyOptionsInit1_24 = ScrcpyOptionsInit1_24> extends ScrcpyOptions1_23<T> {
|
|
6
|
+
constructor(init: Partial<ScrcpyOptionsInit1_24>);
|
|
7
|
+
protected getDefaultValue(): T;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=1_24.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1_24.d.ts","sourceRoot":"","sources":["../../src/options/1_24.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAChE,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,iBAAiB,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,CAAE,SAAQ,iBAAiB,CAAC,CAAC,CAAC;gBAC7F,IAAI,EAAE,OAAO,CAAC,qBAAqB,CAAC;cAIpC,eAAe,IAAI,CAAC;CAM1C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ScrcpyOptions1_23 } from './1_23.js';
|
|
2
|
+
export class ScrcpyOptions1_24 extends ScrcpyOptions1_23 {
|
|
3
|
+
constructor(init) {
|
|
4
|
+
super(init);
|
|
5
|
+
}
|
|
6
|
+
getDefaultValue() {
|
|
7
|
+
return Object.assign(Object.assign({}, super.getDefaultValue()), { powerOn: true });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=1_24.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1_24.js","sourceRoot":"","sources":["../../src/options/1_24.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA8B,MAAM,WAAW,CAAC;AAM1E,MAAM,OAAO,iBAA2E,SAAQ,iBAAoB;IAChH,YAAmB,IAAoC;QACnD,KAAK,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAEkB,eAAe;QAC9B,uCACO,KAAK,CAAC,eAAe,EAAE,KAC1B,OAAO,EAAE,IAAI,IACf;IACN,CAAC;CACJ"}
|
package/esm/options/common.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Adb,
|
|
1
|
+
import type { Adb, TransformStream } from "@yume-chan/adb";
|
|
2
2
|
import type { ScrcpyClientConnection } from "../connection.js";
|
|
3
3
|
import type { H264Configuration } from "../decoder/index.js";
|
|
4
4
|
import type { ScrcpyBackOrScreenOnEvent1_18 } from "./1_18.js";
|
|
@@ -15,6 +15,8 @@ export interface VideoStreamConfigurationPacket {
|
|
|
15
15
|
}
|
|
16
16
|
export interface VideoStreamFramePacket {
|
|
17
17
|
type: 'frame';
|
|
18
|
+
keyframe?: boolean | undefined;
|
|
19
|
+
pts?: bigint | undefined;
|
|
18
20
|
data: Uint8Array;
|
|
19
21
|
}
|
|
20
22
|
export declare type VideoStreamPacket = VideoStreamConfigurationPacket | VideoStreamFramePacket;
|
|
@@ -23,7 +25,7 @@ export interface ScrcpyOptions<T> {
|
|
|
23
25
|
formatServerArguments(): string[];
|
|
24
26
|
getOutputEncoderNameRegex(): RegExp;
|
|
25
27
|
createConnection(adb: Adb): ScrcpyClientConnection;
|
|
26
|
-
|
|
28
|
+
createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket>;
|
|
27
29
|
serializeBackOrScreenOnControlMessage(message: ScrcpyBackOrScreenOnEvent1_18): Uint8Array | undefined;
|
|
28
30
|
serializeInjectScrollControlMessage(message: ScrcpyInjectScrollControlMessage1_22): Uint8Array;
|
|
29
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/options/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/options/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,WAAW,CAAC;AAEtE,eAAO,MAAM,mBAAmB,sCAAsC,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAC9B,aAAa,IAAI,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,iBAAiB,CAI1E;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAUvE;AAED,MAAM,WAAW,8BAA8B;IAC3C,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;CACpB;AAED,oBAAY,iBAAiB,GAAG,8BAA8B,GAAG,sBAAsB,CAAC;AAExF,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAElB,qBAAqB,IAAI,MAAM,EAAE,CAAC;IAElC,yBAAyB,IAAI,MAAM,CAAC;IAEpC,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,sBAAsB,CAAC;IAEnD,4BAA4B,IAAI,eAAe,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;IAE/E,qCAAqC,CACjC,OAAO,EAAE,6BAA6B,GACvC,UAAU,GAAG,SAAS,CAAC;IAE1B,mCAAmC,CAC/B,OAAO,EAAE,oCAAoC,GAC9C,UAAU,CAAC;CACjB"}
|
package/esm/options/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/options/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/options/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
package/esm/options/index.js
CHANGED
package/esm/options/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/options/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/options/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
package/esm/push-server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WrapWritableStream, type Adb } from "@yume-chan/adb";
|
|
2
2
|
export interface PushServerOptions {
|
|
3
3
|
path?: string;
|
|
4
4
|
}
|
|
5
|
-
export declare function pushServer(device: Adb, options?: PushServerOptions): WrapWritableStream<Uint8Array
|
|
5
|
+
export declare function pushServer(device: Adb, options?: PushServerOptions): WrapWritableStream<Uint8Array>;
|
|
6
6
|
//# sourceMappingURL=push-server.d.ts.map
|
package/esm/push-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push-server.d.ts","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"push-server.d.ts","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,GAAG,EAAgB,MAAM,gBAAgB,CAAC;AAG5E,MAAM,WAAW,iBAAiB;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CACtB,MAAM,EAAE,GAAG,EACX,OAAO,GAAE,iBAAsB,kCAclC"}
|
package/esm/push-server.js
CHANGED
|
@@ -3,17 +3,15 @@ import { WrapWritableStream } from "@yume-chan/adb";
|
|
|
3
3
|
import { DEFAULT_SERVER_PATH } from "./options/index.js";
|
|
4
4
|
export function pushServer(device, options = {}) {
|
|
5
5
|
const { path = DEFAULT_SERVER_PATH } = options;
|
|
6
|
+
let sync;
|
|
6
7
|
return new WrapWritableStream({
|
|
7
8
|
start() {
|
|
8
9
|
return __awaiter(this, void 0, void 0, function* () {
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
writable: sync.write(path),
|
|
12
|
-
state: sync,
|
|
13
|
-
};
|
|
10
|
+
sync = yield device.sync();
|
|
11
|
+
return sync.write(path);
|
|
14
12
|
});
|
|
15
13
|
},
|
|
16
|
-
close(
|
|
14
|
+
close() {
|
|
17
15
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
16
|
yield sync.dispose();
|
|
19
17
|
});
|
package/esm/push-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push-server.js","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"push-server.js","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,kBAAkB,EAA0B,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAMzD,MAAM,UAAU,UAAU,CACtB,MAAW,EACX,UAA6B,EAAE;IAE/B,MAAM,EAAE,IAAI,GAAG,mBAAmB,EAAE,GAAG,OAAO,CAAC;IAE/C,IAAI,IAAc,CAAC;IACnB,OAAO,IAAI,kBAAkB,CAAa;QAChC,KAAK;;gBACP,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;SAAA;QACK,KAAK;;gBACP,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,CAAC;SAAA;KACJ,CAAC,CAAC;AACP,CAAC"}
|
package/esm/utils.d.ts
CHANGED
package/esm/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjD
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjD"}
|
package/esm/utils.js
CHANGED
|
@@ -3,12 +3,4 @@ export function delay(time) {
|
|
|
3
3
|
globalThis.setTimeout(resolve, time);
|
|
4
4
|
});
|
|
5
5
|
}
|
|
6
|
-
const Utf8Encoder = new TextEncoder();
|
|
7
|
-
const Utf8Decoder = new TextDecoder();
|
|
8
|
-
export function encodeUtf8(input) {
|
|
9
|
-
return Utf8Encoder.encode(input).buffer;
|
|
10
|
-
}
|
|
11
|
-
export function decodeUtf8(buffer) {
|
|
12
|
-
return Utf8Decoder.decode(buffer);
|
|
13
|
-
}
|
|
14
6
|
//# sourceMappingURL=utils.js.map
|
package/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,IAAY;IAC9B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACxB,UAAkB,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACP,CAAC
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,IAAY;IAC9B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACxB,UAAkB,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yume-chan/scrcpy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "TypeScript implementation of Scrcpy.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adb",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"main": "esm/index.js",
|
|
30
30
|
"types": "esm/index.d.ts",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@yume-chan/adb": "^0.0.
|
|
32
|
+
"@yume-chan/adb": "^0.0.14",
|
|
33
33
|
"@yume-chan/async": "^2.1.4",
|
|
34
|
-
"@yume-chan/event": "^0.0.
|
|
35
|
-
"@yume-chan/struct": "^0.0.
|
|
34
|
+
"@yume-chan/event": "^0.0.14",
|
|
35
|
+
"@yume-chan/struct": "^0.0.14",
|
|
36
36
|
"tslib": "^2.3.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"gh-release-fetch": "^2.0.4",
|
|
43
43
|
"jest": "^27.5.1",
|
|
44
44
|
"tinyh264": "^0.0.7",
|
|
45
|
-
"typescript": "
|
|
45
|
+
"typescript": "4.7.0-beta",
|
|
46
46
|
"yuv-buffer": "^1.0.0",
|
|
47
47
|
"yuv-canvas": "^1.2.7"
|
|
48
48
|
},
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"build:watch": "build-ts-package --incremental",
|
|
58
58
|
"test": "jest --coverage"
|
|
59
59
|
},
|
|
60
|
-
"readme": "# @yume-chan/scrcpy\r\n\r\nTypeScript implementation of [Scrcpy](https://github.com/Genymobile/scrcpy) client.\r\n\r\nIt uses the official Scrcpy server releases.\r\n\r\n**WARNING:** The public API is UNSTABLE. If you have any questions, please open an issue.\r\n\r\n## Download Server Binary\r\n\r\nThis package has a script `fetch-scrcpy-server` to help you download the official server binary.\r\n\r\nThe server binary is subject to [Apache License 2.0](https://github.com/Genymobile/scrcpy/blob/master/LICENSE).\r\n\r\nUsage:\r\n\r\n```\r\n$ npx fetch-scrcpy-server <version>\r\n```\r\n\r\nFor example:\r\n\r\n```\r\n$ npx fetch-scrcpy-server 1.21\r\n```\r\n\r\nYou can also add it to the `postinstall` script of your `package.json` so it will run automatically when you do `npm install`:\r\n\r\n```json\r\n\"scripts\": {\r\n \"postinstall\": \"fetch-scrcpy-server 1.21\",\r\n},\r\n```\r\n\r\nIt will download the binary to `bin/scrcpy` and write the version string to `bin/version.js`. You can import the version string with\r\n\r\n```js\r\nimport SCRCPY_SERVER_VERSION from '@yume-chan/scrcpy/bin/version';\r\n```\r\n\r\nAnd import the server binary with [file-loader](https://v4.webpack.js.org/loaders/file-loader/) (Webpack 4) or [Asset Modules](https://webpack.js.org/guides/asset-modules/) (Webpack 5).\r\n\r\n## Option versions\r\n\r\nScrcpy server has no backward compatibility on options input format. Currently the following versions are supported:\r\n\r\n| versions | type |\r\n| --------- | ------------------- |\r\n| 1.16~1.17 | `ScrcpyOptions1_16` |\r\n| 1.18~1.20 | `ScrcpyOptions1_18` |\r\n| 1.21 | `ScrcpyOptions1_21` |\r\n| 1.22 | `ScrcpyOptions1_22` |\r\n\r\nYou must use the correct type according to the server version.\r\n\r\n## Video stream\r\n\r\nThe data from `onVideoData` event is a raw H.264 stream. You can process it as you want, or use the following built-in decoders to render it in browsers:\r\n\r\n* WebCodecs decoder: Uses the [WebCodecs API](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API). The video stream will be decoded into `VideoFrame`s and drawn on a 2D canvas.\r\n* TinyH264 decoder: TinyH264 compiles the old Android H.264 software decoder (now deprecated and removed) into WebAssembly, and wrap it in Web Worker to prevent blocking the main thread. The video stream will be decoded into YUV frames, then converted to RGB using a WebGL shader.\r\n\r\n| Name | Chrome | Firefox | Safari | Performance | Supported H.264 profile/level |\r\n| ----------------- | ------ | ------- | ------ | ------------------------------- | ----------------------------- |\r\n| WebCodecs decoder | 94 | No | No | High with Hardware acceleration | High level 5 |\r\n| TinyH264 decoder | 57 | 52 | 11 | Poor | Baseline level 4 |\r\n\r\nTinyH264 decoder needs some extra setup:\r\n\r\n1. `tinyh264`, `yuv-buffer` and `yuv-canvas` packages are peer dependencies. You must install them separately.\r\n2. The bundler you use must support the `new Worker(new URL('./worker.js', import.meta.url))` syntax. It's known to work with Webpack 5.\r\n\r\nExample usage:\r\n\r\n```ts\r\nconst client = new ScrcpyClient(adb);\r\nconst decoder = new WebCodecsDecoder(); // Or `new TinyH264Decoder()`\r\nclient.onSizeChanged(size => decoder.setSize(size));\r\nclient.onVideoData(data => decoder.feed(data));\r\nclient.start(serverPath, serverVersion, new ScrcpyOptionsX_XX({\r\n ...options,\r\n codecOptions: new CodecOptions({\r\n profile: decoder.maxProfile,\r\n level: decoder.maxLevel,\r\n }),\r\n}));\r\ndocument.body.appendChild(decoder.element);\r\n```\r\n"
|
|
60
|
+
"readme": "# @yume-chan/scrcpy\r\n\r\nTypeScript implementation of [Scrcpy](https://github.com/Genymobile/scrcpy) client.\r\n\r\nIt uses the official Scrcpy server releases.\r\n\r\n**WARNING:** The public API is UNSTABLE. If you have any questions, please open an issue.\r\n\r\n## Download Server Binary\r\n\r\nThis package has a script `fetch-scrcpy-server` to help you download the official server binary.\r\n\r\nThe server binary is subject to [Apache License 2.0](https://github.com/Genymobile/scrcpy/blob/master/LICENSE).\r\n\r\nUsage:\r\n\r\n```\r\n$ npx fetch-scrcpy-server <version>\r\n```\r\n\r\nFor example:\r\n\r\n```\r\n$ npx fetch-scrcpy-server 1.21\r\n```\r\n\r\nYou can also add it to the `postinstall` script of your `package.json` so it will run automatically when you do `npm install`:\r\n\r\n```json\r\n\"scripts\": {\r\n \"postinstall\": \"fetch-scrcpy-server 1.21\",\r\n},\r\n```\r\n\r\nIt will download the binary to `bin/scrcpy` and write the version string to `bin/version.js`. You can import the version string with\r\n\r\n```js\r\nimport SCRCPY_SERVER_VERSION from '@yume-chan/scrcpy/bin/version';\r\n```\r\n\r\nAnd import the server binary with [file-loader](https://v4.webpack.js.org/loaders/file-loader/) (Webpack 4) or [Asset Modules](https://webpack.js.org/guides/asset-modules/) (Webpack 5).\r\n\r\n## Option versions\r\n\r\nScrcpy server has no backward compatibility on options input format. Currently the following versions are supported:\r\n\r\n| versions | type |\r\n| --------- | ------------------- |\r\n| 1.16~1.17 | `ScrcpyOptions1_16` |\r\n| 1.18~1.20 | `ScrcpyOptions1_18` |\r\n| 1.21 | `ScrcpyOptions1_21` |\r\n| 1.22 | `ScrcpyOptions1_22` |\r\n| 1.23 | `ScrcpyOptions1_23` |\r\n\r\nYou must use the correct type according to the server version.\r\n\r\n## Video stream\r\n\r\nThe data from `onVideoData` event is a raw H.264 stream. You can process it as you want, or use the following built-in decoders to render it in browsers:\r\n\r\n* WebCodecs decoder: Uses the [WebCodecs API](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API). The video stream will be decoded into `VideoFrame`s and drawn on a 2D canvas.\r\n* TinyH264 decoder: TinyH264 compiles the old Android H.264 software decoder (now deprecated and removed) into WebAssembly, and wrap it in Web Worker to prevent blocking the main thread. The video stream will be decoded into YUV frames, then converted to RGB using a WebGL shader.\r\n\r\n| Name | Chrome | Firefox | Safari | Performance | Supported H.264 profile/level |\r\n| ----------------- | ------ | ------- | ------ | ------------------------------- | ----------------------------- |\r\n| WebCodecs decoder | 94 | No | No | High with Hardware acceleration | High level 5 |\r\n| TinyH264 decoder | 57 | 52 | 11 | Poor | Baseline level 4 |\r\n\r\nTinyH264 decoder needs some extra setup:\r\n\r\n1. `tinyh264`, `yuv-buffer` and `yuv-canvas` packages are peer dependencies. You must install them separately.\r\n2. The bundler you use must support the `new Worker(new URL('./worker.js', import.meta.url))` syntax. It's known to work with Webpack 5.\r\n\r\nExample usage:\r\n\r\n```ts\r\nconst client = new ScrcpyClient(adb);\r\nconst decoder = new WebCodecsDecoder(); // Or `new TinyH264Decoder()`\r\nclient.onSizeChanged(size => decoder.setSize(size));\r\nclient.onVideoData(data => decoder.feed(data));\r\nclient.start(serverPath, serverVersion, new ScrcpyOptionsX_XX({\r\n ...options,\r\n codecOptions: new CodecOptions({\r\n profile: decoder.maxProfile,\r\n level: decoder.maxLevel,\r\n }),\r\n}));\r\ndocument.body.appendChild(decoder.element);\r\n```\r\n"
|
|
61
61
|
}
|
package/src/client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdbBufferedStream,
|
|
1
|
+
import { AdbBufferedStream, AdbSubprocessNoneProtocol, DecodeUtf8Stream, InspectStream, TransformStream, WritableStream, type Adb, type AdbSocket, type AdbSubprocessProtocol, type ReadableStream, type WritableStreamDefaultWriter } from '@yume-chan/adb';
|
|
2
2
|
import { EventEmitter } from '@yume-chan/event';
|
|
3
3
|
import Struct from '@yume-chan/struct';
|
|
4
4
|
import { AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, type AndroidKeyEventAction } from './message.js';
|
|
@@ -81,7 +81,7 @@ export class ScrcpyClient {
|
|
|
81
81
|
{
|
|
82
82
|
// Scrcpy server doesn't split stdout and stderr,
|
|
83
83
|
// so disable Shell Protocol to simplify processing
|
|
84
|
-
protocols: [
|
|
84
|
+
protocols: [AdbSubprocessNoneProtocol],
|
|
85
85
|
}
|
|
86
86
|
);
|
|
87
87
|
|
|
@@ -135,7 +135,7 @@ export class ScrcpyClient {
|
|
|
135
135
|
adb: Adb,
|
|
136
136
|
options: ScrcpyOptions<any>,
|
|
137
137
|
process: AdbSubprocessProtocol,
|
|
138
|
-
videoStream:
|
|
138
|
+
videoStream: AdbSocket,
|
|
139
139
|
controlStream: AdbSocket | undefined,
|
|
140
140
|
) {
|
|
141
141
|
this._adb = adb;
|
|
@@ -155,8 +155,8 @@ export class ScrcpyClient {
|
|
|
155
155
|
},
|
|
156
156
|
}));
|
|
157
157
|
|
|
158
|
-
this._videoStream =
|
|
159
|
-
.
|
|
158
|
+
this._videoStream = videoStream.readable
|
|
159
|
+
.pipeThrough(options.createVideoStreamTransformer())
|
|
160
160
|
.pipeThrough(new InspectStream(packet => {
|
|
161
161
|
if (packet.type === 'configuration') {
|
|
162
162
|
this._screenWidth = packet.data.croppedWidth;
|
package/src/connection.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Adb,
|
|
1
|
+
import type { Adb, AdbSocket } from "@yume-chan/adb";
|
|
2
2
|
import type { Disposable } from "@yume-chan/event";
|
|
3
3
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
4
4
|
import { delay } from "./utils.js";
|
|
@@ -29,7 +29,7 @@ export abstract class ScrcpyClientConnection implements Disposable {
|
|
|
29
29
|
|
|
30
30
|
public initialize(): ValueOrPromise<void> { }
|
|
31
31
|
|
|
32
|
-
public abstract getStreams(): ValueOrPromise<[videoSteam:
|
|
32
|
+
public abstract getStreams(): ValueOrPromise<[videoSteam: AdbSocket, controlStream: AdbSocket | undefined]>;
|
|
33
33
|
|
|
34
34
|
public dispose(): void { }
|
|
35
35
|
}
|
|
@@ -50,26 +50,39 @@ export class ScrcpyClientForwardConnection extends ScrcpyClientConnection {
|
|
|
50
50
|
throw new Error(`Can't connect to server after 100 retries`);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
private async connectVideoStream(): Promise<
|
|
54
|
-
const
|
|
55
|
-
const stream = new AdbBufferedStream(socket);
|
|
53
|
+
private async connectVideoStream(): Promise<AdbSocket> {
|
|
54
|
+
const stream = await this.connectAndRetry();
|
|
56
55
|
if (this.options.sendDummyByte) {
|
|
56
|
+
const reader = stream.readable.getReader();
|
|
57
|
+
const { done, value } = await reader.read();
|
|
57
58
|
// server will write a `0` to signal connection success
|
|
58
|
-
|
|
59
|
+
if (done || value.byteLength !== 1 || value[0] !== 0) {
|
|
60
|
+
throw new Error('Unexpected response from server');
|
|
61
|
+
}
|
|
62
|
+
reader.releaseLock();
|
|
59
63
|
}
|
|
60
64
|
return stream;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
public async getStreams(): Promise<[videoSteam:
|
|
67
|
+
public async getStreams(): Promise<[videoSteam: AdbSocket, controlStream: AdbSocket | undefined]> {
|
|
64
68
|
const videoStream = await this.connectVideoStream();
|
|
69
|
+
|
|
65
70
|
let controlStream: AdbSocket | undefined;
|
|
66
71
|
if (this.options.control) {
|
|
67
72
|
controlStream = await this.connectAndRetry();
|
|
68
73
|
}
|
|
74
|
+
|
|
75
|
+
// Server only writes device meta after control socket is connected (if enabled)
|
|
69
76
|
if (this.options.sendDeviceMeta) {
|
|
77
|
+
const reader = videoStream.readable.getReader();
|
|
78
|
+
const { done, value } = await reader.read();
|
|
70
79
|
// 64 bytes device name + 2 bytes video width + 2 bytes video height
|
|
71
|
-
|
|
80
|
+
if (done || value.byteLength !== 64 + 2 + 2) {
|
|
81
|
+
throw new Error('Unexpected response from server');
|
|
82
|
+
}
|
|
83
|
+
reader.releaseLock();
|
|
72
84
|
}
|
|
85
|
+
|
|
73
86
|
return [videoStream, controlStream];
|
|
74
87
|
}
|
|
75
88
|
}
|
|
@@ -90,27 +103,39 @@ export class ScrcpyClientReverseConnection extends ScrcpyClientConnection {
|
|
|
90
103
|
const queue = new TransformStream<AdbSocket>();
|
|
91
104
|
this.streams = queue.readable.getReader();
|
|
92
105
|
const writer = queue.writable.getWriter();
|
|
93
|
-
this.address = await this.device.reverse.add(
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
this.address = await this.device.reverse.add(
|
|
107
|
+
'localabstract:scrcpy',
|
|
108
|
+
27183,
|
|
109
|
+
socket => {
|
|
110
|
+
writer.write(socket);
|
|
111
|
+
return true;
|
|
96
112
|
},
|
|
97
|
-
|
|
113
|
+
);
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
private async accept(): Promise<AdbSocket> {
|
|
101
117
|
return (await this.streams.read()).value!;
|
|
102
118
|
}
|
|
103
119
|
|
|
104
|
-
public async getStreams(): Promise<[videoSteam:
|
|
105
|
-
const videoStream =
|
|
120
|
+
public async getStreams(): Promise<[videoSteam: AdbSocket, controlStream: AdbSocket | undefined]> {
|
|
121
|
+
const videoStream = await this.accept();
|
|
122
|
+
|
|
106
123
|
let controlStream: AdbSocket | undefined;
|
|
107
124
|
if (this.options.control) {
|
|
108
125
|
controlStream = await this.accept();
|
|
109
126
|
}
|
|
127
|
+
|
|
128
|
+
// Server only writes device meta after control socket is connected (if enabled)
|
|
110
129
|
if (this.options.sendDeviceMeta) {
|
|
130
|
+
const reader = videoStream.readable.getReader();
|
|
131
|
+
const { done, value } = await reader.read();
|
|
111
132
|
// 64 bytes device name + 2 bytes video width + 2 bytes video height
|
|
112
|
-
|
|
133
|
+
if (done || value.byteLength !== 64 + 2 + 2) {
|
|
134
|
+
throw new Error('Unexpected response from server');
|
|
135
|
+
}
|
|
136
|
+
reader.releaseLock();
|
|
113
137
|
}
|
|
138
|
+
|
|
114
139
|
return [videoStream, controlStream];
|
|
115
140
|
}
|
|
116
141
|
|
|
@@ -55,7 +55,8 @@ export class WebCodecsDecoder implements H264Decoder {
|
|
|
55
55
|
break;
|
|
56
56
|
case 'frame':
|
|
57
57
|
this.decoder.decode(new EncodedVideoChunk({
|
|
58
|
-
|
|
58
|
+
// Treat `undefined` as `key`, otherwise won't decode.
|
|
59
|
+
type: packet.keyframe === false ? 'delta' : 'key',
|
|
59
60
|
timestamp: 0,
|
|
60
61
|
data: packet.data,
|
|
61
62
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StructDeserializeStream, TransformStream, type Adb } from "@yume-chan/adb";
|
|
2
2
|
import Struct, { placeholder } from "@yume-chan/struct";
|
|
3
3
|
import type { AndroidCodecLevel, AndroidCodecProfile } from "../../codec.js";
|
|
4
4
|
import { ScrcpyClientConnection, ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnectionOptions } from "../../connection.js";
|
|
@@ -112,11 +112,11 @@ export interface ScrcpyOptionsInit1_16 {
|
|
|
112
112
|
|
|
113
113
|
export const VideoPacket =
|
|
114
114
|
new Struct()
|
|
115
|
-
.
|
|
115
|
+
.uint64('pts')
|
|
116
116
|
.uint32('size')
|
|
117
117
|
.uint8Array('data', { lengthField: 'size' });
|
|
118
118
|
|
|
119
|
-
export const
|
|
119
|
+
export const NO_PTS = BigInt(1) << BigInt(63);
|
|
120
120
|
|
|
121
121
|
export const ScrcpyBackOrScreenOnEvent1_16 =
|
|
122
122
|
new Struct()
|
|
@@ -211,29 +211,28 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
|
|
|
211
211
|
return /\s+scrcpy --encoder-name '(.*?)'/;
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
public
|
|
214
|
+
public createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket> {
|
|
215
215
|
// Optimized path for video frames only
|
|
216
216
|
if (this.value.sendFrameMeta === false) {
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
},
|
|
226
|
-
}));
|
|
217
|
+
return new TransformStream({
|
|
218
|
+
transform(chunk, controller) {
|
|
219
|
+
controller.enqueue({
|
|
220
|
+
type: 'frame',
|
|
221
|
+
data: chunk,
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
});
|
|
227
225
|
}
|
|
228
226
|
|
|
229
227
|
let header: Uint8Array | undefined;
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
229
|
+
let deserializeStream = new StructDeserializeStream(VideoPacket);
|
|
230
|
+
return {
|
|
231
|
+
writable: deserializeStream.writable,
|
|
232
|
+
readable: deserializeStream.readable.pipeThrough(new TransformStream({
|
|
233
|
+
transform(packet, controller) {
|
|
234
|
+
if (packet.pts === NO_PTS) {
|
|
235
|
+
const sequenceParameterSet = parse_sequence_parameter_set(packet.data.slice().buffer);
|
|
237
236
|
|
|
238
237
|
const {
|
|
239
238
|
profile_idc: profileIndex,
|
|
@@ -258,7 +257,7 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
|
|
|
258
257
|
const croppedWidth = encodedWidth - cropLeft - cropRight;
|
|
259
258
|
const croppedHeight = encodedHeight - cropTop - cropBottom;
|
|
260
259
|
|
|
261
|
-
header = data;
|
|
260
|
+
header = packet.data;
|
|
262
261
|
controller.enqueue({
|
|
263
262
|
type: 'configuration',
|
|
264
263
|
data: {
|
|
@@ -280,28 +279,22 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
|
|
|
280
279
|
|
|
281
280
|
let frameData: Uint8Array;
|
|
282
281
|
if (header) {
|
|
283
|
-
frameData = new Uint8Array(header.byteLength + data.byteLength);
|
|
282
|
+
frameData = new Uint8Array(header.byteLength + packet.data.byteLength);
|
|
284
283
|
frameData.set(header);
|
|
285
|
-
frameData.set(data
|
|
284
|
+
frameData.set(packet.data, header.byteLength);
|
|
286
285
|
header = undefined;
|
|
287
286
|
} else {
|
|
288
|
-
frameData = data;
|
|
287
|
+
frameData = packet.data;
|
|
289
288
|
}
|
|
290
289
|
|
|
291
290
|
controller.enqueue({
|
|
292
291
|
type: 'frame',
|
|
292
|
+
pts: packet.pts,
|
|
293
293
|
data: frameData,
|
|
294
294
|
});
|
|
295
|
-
} catch (e) {
|
|
296
|
-
if (e instanceof BufferedStreamEndedError) {
|
|
297
|
-
controller.close();
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
throw e;
|
|
302
295
|
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
296
|
+
}))
|
|
297
|
+
};
|
|
305
298
|
}
|
|
306
299
|
|
|
307
300
|
public serializeBackOrScreenOnControlMessage(
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TransformStream } from "@yume-chan/adb";
|
|
2
|
+
import { ScrcpyOptions1_22, type ScrcpyOptionsInit1_22 } from './1_22.js';
|
|
3
|
+
import type { VideoStreamPacket } from "./common.js";
|
|
4
|
+
|
|
5
|
+
export interface ScrcpyOptionsInit1_23 extends ScrcpyOptionsInit1_22 {
|
|
6
|
+
cleanup: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const KEYFRAME_PTS = BigInt(1) << BigInt(62);
|
|
10
|
+
|
|
11
|
+
export class ScrcpyOptions1_23<T extends ScrcpyOptionsInit1_23 = ScrcpyOptionsInit1_23> extends ScrcpyOptions1_22<T> {
|
|
12
|
+
public constructor(init: Partial<ScrcpyOptionsInit1_23>) {
|
|
13
|
+
super(init);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected override getDefaultValue(): T {
|
|
17
|
+
return {
|
|
18
|
+
...super.getDefaultValue(),
|
|
19
|
+
cleanup: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public override createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket> {
|
|
24
|
+
const superStream = super.createVideoStreamTransformer();
|
|
25
|
+
return {
|
|
26
|
+
writable: superStream.writable,
|
|
27
|
+
readable: superStream.readable.pipeThrough(new TransformStream({
|
|
28
|
+
transform(packet, controller): void {
|
|
29
|
+
if (packet.type !== 'frame') {
|
|
30
|
+
controller.enqueue(packet);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (packet.pts !== undefined && packet.pts & KEYFRAME_PTS) {
|
|
35
|
+
packet.keyframe = true;
|
|
36
|
+
packet.pts &= ~KEYFRAME_PTS;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
controller.enqueue(packet);
|
|
40
|
+
}
|
|
41
|
+
}))
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ScrcpyOptions1_23, type ScrcpyOptionsInit1_23 } from './1_23.js';
|
|
2
|
+
|
|
3
|
+
export interface ScrcpyOptionsInit1_24 extends ScrcpyOptionsInit1_23 {
|
|
4
|
+
powerOn: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class ScrcpyOptions1_24<T extends ScrcpyOptionsInit1_24 = ScrcpyOptionsInit1_24> extends ScrcpyOptions1_23<T> {
|
|
8
|
+
public constructor(init: Partial<ScrcpyOptionsInit1_24>) {
|
|
9
|
+
super(init);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected override getDefaultValue(): T {
|
|
13
|
+
return {
|
|
14
|
+
...super.getDefaultValue(),
|
|
15
|
+
powerOn: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/options/common.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Adb,
|
|
1
|
+
import type { Adb, TransformStream } from "@yume-chan/adb";
|
|
2
2
|
import type { ScrcpyClientConnection } from "../connection.js";
|
|
3
3
|
import type { H264Configuration } from "../decoder/index.js";
|
|
4
4
|
import type { ScrcpyBackOrScreenOnEvent1_18 } from "./1_18.js";
|
|
@@ -35,6 +35,8 @@ export interface VideoStreamConfigurationPacket {
|
|
|
35
35
|
|
|
36
36
|
export interface VideoStreamFramePacket {
|
|
37
37
|
type: 'frame';
|
|
38
|
+
keyframe?: boolean | undefined;
|
|
39
|
+
pts?: bigint | undefined;
|
|
38
40
|
data: Uint8Array;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -49,7 +51,7 @@ export interface ScrcpyOptions<T> {
|
|
|
49
51
|
|
|
50
52
|
createConnection(adb: Adb): ScrcpyClientConnection;
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket>;
|
|
53
55
|
|
|
54
56
|
serializeBackOrScreenOnControlMessage(
|
|
55
57
|
message: ScrcpyBackOrScreenOnEvent1_18,
|
package/src/options/index.ts
CHANGED
package/src/push-server.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WrapWritableStream, type Adb, type AdbSync } from "@yume-chan/adb";
|
|
2
2
|
import { DEFAULT_SERVER_PATH } from "./options/index.js";
|
|
3
3
|
|
|
4
4
|
export interface PushServerOptions {
|
|
@@ -11,15 +11,13 @@ export function pushServer(
|
|
|
11
11
|
) {
|
|
12
12
|
const { path = DEFAULT_SERVER_PATH } = options;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
let sync!: AdbSync;
|
|
15
|
+
return new WrapWritableStream<Uint8Array>({
|
|
15
16
|
async start() {
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
writable: sync.write(path),
|
|
19
|
-
state: sync,
|
|
20
|
-
};
|
|
17
|
+
sync = await device.sync();
|
|
18
|
+
return sync.write(path);
|
|
21
19
|
},
|
|
22
|
-
async close(
|
|
20
|
+
async close() {
|
|
23
21
|
await sync.dispose();
|
|
24
22
|
},
|
|
25
23
|
});
|
package/src/utils.ts
CHANGED
|
@@ -3,14 +3,3 @@ export function delay(time: number): Promise<void> {
|
|
|
3
3
|
(globalThis as any).setTimeout(resolve, time);
|
|
4
4
|
});
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
const Utf8Encoder = new TextEncoder();
|
|
8
|
-
const Utf8Decoder = new TextDecoder();
|
|
9
|
-
|
|
10
|
-
export function encodeUtf8(input: string): ArrayBuffer {
|
|
11
|
-
return Utf8Encoder.encode(input).buffer;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function decodeUtf8(buffer: ArrayBuffer): string {
|
|
15
|
-
return Utf8Decoder.decode(buffer);
|
|
16
|
-
}
|