@yume-chan/scrcpy 0.0.12 → 0.0.15

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.
Files changed (78) hide show
  1. package/CHANGELOG.json +39 -0
  2. package/CHANGELOG.md +23 -1
  3. package/README.md +1 -0
  4. package/bin/scrcpy-server +0 -0
  5. package/bin/version.d.ts +1 -1
  6. package/bin/version.js +1 -1
  7. package/esm/client.d.ts +14 -2
  8. package/esm/client.d.ts.map +1 -1
  9. package/esm/client.js +277 -145
  10. package/esm/client.js.map +1 -1
  11. package/esm/connection.d.ts +4 -4
  12. package/esm/connection.d.ts.map +1 -1
  13. package/esm/connection.js +74 -71
  14. package/esm/connection.js.map +1 -1
  15. package/esm/decoder/tinyh264/index.js +48 -48
  16. package/esm/decoder/tinyh264/index.js.map +1 -1
  17. package/esm/decoder/tinyh264/wrapper.js +3 -2
  18. package/esm/decoder/tinyh264/wrapper.js.map +1 -1
  19. package/esm/decoder/web-codecs/index.d.ts.map +1 -1
  20. package/esm/decoder/web-codecs/index.js +28 -22
  21. package/esm/decoder/web-codecs/index.js.map +1 -1
  22. package/esm/message.d.ts +12 -8
  23. package/esm/message.d.ts.map +1 -1
  24. package/esm/message.js +13 -8
  25. package/esm/message.js.map +1 -1
  26. package/esm/options/1_16/index.d.ts +8 -7
  27. package/esm/options/1_16/index.d.ts.map +1 -1
  28. package/esm/options/1_16/index.js +88 -82
  29. package/esm/options/1_16/index.js.map +1 -1
  30. package/esm/options/1_16/sps.js +3 -2
  31. package/esm/options/1_16/sps.js.map +1 -1
  32. package/esm/options/1_18.d.ts +3 -2
  33. package/esm/options/1_18.d.ts.map +1 -1
  34. package/esm/options/1_18.js +24 -1
  35. package/esm/options/1_18.js.map +1 -1
  36. package/esm/options/1_21.js +4 -1
  37. package/esm/options/1_21.js.map +1 -1
  38. package/esm/options/1_22.d.ts +3 -3
  39. package/esm/options/1_22.d.ts.map +1 -1
  40. package/esm/options/1_22.js +9 -6
  41. package/esm/options/1_22.js.map +1 -1
  42. package/esm/options/1_23.d.ts +12 -0
  43. package/esm/options/1_23.d.ts.map +1 -0
  44. package/esm/options/1_23.js +34 -0
  45. package/esm/options/1_23.js.map +1 -0
  46. package/esm/options/1_24.d.ts +9 -0
  47. package/esm/options/1_24.d.ts.map +1 -0
  48. package/esm/options/1_24.js +13 -0
  49. package/esm/options/1_24.js.map +1 -0
  50. package/esm/options/common.d.ts +6 -2
  51. package/esm/options/common.d.ts.map +1 -1
  52. package/esm/options/common.js.map +1 -1
  53. package/esm/options/index.d.ts +2 -0
  54. package/esm/options/index.d.ts.map +1 -1
  55. package/esm/options/index.js +2 -0
  56. package/esm/options/index.js.map +1 -1
  57. package/esm/push-server.d.ts +2 -2
  58. package/esm/push-server.d.ts.map +1 -1
  59. package/esm/push-server.js +6 -13
  60. package/esm/push-server.js.map +1 -1
  61. package/esm/utils.d.ts +0 -2
  62. package/esm/utils.d.ts.map +1 -1
  63. package/esm/utils.js +0 -8
  64. package/esm/utils.js.map +1 -1
  65. package/package.json +6 -6
  66. package/src/client.ts +185 -25
  67. package/src/connection.ts +40 -15
  68. package/src/decoder/web-codecs/index.ts +2 -1
  69. package/src/message.ts +10 -3
  70. package/src/options/1_16/index.ts +51 -43
  71. package/src/options/1_18.ts +21 -1
  72. package/src/options/1_22.ts +6 -8
  73. package/src/options/1_23.ts +44 -0
  74. package/src/options/1_24.ts +18 -0
  75. package/src/options/common.ts +7 -2
  76. package/src/options/index.ts +2 -0
  77. package/src/push-server.ts +6 -8
  78. 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,13 @@
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 {
8
+ ...super.getDefaultValue(),
9
+ powerOn: true,
10
+ };
11
+ }
12
+ }
13
+ //# 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,OAAO;YACH,GAAG,KAAK,CAAC,eAAe,EAAE;YAC1B,OAAO,EAAE,IAAI;SAChB,CAAC;IACN,CAAC;CACJ"}
@@ -1,6 +1,7 @@
1
- import type { Adb, AdbBufferedStream, ReadableStream } from "@yume-chan/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
+ import type { ScrcpyControlMessageType } from "../message.js";
4
5
  import type { ScrcpyBackOrScreenOnEvent1_18 } from "./1_18.js";
5
6
  import type { ScrcpyInjectScrollControlMessage1_22 } from "./1_22.js";
6
7
  export declare const DEFAULT_SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
@@ -15,6 +16,8 @@ export interface VideoStreamConfigurationPacket {
15
16
  }
16
17
  export interface VideoStreamFramePacket {
17
18
  type: 'frame';
19
+ keyframe?: boolean | undefined;
20
+ pts?: bigint | undefined;
18
21
  data: Uint8Array;
19
22
  }
20
23
  export declare type VideoStreamPacket = VideoStreamConfigurationPacket | VideoStreamFramePacket;
@@ -23,7 +26,8 @@ export interface ScrcpyOptions<T> {
23
26
  formatServerArguments(): string[];
24
27
  getOutputEncoderNameRegex(): RegExp;
25
28
  createConnection(adb: Adb): ScrcpyClientConnection;
26
- parseVideoStream(stream: AdbBufferedStream): ReadableStream<VideoStreamPacket>;
29
+ createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket>;
30
+ getControlMessageTypes(): ScrcpyControlMessageType[];
27
31
  serializeBackOrScreenOnControlMessage(message: ScrcpyBackOrScreenOnEvent1_18): Uint8Array | undefined;
28
32
  serializeInjectScrollControlMessage(message: ScrcpyInjectScrollControlMessage1_22): Uint8Array;
29
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/options/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC7E,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,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,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAAC,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"}
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,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAC9D,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,sBAAsB,IAAI,wBAAwB,EAAE,CAAC;IAErD,qCAAqC,CACjC,OAAO,EAAE,6BAA6B,GACvC,UAAU,GAAG,SAAS,CAAC;IAE1B,mCAAmC,CAC/B,OAAO,EAAE,oCAAoC,GAC9C,UAAU,CAAC;CACjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/options/common.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAMvE,MAAM,UAAU,mBAAmB,CAAC,KAAU;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ;QAC5B,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,CAAC,aAAa,KAAK,UAAU,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAI,KAAU,EAAE,KAAQ;IACvD,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE;QAC5B,KAAK,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;KACjC;IAED,IAAI,KAAK,KAAK,SAAS,EAAE;QACrB,OAAO,KAAK,CAAC;KAChB;IAED,OAAO,GAAG,KAAK,EAAE,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/options/common.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAMvE,MAAM,UAAU,mBAAmB,CAAC,KAAU;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ;QAC5B,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,CAAC,aAAa,KAAK,UAAU,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAI,KAAU,EAAE,KAAQ;IACvD,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE;QAC5B,KAAK,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;KACjC;IAED,IAAI,KAAK,KAAK,SAAS,EAAE;QACrB,OAAO,KAAK,CAAC;KAChB;IAED,OAAO,GAAG,KAAK,EAAE,CAAC;AACtB,CAAC"}
@@ -2,5 +2,7 @@ export * from './1_16/index.js';
2
2
  export * from './1_18.js';
3
3
  export * from './1_21.js';
4
4
  export * from './1_22.js';
5
+ export * from './1_23.js';
6
+ export * from './1_24.js';
5
7
  export * from './common.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -2,5 +2,7 @@ export * from './1_16/index.js';
2
2
  export * from './1_18.js';
3
3
  export * from './1_21.js';
4
4
  export * from './1_22.js';
5
+ export * from './1_23.js';
6
+ export * from './1_24.js';
5
7
  export * from './common.js';
6
8
  //# sourceMappingURL=index.js.map
@@ -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"}
@@ -1,6 +1,6 @@
1
- import { Adb, AdbSync, WrapWritableStream, WritableStream } from "@yume-chan/adb";
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, WritableStream<Uint8Array>, AdbSync>;
5
+ export declare function pushServer(device: Adb, options?: PushServerOptions): WrapWritableStream<Uint8Array>;
6
6
  //# sourceMappingURL=push-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"push-server.d.ts","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGlF,MAAM,WAAW,iBAAiB;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CACtB,MAAM,EAAE,GAAG,EACX,OAAO,GAAE,iBAAsB,uEAgBlC"}
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"}
@@ -1,22 +1,15 @@
1
- import { __awaiter } from "tslib";
2
1
  import { WrapWritableStream } from "@yume-chan/adb";
3
2
  import { DEFAULT_SERVER_PATH } from "./options/index.js";
4
3
  export function pushServer(device, options = {}) {
5
4
  const { path = DEFAULT_SERVER_PATH } = options;
5
+ let sync;
6
6
  return new WrapWritableStream({
7
- start() {
8
- return __awaiter(this, void 0, void 0, function* () {
9
- const sync = yield device.sync();
10
- return {
11
- writable: sync.write(path),
12
- state: sync,
13
- };
14
- });
7
+ async start() {
8
+ sync = await device.sync();
9
+ return sync.write(path);
15
10
  },
16
- close(sync) {
17
- return __awaiter(this, void 0, void 0, function* () {
18
- yield sync.dispose();
19
- });
11
+ async close() {
12
+ await sync.dispose();
20
13
  },
21
14
  });
22
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"push-server.js","sourceRoot":"","sources":["../src/push-server.ts"],"names":[],"mappings":";AAAA,OAAO,EAAgB,kBAAkB,EAAkB,MAAM,gBAAgB,CAAC;AAClF,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,OAAO,IAAI,kBAAkB,CAAkD;QACrE,KAAK;;gBACP,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjC,OAAO;oBACH,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;oBAC1B,KAAK,EAAE,IAAI;iBACd,CAAC;YACN,CAAC;SAAA;QACK,KAAK,CAAC,IAAI;;gBACZ,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,CAAC;SAAA;KACJ,CAAC,CAAC;AACP,CAAC"}
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;QACtC,KAAK,CAAC,KAAK;YACP,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,KAAK;YACP,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;KACJ,CAAC,CAAC;AACP,CAAC"}
package/esm/utils.d.ts CHANGED
@@ -1,4 +1,2 @@
1
1
  export declare function delay(time: number): Promise<void>;
2
- export declare function encodeUtf8(input: string): ArrayBuffer;
3
- export declare function decodeUtf8(buffer: ArrayBuffer): string;
4
2
  //# sourceMappingURL=utils.d.ts.map
@@ -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;AAKD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAErD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEtD"}
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;AAED,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAEtC,MAAM,UAAU,UAAU,CAAC,KAAa;IACpC,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC1C,OAAO,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACtC,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.12",
3
+ "version": "0.0.15",
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.12",
32
+ "@yume-chan/adb": "^0.0.15",
33
33
  "@yume-chan/async": "^2.1.4",
34
- "@yume-chan/event": "^0.0.12",
35
- "@yume-chan/struct": "^0.0.12",
34
+ "@yume-chan/event": "^0.0.15",
35
+ "@yume-chan/struct": "^0.0.15",
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": "next",
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,7 +1,7 @@
1
- import { AdbBufferedStream, AdbNoneSubprocessProtocol, DecodeUtf8Stream, InspectStream, TransformStream, WritableStream, type Adb, type AdbSocket, type AdbSubprocessProtocol, type ReadableStream, type WritableStreamDefaultWriter } from '@yume-chan/adb';
1
+ import { AbortController, AdbBufferedStream, AdbSubprocessNoneProtocol, DecodeUtf8Stream, InspectStream, ReadableStream, TransformStream, WritableStream, type Adb, type AdbSocket, type AdbSubprocessProtocol, type WritableStreamDefaultWriter } from '@yume-chan/adb';
2
2
  import { EventEmitter } from '@yume-chan/event';
3
3
  import Struct from '@yume-chan/struct';
4
- import { AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, type AndroidKeyEventAction } from './message.js';
4
+ import { AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpySimpleControlMessage, type AndroidKeyEventAction } from './message.js';
5
5
  import type { ScrcpyInjectScrollControlMessage1_22, ScrcpyOptions, VideoStreamPacket } from "./options/index.js";
6
6
 
7
7
  function* splitLines(text: string): Generator<string, void, void> {
@@ -20,12 +20,87 @@ function* splitLines(text: string): Generator<string, void, void> {
20
20
  }
21
21
  }
22
22
 
23
+ class SplitLinesStream extends TransformStream<string, string>{
24
+ constructor() {
25
+ super({
26
+ transform(chunk, controller) {
27
+ for (const line of splitLines(chunk)) {
28
+ if (line === '') {
29
+ continue;
30
+ }
31
+ controller.enqueue(line);
32
+ }
33
+ },
34
+ });
35
+ }
36
+ }
37
+
38
+ class ArrayToStream<T> extends ReadableStream<T>{
39
+ private array!: T[];
40
+ private index = 0;
41
+
42
+ constructor(array: T[]) {
43
+ super({
44
+ start: async () => {
45
+ await Promise.resolve();
46
+ this.array = array;
47
+ },
48
+ pull: (controller) => {
49
+ if (this.index < this.array.length) {
50
+ controller.enqueue(this.array[this.index]!);
51
+ this.index += 1;
52
+ } else {
53
+ controller.close();
54
+ }
55
+ },
56
+ });
57
+ }
58
+ }
59
+
60
+ class ConcatStream<T> extends ReadableStream<T>{
61
+ private streams!: ReadableStream<T>[];
62
+ private index = 0;
63
+ private reader!: ReadableStreamDefaultReader<T>;
64
+
65
+ constructor(...streams: ReadableStream<T>[]) {
66
+ super({
67
+ start: async (controller) => {
68
+ await Promise.resolve();
69
+
70
+ this.streams = streams;
71
+ this.advance(controller);
72
+ },
73
+ pull: async (controller) => {
74
+ const result = await this.reader.read();
75
+ if (!result.done) {
76
+ controller.enqueue(result.value);
77
+ return;
78
+ }
79
+ this.advance(controller);
80
+ }
81
+ });
82
+ }
83
+
84
+ private advance(controller: ReadableStreamDefaultController<T>) {
85
+ if (this.index < this.streams.length) {
86
+ this.reader = this.streams[this.index]!.getReader();
87
+ this.index += 1;
88
+ } else {
89
+ controller.close();
90
+ }
91
+ }
92
+ }
93
+
23
94
  const ClipboardMessage =
24
95
  new Struct()
25
96
  .uint32('length')
26
97
  .string('content', { lengthField: 'length' });
27
98
 
28
99
  export class ScrcpyClient {
100
+ /**
101
+ * This method will modify the given `options`,
102
+ * so don't reuse it elsewhere.
103
+ */
29
104
  public static async getEncoders(
30
105
  adb: Adb,
31
106
  path: string,
@@ -37,6 +112,8 @@ export class ScrcpyClient {
37
112
  options.value.encoderName = '_';
38
113
  // Disable control for faster connection in 1.22+
39
114
  options.value.control = false;
115
+ options.value.sendDeviceMeta = false;
116
+ options.value.sendDummyByte = false;
40
117
 
41
118
  // Scrcpy server will open connections, before initializing encoder
42
119
  // Thus although an invalid encoder name is given, the start process will success
@@ -56,6 +133,45 @@ export class ScrcpyClient {
56
133
  return encoders;
57
134
  }
58
135
 
136
+ /**
137
+ * This method will modify the given `options`,
138
+ * so don't reuse it elsewhere.
139
+ */
140
+ public static async getDisplays(
141
+ adb: Adb,
142
+ path: string,
143
+ version: string,
144
+ options: ScrcpyOptions<any>
145
+ ): Promise<number[]> {
146
+ // Similar to `getEncoders`, pass an invalid option and parse the output
147
+ options.value.displayId = -1;
148
+
149
+ options.value.control = false;
150
+ options.value.sendDeviceMeta = false;
151
+ options.value.sendDummyByte = false;
152
+
153
+ try {
154
+ // Server will exit before opening connections when an invalid display id was given.
155
+ await ScrcpyClient.start(adb, path, version, options);
156
+ } catch (e) {
157
+ if (e instanceof Error) {
158
+ const output = (e as any).output as string[];
159
+
160
+ const displayIdRegex = /\s+scrcpy --display (\d+)/;
161
+ const displays: number[] = [];
162
+ for (const line of output) {
163
+ const match = line.match(displayIdRegex);
164
+ if (match) {
165
+ displays.push(Number.parseInt(match[1]!, 10));
166
+ }
167
+ }
168
+ return displays;
169
+ }
170
+ }
171
+
172
+ throw new Error('failed to get displays');
173
+ }
174
+
59
175
  public static async start(
60
176
  adb: Adb,
61
177
  path: string,
@@ -81,21 +197,54 @@ export class ScrcpyClient {
81
197
  {
82
198
  // Scrcpy server doesn't split stdout and stderr,
83
199
  // so disable Shell Protocol to simplify processing
84
- protocols: [AdbNoneSubprocessProtocol],
200
+ protocols: [AdbSubprocessNoneProtocol],
85
201
  }
86
202
  );
87
203
 
204
+ const stdout = process.stdout
205
+ .pipeThrough(new DecodeUtf8Stream())
206
+ .pipeThrough(new SplitLinesStream());
207
+
208
+ // Read stdout, otherwise `process.exit` won't resolve.
209
+ const output: string[] = [];
210
+ const abortController = new AbortController();
211
+ const pipe = stdout
212
+ .pipeTo(new WritableStream({
213
+ write(chunk) {
214
+ output.push(chunk);
215
+ }
216
+ }), {
217
+ signal: abortController.signal,
218
+ preventCancel: true,
219
+ })
220
+ .catch(() => { });
221
+
88
222
  const result = await Promise.race([
89
223
  process.exit,
90
224
  connection.getStreams(),
91
225
  ]);
92
226
 
93
227
  if (typeof result === 'number') {
94
- throw new Error('scrcpy server exited prematurely');
228
+ const error = new Error('scrcpy server exited prematurely');
229
+ (error as any).output = output;
230
+ throw error;
95
231
  }
96
232
 
233
+ abortController.abort();
234
+ await pipe;
235
+
97
236
  const [videoStream, controlStream] = result;
98
- return new ScrcpyClient(adb, options, process, videoStream, controlStream);
237
+ return new ScrcpyClient(
238
+ adb,
239
+ options,
240
+ process,
241
+ new ConcatStream(
242
+ new ArrayToStream(output),
243
+ stdout,
244
+ ),
245
+ videoStream,
246
+ controlStream
247
+ );
99
248
  } catch (e) {
100
249
  await process?.kill();
101
250
  throw e;
@@ -135,28 +284,18 @@ export class ScrcpyClient {
135
284
  adb: Adb,
136
285
  options: ScrcpyOptions<any>,
137
286
  process: AdbSubprocessProtocol,
138
- videoStream: AdbBufferedStream,
287
+ stdout: ReadableStream<string>,
288
+ videoStream: AdbSocket,
139
289
  controlStream: AdbSocket | undefined,
140
290
  ) {
141
291
  this._adb = adb;
142
292
  this.options = options;
143
293
  this.process = process;
144
294
 
145
- this._stdout = process.stdout
146
- .pipeThrough(new DecodeUtf8Stream())
147
- .pipeThrough(new TransformStream({
148
- transform(chunk, controller) {
149
- for (const line of splitLines(chunk)) {
150
- if (line === '') {
151
- continue;
152
- }
153
- controller.enqueue(line);
154
- }
155
- },
156
- }));
295
+ this._stdout = stdout;
157
296
 
158
- this._videoStream = options
159
- .parseVideoStream(videoStream)
297
+ this._videoStream = videoStream.readable
298
+ .pipeThrough(options.createVideoStreamTransformer())
160
299
  .pipeThrough(new InspectStream(packet => {
161
300
  if (packet.type === 'configuration') {
162
301
  this._screenWidth = packet.data.croppedWidth;
@@ -195,12 +334,21 @@ export class ScrcpyClient {
195
334
  return this._controlStreamWriter;
196
335
  }
197
336
 
337
+ private getControlMessageTypeValue(type: ScrcpyControlMessageType) {
338
+ const list = this.options.getControlMessageTypes();
339
+ const index = list.indexOf(type);
340
+ if (index === -1) {
341
+ throw new Error('Not supported');
342
+ }
343
+ return index;
344
+ }
345
+
198
346
  public async injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, 'type'>) {
199
347
  const controlStream = this.checkControlStream('injectKeyCode');
200
348
 
201
349
  await controlStream.write(ScrcpyInjectKeyCodeControlMessage.serialize({
202
350
  ...message,
203
- type: ScrcpyControlMessageType.InjectKeycode,
351
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectKeycode),
204
352
  }));
205
353
  }
206
354
 
@@ -208,7 +356,7 @@ export class ScrcpyClient {
208
356
  const controlStream = this.checkControlStream('injectText');
209
357
 
210
358
  await controlStream.write(ScrcpyInjectTextControlMessage.serialize({
211
- type: ScrcpyControlMessageType.InjectText,
359
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectText),
212
360
  text,
213
361
  }));
214
362
  }
@@ -234,7 +382,7 @@ export class ScrcpyClient {
234
382
  this.lastTouchMessage = now;
235
383
  await controlStream.write(ScrcpyInjectTouchControlMessage.serialize({
236
384
  ...message,
237
- type: ScrcpyControlMessageType.InjectTouch,
385
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectTouch),
238
386
  screenWidth: this.screenWidth,
239
387
  screenHeight: this.screenHeight,
240
388
  }));
@@ -249,7 +397,7 @@ export class ScrcpyClient {
249
397
 
250
398
  const buffer = this.options!.serializeInjectScrollControlMessage({
251
399
  ...message,
252
- type: ScrcpyControlMessageType.InjectScroll,
400
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectScroll),
253
401
  screenWidth: this.screenWidth,
254
402
  screenHeight: this.screenHeight,
255
403
  });
@@ -260,7 +408,7 @@ export class ScrcpyClient {
260
408
  const controlStream = this.checkControlStream('pressBackOrTurnOnScreen');
261
409
 
262
410
  const buffer = this.options!.serializeBackOrScreenOnControlMessage({
263
- type: ScrcpyControlMessageType.BackOrScreenOn,
411
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.BackOrScreenOn),
264
412
  action,
265
413
  });
266
414
  if (buffer) {
@@ -268,6 +416,18 @@ export class ScrcpyClient {
268
416
  }
269
417
  }
270
418
 
419
+ private async sendSimpleControlMessage(type: ScrcpyControlMessageType, name: string) {
420
+ const controlStream = this.checkControlStream(name);
421
+ const buffer = ScrcpySimpleControlMessage.serialize({
422
+ type: this.getControlMessageTypeValue(type),
423
+ });
424
+ await controlStream.write(buffer);
425
+ }
426
+
427
+ public async rotateDevice() {
428
+ await this.sendSimpleControlMessage(ScrcpyControlMessageType.RotateDevice, 'rotateDevice');
429
+ }
430
+
271
431
  public async close() {
272
432
  // No need to close streams. Kill the process will destroy them from the other side.
273
433
  await this.process?.kill();