@yume-chan/scrcpy 0.0.19 → 0.0.21

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 (278) hide show
  1. package/CHANGELOG.json +30 -0
  2. package/CHANGELOG.md +17 -1
  3. package/README.md +76 -266
  4. package/esm/codec/av1.js +2 -2
  5. package/esm/codec/av1.js.map +1 -1
  6. package/esm/codec/h264.d.ts +1 -21
  7. package/esm/codec/h264.d.ts.map +1 -1
  8. package/esm/codec/h264.js +15 -236
  9. package/esm/codec/h264.js.map +1 -1
  10. package/esm/codec/h265.d.ts +454 -0
  11. package/esm/codec/h265.d.ts.map +1 -1
  12. package/esm/codec/h265.js +1062 -2
  13. package/esm/codec/h265.js.map +1 -1
  14. package/esm/codec/index.d.ts +1 -0
  15. package/esm/codec/index.d.ts.map +1 -1
  16. package/esm/codec/index.js +1 -0
  17. package/esm/codec/index.js.map +1 -1
  18. package/esm/codec/nalu.d.ts +37 -0
  19. package/esm/codec/nalu.d.ts.map +1 -0
  20. package/esm/codec/nalu.js +323 -0
  21. package/esm/codec/nalu.js.map +1 -0
  22. package/esm/control/back-or-screen-on.d.ts +1 -1
  23. package/esm/control/back-or-screen-on.d.ts.map +1 -1
  24. package/esm/control/hover-helper.d.ts +1 -1
  25. package/esm/control/hover-helper.d.ts.map +1 -1
  26. package/esm/control/hover-helper.js +8 -9
  27. package/esm/control/hover-helper.js.map +1 -1
  28. package/esm/control/index.d.ts +2 -0
  29. package/esm/control/index.d.ts.map +1 -1
  30. package/esm/control/index.js +2 -0
  31. package/esm/control/index.js.map +1 -1
  32. package/esm/control/inject-keycode.d.ts +1 -2
  33. package/esm/control/inject-keycode.d.ts.map +1 -1
  34. package/esm/control/inject-keycode.js +4 -5
  35. package/esm/control/inject-keycode.js.map +1 -1
  36. package/esm/control/inject-text.d.ts +1 -2
  37. package/esm/control/inject-text.d.ts.map +1 -1
  38. package/esm/control/inject-text.js +1 -2
  39. package/esm/control/inject-text.js.map +1 -1
  40. package/esm/control/inject-touch.d.ts +15 -20
  41. package/esm/control/inject-touch.d.ts.map +1 -1
  42. package/esm/control/inject-touch.js +16 -43
  43. package/esm/control/inject-touch.js.map +1 -1
  44. package/esm/control/rotate-device.d.ts +2 -4
  45. package/esm/control/rotate-device.d.ts.map +1 -1
  46. package/esm/control/rotate-device.js +2 -3
  47. package/esm/control/rotate-device.js.map +1 -1
  48. package/esm/control/serializer.d.ts +11 -23
  49. package/esm/control/serializer.d.ts.map +1 -1
  50. package/esm/control/serializer.js +23 -52
  51. package/esm/control/serializer.js.map +1 -1
  52. package/esm/control/set-screen-power-mode.d.ts +1 -2
  53. package/esm/control/set-screen-power-mode.d.ts.map +1 -1
  54. package/esm/control/set-screen-power-mode.js +2 -3
  55. package/esm/control/set-screen-power-mode.js.map +1 -1
  56. package/esm/control/type.d.ts +14 -0
  57. package/esm/control/type.d.ts.map +1 -1
  58. package/esm/control/type.js +23 -1
  59. package/esm/control/type.js.map +1 -1
  60. package/esm/control/writer.d.ts +26 -0
  61. package/esm/control/writer.d.ts.map +1 -0
  62. package/esm/control/writer.js +53 -0
  63. package/esm/control/writer.js.map +1 -0
  64. package/esm/device-message/stream.d.ts.map +1 -1
  65. package/esm/device-message/stream.js +23 -8
  66. package/esm/device-message/stream.js.map +1 -1
  67. package/esm/device-message/type.js +1 -1
  68. package/esm/device-message/type.js.map +1 -1
  69. package/esm/index.d.ts +1 -2
  70. package/esm/index.d.ts.map +1 -1
  71. package/esm/index.js +1 -2
  72. package/esm/index.js.map +1 -1
  73. package/esm/options/1_16/codec-options.d.ts +2 -3
  74. package/esm/options/1_16/codec-options.d.ts.map +1 -1
  75. package/esm/options/1_16/codec-options.js.map +1 -1
  76. package/esm/options/1_16/index.d.ts +3 -1
  77. package/esm/options/1_16/index.d.ts.map +1 -1
  78. package/esm/options/1_16/index.js +3 -1
  79. package/esm/options/1_16/index.js.map +1 -1
  80. package/esm/options/1_16/init.js +2 -2
  81. package/esm/options/1_16/init.js.map +1 -1
  82. package/esm/options/1_16/options.d.ts +42 -96
  83. package/esm/options/1_16/options.d.ts.map +1 -1
  84. package/esm/options/1_16/options.js +104 -142
  85. package/esm/options/1_16/options.js.map +1 -1
  86. package/esm/options/1_16/scroll.d.ts +2 -4
  87. package/esm/options/1_16/scroll.d.ts.map +1 -1
  88. package/esm/options/1_16/scroll.js +13 -14
  89. package/esm/options/1_16/scroll.js.map +1 -1
  90. package/esm/options/1_17.d.ts +1 -0
  91. package/esm/options/1_17.d.ts.map +1 -1
  92. package/esm/options/1_17.js +8 -3
  93. package/esm/options/1_17.js.map +1 -1
  94. package/esm/options/1_18.d.ts +49 -11
  95. package/esm/options/1_18.d.ts.map +1 -1
  96. package/esm/options/1_18.js +64 -32
  97. package/esm/options/1_18.js.map +1 -1
  98. package/esm/options/1_21.d.ts +26 -7
  99. package/esm/options/1_21.d.ts.map +1 -1
  100. package/esm/options/1_21.js +34 -20
  101. package/esm/options/1_21.js.map +1 -1
  102. package/esm/options/1_22/index.d.ts +1 -0
  103. package/esm/options/1_22/index.d.ts.map +1 -1
  104. package/esm/options/1_22/index.js +1 -0
  105. package/esm/options/1_22/index.js.map +1 -1
  106. package/esm/options/1_22/options.d.ts +32 -26
  107. package/esm/options/1_22/options.d.ts.map +1 -1
  108. package/esm/options/1_22/options.js +26 -16
  109. package/esm/options/1_22/options.js.map +1 -1
  110. package/esm/options/1_22/scroll.d.ts +1 -1
  111. package/esm/options/1_22/scroll.d.ts.map +1 -1
  112. package/esm/options/1_22/scroll.js +1 -1
  113. package/esm/options/1_22/scroll.js.map +1 -1
  114. package/esm/options/1_23.d.ts +32 -8
  115. package/esm/options/1_23.d.ts.map +1 -1
  116. package/esm/options/1_23.js +23 -14
  117. package/esm/options/1_23.js.map +1 -1
  118. package/esm/options/1_24.d.ts +29 -4
  119. package/esm/options/1_24.d.ts.map +1 -1
  120. package/esm/options/1_24.js +16 -7
  121. package/esm/options/1_24.js.map +1 -1
  122. package/esm/options/1_25/options.d.ts +6 -2
  123. package/esm/options/1_25/options.d.ts.map +1 -1
  124. package/esm/options/1_25/options.js +16 -2
  125. package/esm/options/1_25/options.js.map +1 -1
  126. package/esm/options/1_25/scroll.d.ts.map +1 -1
  127. package/esm/options/1_25/scroll.js +2 -1
  128. package/esm/options/1_25/scroll.js.map +1 -1
  129. package/esm/options/2_0.d.ts +12 -8
  130. package/esm/options/2_0.d.ts.map +1 -1
  131. package/esm/options/2_0.js +82 -15
  132. package/esm/options/2_0.js.map +1 -1
  133. package/esm/options/2_1.d.ts +48 -0
  134. package/esm/options/2_1.d.ts.map +1 -0
  135. package/esm/options/2_1.js +23 -0
  136. package/esm/options/2_1.js.map +1 -0
  137. package/esm/options/codec.d.ts +4 -4
  138. package/esm/options/codec.d.ts.map +1 -1
  139. package/esm/options/codec.js +5 -6
  140. package/esm/options/codec.js.map +1 -1
  141. package/esm/options/index.d.ts +5 -0
  142. package/esm/options/index.d.ts.map +1 -1
  143. package/esm/options/index.js +5 -0
  144. package/esm/options/index.js.map +1 -1
  145. package/esm/options/latest.d.ts +4 -5
  146. package/esm/options/latest.d.ts.map +1 -1
  147. package/esm/options/latest.js +2 -3
  148. package/esm/options/latest.js.map +1 -1
  149. package/esm/options/types.d.ts +74 -33
  150. package/esm/options/types.d.ts.map +1 -1
  151. package/esm/options/types.js +62 -0
  152. package/esm/options/types.js.map +1 -1
  153. package/package.json +9 -19
  154. package/src/codec/av1.ts +33 -0
  155. package/src/codec/h264.ts +317 -0
  156. package/src/codec/h265.ts +1375 -0
  157. package/src/codec/index.ts +4 -0
  158. package/src/codec/nalu.ts +391 -0
  159. package/src/control/back-or-screen-on.ts +1 -1
  160. package/{esm/control/simple.js → src/control/basic.ts} +3 -3
  161. package/src/control/hover-helper.ts +10 -11
  162. package/src/control/index.ts +2 -0
  163. package/src/control/inject-keycode.ts +1 -3
  164. package/src/control/inject-text.ts +1 -3
  165. package/src/control/inject-touch.ts +16 -55
  166. package/src/control/rotate-device.ts +2 -7
  167. package/src/control/serializer.ts +50 -113
  168. package/src/control/set-screen-power-mode.ts +1 -3
  169. package/src/control/type.ts +32 -0
  170. package/src/control/writer.ts +85 -0
  171. package/src/device-message/stream.ts +32 -12
  172. package/src/index.ts +1 -2
  173. package/src/options/1_16/codec-options.ts +6 -7
  174. package/src/options/1_16/float-to-uint16.ts +33 -0
  175. package/src/options/1_16/index.ts +3 -1
  176. package/src/options/1_16/init.ts +80 -0
  177. package/src/options/1_16/message.ts +55 -0
  178. package/src/options/1_16/options.ts +147 -260
  179. package/src/options/1_16/scroll.ts +17 -18
  180. package/src/options/1_17.ts +66 -0
  181. package/src/options/1_18.ts +103 -39
  182. package/src/options/1_21.ts +49 -27
  183. package/src/options/1_22/index.ts +1 -0
  184. package/src/options/1_22/init.ts +19 -0
  185. package/src/options/1_22/options.ts +38 -45
  186. package/src/options/1_22/scroll.ts +3 -3
  187. package/src/options/1_23.ts +34 -22
  188. package/src/options/1_24.ts +23 -11
  189. package/src/options/1_25/options.ts +22 -4
  190. package/src/options/1_25/scroll.ts +4 -3
  191. package/src/options/2_0.ts +342 -0
  192. package/src/options/2_1.ts +35 -0
  193. package/src/options/codec.ts +98 -0
  194. package/src/options/index.ts +5 -0
  195. package/src/options/latest.ts +12 -0
  196. package/src/options/types.ts +164 -43
  197. package/tsconfig.build.json +9 -1
  198. package/tsconfig.build.tsbuildinfo +1 -1
  199. package/bin/scrcpy-server +0 -0
  200. package/bin/version.d.ts +0 -1
  201. package/bin/version.js +0 -1
  202. package/esm/adb/client.d.ts +0 -41
  203. package/esm/adb/client.d.ts.map +0 -1
  204. package/esm/adb/client.js +0 -224
  205. package/esm/adb/client.js.map +0 -1
  206. package/esm/adb/connection.d.ts +0 -49
  207. package/esm/adb/connection.d.ts.map +0 -1
  208. package/esm/adb/connection.js +0 -122
  209. package/esm/adb/connection.js.map +0 -1
  210. package/esm/adb/index.d.ts +0 -4
  211. package/esm/adb/index.d.ts.map +0 -1
  212. package/esm/adb/index.js +0 -4
  213. package/esm/adb/index.js.map +0 -1
  214. package/esm/adb/options/1_16.d.ts +0 -8
  215. package/esm/adb/options/1_16.d.ts.map +0 -1
  216. package/esm/adb/options/1_16.js +0 -20
  217. package/esm/adb/options/1_16.js.map +0 -1
  218. package/esm/adb/options/1_22.d.ts +0 -8
  219. package/esm/adb/options/1_22.d.ts.map +0 -1
  220. package/esm/adb/options/1_22.js +0 -17
  221. package/esm/adb/options/1_22.js.map +0 -1
  222. package/esm/adb/options/1_26.d.ts +0 -9
  223. package/esm/adb/options/1_26.d.ts.map +0 -1
  224. package/esm/adb/options/1_26.js +0 -11
  225. package/esm/adb/options/1_26.js.map +0 -1
  226. package/esm/adb/options/2_0.d.ts +0 -8
  227. package/esm/adb/options/2_0.d.ts.map +0 -1
  228. package/esm/adb/options/2_0.js +0 -13
  229. package/esm/adb/options/2_0.js.map +0 -1
  230. package/esm/adb/options/index.d.ts +0 -4
  231. package/esm/adb/options/index.d.ts.map +0 -1
  232. package/esm/adb/options/index.js +0 -4
  233. package/esm/adb/options/index.js.map +0 -1
  234. package/esm/adb/options/types.d.ts +0 -24
  235. package/esm/adb/options/types.d.ts.map +0 -1
  236. package/esm/adb/options/types.js +0 -37
  237. package/esm/adb/options/types.js.map +0 -1
  238. package/esm/codec.d.ts +0 -34
  239. package/esm/codec.d.ts.map +0 -1
  240. package/esm/codec.js +0 -37
  241. package/esm/codec.js.map +0 -1
  242. package/esm/control/simple.d.ts +0 -5
  243. package/esm/control/simple.d.ts.map +0 -1
  244. package/esm/control/simple.js.map +0 -1
  245. package/esm/decoder.d.ts +0 -2
  246. package/esm/decoder.d.ts.map +0 -1
  247. package/esm/decoder.js +0 -2
  248. package/esm/decoder.js.map +0 -1
  249. package/esm/options/1.17.d.ts +0 -3
  250. package/esm/options/1.17.d.ts.map +0 -1
  251. package/esm/options/1.17.js +0 -2
  252. package/esm/options/1.17.js.map +0 -1
  253. package/esm/options/1_16/h264-configuration.d.ts +0 -49
  254. package/esm/options/1_16/h264-configuration.d.ts.map +0 -1
  255. package/esm/options/1_16/h264-configuration.js +0 -414
  256. package/esm/options/1_16/h264-configuration.js.map +0 -1
  257. package/esm/options/1_16/sps.d.ts +0 -22
  258. package/esm/options/1_16/sps.d.ts.map +0 -1
  259. package/esm/options/1_16/sps.js +0 -263
  260. package/esm/options/1_16/sps.js.map +0 -1
  261. package/esm/options/1_26.d.ts +0 -57
  262. package/esm/options/1_26.d.ts.map +0 -1
  263. package/esm/options/1_26.js +0 -67
  264. package/esm/options/1_26.js.map +0 -1
  265. package/esm/options/2.d.ts +0 -57
  266. package/esm/options/2.d.ts.map +0 -1
  267. package/esm/options/2.js +0 -67
  268. package/esm/options/2.js.map +0 -1
  269. package/scripts/fetch-server.cjs +0 -38
  270. package/src/adb/client.ts +0 -329
  271. package/src/adb/connection.ts +0 -209
  272. package/src/adb/index.ts +0 -3
  273. package/src/adb/options/1_16.ts +0 -32
  274. package/src/adb/options/1_22.ts +0 -26
  275. package/src/adb/options/index.ts +0 -3
  276. package/src/adb/options/types.ts +0 -75
  277. package/src/codec.ts +0 -35
  278. package/src/options/1_16/h264-configuration.ts +0 -485
@@ -1,329 +1,216 @@
1
+ import type { ReadableStream } from "@yume-chan/stream-extra";
1
2
  import {
3
+ BufferedReadableStream,
2
4
  StructDeserializeStream,
3
5
  TransformStream,
4
6
  } from "@yume-chan/stream-extra";
5
- import Struct from "@yume-chan/struct";
7
+ import type { AsyncExactReadable, ValueOrPromise } from "@yume-chan/struct";
8
+ import { NumberFieldType, decodeUtf8 } from "@yume-chan/struct";
6
9
 
7
10
  import type {
8
11
  ScrcpyBackOrScreenOnControlMessage,
9
- ScrcpySetClipboardControlMessage,
10
- } from "../../control/index.js";
11
- import {
12
- AndroidKeyEventAction,
13
12
  ScrcpyControlMessageType,
13
+ ScrcpyInjectTouchControlMessage,
14
+ ScrcpySetClipboardControlMessage,
14
15
  } from "../../control/index.js";
15
- import type { ScrcpyOptions, ScrcpyVideoStreamPacket } from "../types.js";
16
+ import { AndroidKeyEventAction } from "../../control/index.js";
17
+ import type {
18
+ ScrcpyMediaStreamPacket,
19
+ ScrcpyVideoStream,
20
+ ScrcpyVideoStreamMetadata,
21
+ } from "../codec.js";
22
+ import { ScrcpyVideoCodecId } from "../codec.js";
23
+ import type { ScrcpyDisplay, ScrcpyEncoder, ScrcpyOptions } from "../types.js";
16
24
  import { toScrcpyOptionValue } from "../types.js";
17
25
 
18
26
  import { CodecOptions } from "./codec-options.js";
27
+ import type { ScrcpyOptionsInit1_16 } from "./init.js";
28
+ import { ScrcpyLogLevel1_16, ScrcpyVideoOrientation1_16 } from "./init.js";
19
29
  import {
20
- parseH264Configuration,
21
- parseSequenceParameterSet,
22
- removeH264Emulation,
23
- } from "./h264-configuration.js";
30
+ SCRCPY_CONTROL_MESSAGE_TYPES_1_16,
31
+ SCRCPY_MEDIA_PACKET_FLAG_CONFIG,
32
+ ScrcpyBackOrScreenOnControlMessage1_16,
33
+ ScrcpyInjectTouchControlMessage1_16,
34
+ ScrcpyMediaStreamRawPacket,
35
+ ScrcpySetClipboardControlMessage1_15,
36
+ } from "./message.js";
24
37
  import type { ScrcpyScrollController } from "./scroll.js";
25
38
  import { ScrcpyScrollController1_16 } from "./scroll.js";
26
39
 
27
- export enum ScrcpyLogLevel {
28
- Verbose = "verbose",
29
- Debug = "debug",
30
- Info = "info",
31
- Warn = "warn",
32
- Error = "error",
33
- }
34
-
35
- export enum ScrcpyVideoOrientation {
36
- Initial = -2,
37
- Unlocked = -1,
38
- Portrait = 0,
39
- Landscape = 1,
40
- PortraitFlipped = 2,
41
- LandscapeFlipped = 3,
42
- }
43
-
44
- export interface ScrcpyOptionsInit1_16 {
45
- logLevel: ScrcpyLogLevel;
46
-
47
- /**
48
- * The maximum value of both width and height.
49
- */
50
- maxSize: number;
51
-
52
- bitRate: number;
53
-
54
- /**
55
- * 0 for unlimited.
56
- *
57
- * @default 0
58
- */
59
- maxFps: number;
60
-
61
- /**
62
- * The orientation of the video stream.
63
- *
64
- * It will not keep the device screen in specific orientation,
65
- * only the captured video will in this orientation.
66
- */
67
- lockVideoOrientation: ScrcpyVideoOrientation;
68
-
69
- /**
70
- * Use ADB forward tunnel instead of reverse tunnel.
71
- *
72
- * This option is mainly used for working around the bug that on Android <9,
73
- * ADB daemon can't create reverse tunnels if connected wirelessly (ADB over WiFi).
74
- *
75
- * When using `AdbScrcpyClient`, it can detect this situation and enable this option automatically.
76
- */
77
- tunnelForward: boolean;
78
-
79
- crop: string;
80
-
81
- /**
82
- * Send PTS so that the client may record properly
83
- *
84
- * Note: When `sendFrameMeta: false` is specified,
85
- * `onChangeEncoding` event won't fire and `onVideoData` event doesn't
86
- * merge sps/pps frame and first video frame. Which means you can't use
87
- * the shipped decoders to render the video
88
- * (You can still record the stream into a file).
89
- *
90
- * @default true
91
- */
92
- sendFrameMeta: boolean;
93
-
94
- /**
95
- * @default true
96
- */
97
- control: boolean;
98
-
99
- displayId: number;
100
-
101
- showTouches: boolean;
102
-
103
- stayAwake: boolean;
104
-
105
- codecOptions: CodecOptions;
40
+ export class ScrcpyOptions1_16 implements ScrcpyOptions<ScrcpyOptionsInit1_16> {
41
+ static readonly DEFAULTS = {
42
+ logLevel: ScrcpyLogLevel1_16.Debug,
43
+ maxSize: 0,
44
+ bitRate: 8_000_000,
45
+ maxFps: 0,
46
+ lockVideoOrientation: ScrcpyVideoOrientation1_16.Unlocked,
47
+ tunnelForward: false,
48
+ crop: undefined,
49
+ sendFrameMeta: true,
50
+ control: true,
51
+ displayId: 0,
52
+ showTouches: false,
53
+ stayAwake: false,
54
+ codecOptions: new CodecOptions(),
55
+ } as const satisfies Required<ScrcpyOptionsInit1_16>;
56
+
57
+ static readonly SERIALIZE_ORDER = [
58
+ "logLevel",
59
+ "maxSize",
60
+ "bitRate",
61
+ "maxFps",
62
+ "lockVideoOrientation",
63
+ "tunnelForward",
64
+ "crop",
65
+ "sendFrameMeta",
66
+ "control",
67
+ "displayId",
68
+ "showTouches",
69
+ "stayAwake",
70
+ "codecOptions",
71
+ ] as const satisfies readonly (keyof ScrcpyOptionsInit1_16)[];
72
+
73
+ static serialize<T>(options: T, order: readonly (keyof T)[]) {
74
+ return order.map((key) => toScrcpyOptionValue(options[key], "-"));
75
+ }
106
76
 
107
- encoderName: string;
108
- }
77
+ static async parseCString(
78
+ stream: AsyncExactReadable,
79
+ maxLength: number,
80
+ ): Promise<string> {
81
+ let result = decodeUtf8(await stream.readExactly(maxLength));
82
+ result = result.substring(0, result.indexOf("\0"));
83
+ return result;
84
+ }
109
85
 
110
- export const VideoPacket = new Struct()
111
- .uint64("pts")
112
- .uint32("size")
113
- .uint8Array("data", { lengthField: "size" });
86
+ static async parseUint16BE(stream: AsyncExactReadable): Promise<number> {
87
+ const buffer = await stream.readExactly(NumberFieldType.Uint16.size);
88
+ return NumberFieldType.Uint16.deserialize(buffer, false);
89
+ }
114
90
 
115
- export const NO_PTS = BigInt(1) << BigInt(63);
91
+ static async parseUint32BE(stream: AsyncExactReadable): Promise<number> {
92
+ const buffer = await stream.readExactly(NumberFieldType.Uint32.size);
93
+ return NumberFieldType.Uint32.deserialize(buffer, false);
94
+ }
116
95
 
117
- export const ScrcpyBackOrScreenOnControlMessage1_16 = new Struct().uint8(
118
- "type",
119
- ScrcpyControlMessageType.BackOrScreenOn as const
120
- );
96
+ value: Required<ScrcpyOptionsInit1_16>;
121
97
 
122
- export const ScrcpySetClipboardControlMessage1_15 = new Struct()
123
- .uint8("type", ScrcpyControlMessageType.SetClipboard as const)
124
- .uint32("length")
125
- .string("content", { lengthField: "length" });
98
+ readonly defaults: Required<ScrcpyOptionsInit1_16> =
99
+ ScrcpyOptions1_16.DEFAULTS;
126
100
 
127
- export type ScrcpySetClipboardControlMessage1_15 =
128
- (typeof ScrcpySetClipboardControlMessage1_15)["TInit"];
101
+ readonly controlMessageTypes: readonly ScrcpyControlMessageType[] =
102
+ SCRCPY_CONTROL_MESSAGE_TYPES_1_16;
129
103
 
130
- export class ScrcpyOptions1_16<
131
- T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16
132
- > implements ScrcpyOptions<T>
133
- {
134
- public value: Partial<T>;
104
+ constructor(init: ScrcpyOptionsInit1_16) {
105
+ this.value = { ...ScrcpyOptions1_16.DEFAULTS, ...init };
106
+ }
135
107
 
136
- public constructor(value: Partial<ScrcpyOptionsInit1_16>) {
137
- if (
138
- new.target === ScrcpyOptions1_16 &&
139
- value.logLevel === ScrcpyLogLevel.Verbose
140
- ) {
141
- value.logLevel = ScrcpyLogLevel.Debug;
142
- }
108
+ serialize(): string[] {
109
+ return ScrcpyOptions1_16.serialize(
110
+ this.value,
111
+ ScrcpyOptions1_16.SERIALIZE_ORDER,
112
+ );
113
+ }
143
114
 
144
- if (
145
- new.target === ScrcpyOptions1_16 &&
146
- value.lockVideoOrientation === ScrcpyVideoOrientation.Initial
147
- ) {
148
- value.lockVideoOrientation = ScrcpyVideoOrientation.Unlocked;
149
- }
115
+ setListEncoders(): void {
116
+ throw new Error("Not supported");
117
+ }
150
118
 
151
- this.value = value as Partial<T>;
119
+ setListDisplays(): void {
120
+ // Set to an invalid value
121
+ // Server will print valid values before crashing
122
+ // (server will crash before opening sockets)
123
+ this.value.displayId = -1;
152
124
  }
153
125
 
154
- protected getArgumentOrder(): (keyof T)[] {
155
- return [
156
- "logLevel",
157
- "maxSize",
158
- "bitRate",
159
- "maxFps",
160
- "lockVideoOrientation",
161
- "tunnelForward",
162
- "crop",
163
- "sendFrameMeta",
164
- "control",
165
- "displayId",
166
- "showTouches",
167
- "stayAwake",
168
- "codecOptions",
169
- "encoderName",
170
- ];
126
+ parseEncoder(): ScrcpyEncoder | undefined {
127
+ throw new Error("Not supported");
171
128
  }
172
129
 
173
- public getDefaultValue(): T {
174
- return {
175
- logLevel: ScrcpyLogLevel.Debug,
176
- maxSize: 0,
177
- bitRate: 8_000_000,
178
- maxFps: 0,
179
- lockVideoOrientation: ScrcpyVideoOrientation.Unlocked,
180
- tunnelForward: false,
181
- crop: "-",
182
- sendFrameMeta: true,
183
- control: true,
184
- displayId: 0,
185
- showTouches: false,
186
- stayAwake: false,
187
- codecOptions: new CodecOptions({}),
188
- encoderName: "-",
189
- } as T;
130
+ parseDisplay(line: string): ScrcpyDisplay | undefined {
131
+ const displayIdRegex = /\s+scrcpy --display (\d+)/;
132
+ const match = line.match(displayIdRegex);
133
+ if (match) {
134
+ return {
135
+ id: Number.parseInt(match[1]!, 10),
136
+ };
137
+ }
138
+ return undefined;
190
139
  }
191
140
 
192
- public formatServerArguments(): string[] {
193
- const defaults = this.getDefaultValue();
194
- return this.getArgumentOrder().map((key) =>
195
- toScrcpyOptionValue(this.value[key] || defaults[key], "-")
196
- );
141
+ parseVideoStreamMetadata(
142
+ stream: ReadableStream<Uint8Array>,
143
+ ): ValueOrPromise<ScrcpyVideoStream> {
144
+ return (async () => {
145
+ const buffered = new BufferedReadableStream(stream);
146
+ const metadata: ScrcpyVideoStreamMetadata = {
147
+ codec: ScrcpyVideoCodecId.H264,
148
+ };
149
+ metadata.deviceName = await ScrcpyOptions1_16.parseCString(
150
+ buffered,
151
+ 64,
152
+ );
153
+ metadata.width = await ScrcpyOptions1_16.parseUint16BE(buffered);
154
+ metadata.height = await ScrcpyOptions1_16.parseUint16BE(buffered);
155
+ return { stream: buffered.release(), metadata };
156
+ })();
197
157
  }
198
158
 
199
- public getOutputEncoderNameRegex(): RegExp {
200
- return /\s+scrcpy --encoder-name '(.*?)'/;
159
+ parseAudioStreamMetadata(): never {
160
+ throw new Error("Not supported");
201
161
  }
202
162
 
203
- public createVideoStreamTransformer(): TransformStream<
163
+ createMediaStreamTransformer(): TransformStream<
204
164
  Uint8Array,
205
- ScrcpyVideoStreamPacket
165
+ ScrcpyMediaStreamPacket
206
166
  > {
207
167
  // Optimized path for video frames only
208
- if (this.value.sendFrameMeta === false) {
168
+ if (!this.value.sendFrameMeta) {
209
169
  return new TransformStream({
210
170
  transform(chunk, controller) {
211
171
  controller.enqueue({
212
- type: "frame",
172
+ type: "data",
213
173
  data: chunk,
214
174
  });
215
175
  },
216
176
  });
217
177
  }
218
178
 
219
- let header: Uint8Array | undefined;
220
-
221
- const deserializeStream = new StructDeserializeStream(VideoPacket);
179
+ const deserializeStream = new StructDeserializeStream(
180
+ ScrcpyMediaStreamRawPacket,
181
+ );
222
182
  return {
223
183
  writable: deserializeStream.writable,
224
184
  readable: deserializeStream.readable.pipeThrough(
225
185
  new TransformStream({
226
186
  transform(packet, controller) {
227
- if (packet.pts === NO_PTS) {
228
- const {
229
- sequenceParameterSet,
230
- pictureParameterSet,
231
- } = parseH264Configuration(packet.data);
232
-
233
- const {
234
- profile_idc: profileIndex,
235
- constraint_set: constraintSet,
236
- level_idc: levelIndex,
237
- pic_width_in_mbs_minus1,
238
- pic_height_in_map_units_minus1,
239
- frame_mbs_only_flag,
240
- frame_crop_left_offset,
241
- frame_crop_right_offset,
242
- frame_crop_top_offset,
243
- frame_crop_bottom_offset,
244
- } = parseSequenceParameterSet(
245
- removeH264Emulation(sequenceParameterSet)
246
- );
247
-
248
- const encodedWidth =
249
- (pic_width_in_mbs_minus1 + 1) * 16;
250
- const encodedHeight =
251
- (pic_height_in_map_units_minus1 + 1) *
252
- (2 - frame_mbs_only_flag) *
253
- 16;
254
- const cropLeft = frame_crop_left_offset * 2;
255
- const cropRight = frame_crop_right_offset * 2;
256
- const cropTop = frame_crop_top_offset * 2;
257
- const cropBottom = frame_crop_bottom_offset * 2;
258
-
259
- const croppedWidth =
260
- encodedWidth - cropLeft - cropRight;
261
- const croppedHeight =
262
- encodedHeight - cropTop - cropBottom;
263
-
264
- header = packet.data;
187
+ if (packet.pts === SCRCPY_MEDIA_PACKET_FLAG_CONFIG) {
265
188
  controller.enqueue({
266
189
  type: "configuration",
267
- pictureParameterSet,
268
- sequenceParameterSet,
269
- data: {
270
- profileIndex,
271
- constraintSet,
272
- levelIndex,
273
- encodedWidth,
274
- encodedHeight,
275
- cropLeft,
276
- cropRight,
277
- cropTop,
278
- cropBottom,
279
- croppedWidth,
280
- croppedHeight,
281
- },
190
+ data: packet.data,
282
191
  });
283
192
  return;
284
193
  }
285
194
 
286
- let frameData: Uint8Array;
287
- if (header) {
288
- frameData = new Uint8Array(
289
- header.byteLength + packet.data.byteLength
290
- );
291
- frameData.set(header);
292
- frameData.set(packet.data, header.byteLength);
293
- header = undefined;
294
- } else {
295
- frameData = packet.data;
296
- }
297
-
298
195
  controller.enqueue({
299
- type: "frame",
196
+ type: "data",
300
197
  pts: packet.pts,
301
- data: frameData,
198
+ data: packet.data,
302
199
  });
303
200
  },
304
- })
201
+ }),
305
202
  ),
306
203
  };
307
204
  }
308
205
 
309
- public getControlMessageTypes(): ScrcpyControlMessageType[] {
310
- return [
311
- /* 0 */ ScrcpyControlMessageType.InjectKeyCode,
312
- /* 1 */ ScrcpyControlMessageType.InjectText,
313
- /* 2 */ ScrcpyControlMessageType.InjectTouch,
314
- /* 3 */ ScrcpyControlMessageType.InjectScroll,
315
- /* 4 */ ScrcpyControlMessageType.BackOrScreenOn,
316
- /* 5 */ ScrcpyControlMessageType.ExpandNotificationPanel,
317
- /* 6 */ ScrcpyControlMessageType.CollapseNotificationPanel,
318
- /* 7 */ ScrcpyControlMessageType.GetClipboard,
319
- /* 8 */ ScrcpyControlMessageType.SetClipboard,
320
- /* 9 */ ScrcpyControlMessageType.SetScreenPowerMode,
321
- /* 10 */ ScrcpyControlMessageType.RotateDevice,
322
- ];
206
+ serializeInjectTouchControlMessage(
207
+ message: ScrcpyInjectTouchControlMessage,
208
+ ): Uint8Array {
209
+ return ScrcpyInjectTouchControlMessage1_16.serialize(message);
323
210
  }
324
211
 
325
- public serializeBackOrScreenOnControlMessage(
326
- message: ScrcpyBackOrScreenOnControlMessage
212
+ serializeBackOrScreenOnControlMessage(
213
+ message: ScrcpyBackOrScreenOnControlMessage,
327
214
  ) {
328
215
  if (message.action === AndroidKeyEventAction.Down) {
329
216
  return ScrcpyBackOrScreenOnControlMessage1_16.serialize(message);
@@ -332,13 +219,13 @@ export class ScrcpyOptions1_16<
332
219
  return undefined;
333
220
  }
334
221
 
335
- public serializeSetClipboardControlMessage(
336
- message: ScrcpySetClipboardControlMessage
222
+ serializeSetClipboardControlMessage(
223
+ message: ScrcpySetClipboardControlMessage,
337
224
  ): Uint8Array {
338
225
  return ScrcpySetClipboardControlMessage1_15.serialize(message);
339
226
  }
340
227
 
341
- public getScrollController(): ScrcpyScrollController {
228
+ createScrollController(): ScrcpyScrollController {
342
229
  return new ScrcpyScrollController1_16();
343
230
  }
344
231
  }
@@ -1,16 +1,15 @@
1
1
  import Struct from "@yume-chan/struct";
2
2
 
3
3
  import type { ScrcpyInjectScrollControlMessage } from "../../control/index.js";
4
- import { ScrcpyControlMessageType } from "../../control/index.js";
5
4
 
6
5
  export interface ScrcpyScrollController {
7
6
  serializeScrollMessage(
8
- message: ScrcpyInjectScrollControlMessage
7
+ message: ScrcpyInjectScrollControlMessage,
9
8
  ): Uint8Array | undefined;
10
9
  }
11
10
 
12
11
  export const ScrcpyInjectScrollControlMessage1_16 = new Struct()
13
- .uint8("type", ScrcpyControlMessageType.InjectScroll as const)
12
+ .uint8("type")
14
13
  .uint32("pointerX")
15
14
  .uint32("pointerY")
16
15
  .uint16("screenWidth")
@@ -25,31 +24,31 @@ export const ScrcpyInjectScrollControlMessage1_16 = new Struct()
25
24
  * reaches 1 or -1.
26
25
  */
27
26
  export class ScrcpyScrollController1_16 implements ScrcpyScrollController {
28
- private accumulatedX = 0;
29
- private accumulatedY = 0;
27
+ #accumulatedX = 0;
28
+ #accumulatedY = 0;
30
29
 
31
30
  protected processMessage(
32
- message: ScrcpyInjectScrollControlMessage
31
+ message: ScrcpyInjectScrollControlMessage,
33
32
  ): ScrcpyInjectScrollControlMessage | undefined {
34
- this.accumulatedX += message.scrollX;
35
- this.accumulatedY += message.scrollY;
33
+ this.#accumulatedX += message.scrollX;
34
+ this.#accumulatedY += message.scrollY;
36
35
 
37
36
  let scrollX = 0;
38
37
  let scrollY = 0;
39
- if (this.accumulatedX >= 1) {
38
+ if (this.#accumulatedX >= 1) {
40
39
  scrollX = 1;
41
- this.accumulatedX = 0;
42
- } else if (this.accumulatedX <= -1) {
40
+ this.#accumulatedX = 0;
41
+ } else if (this.#accumulatedX <= -1) {
43
42
  scrollX = -1;
44
- this.accumulatedX = 0;
43
+ this.#accumulatedX = 0;
45
44
  }
46
45
 
47
- if (this.accumulatedY >= 1) {
46
+ if (this.#accumulatedY >= 1) {
48
47
  scrollY = 1;
49
- this.accumulatedY = 0;
50
- } else if (this.accumulatedY <= -1) {
48
+ this.#accumulatedY = 0;
49
+ } else if (this.#accumulatedY <= -1) {
51
50
  scrollY = -1;
52
- this.accumulatedY = 0;
51
+ this.#accumulatedY = 0;
53
52
  }
54
53
 
55
54
  if (scrollX === 0 && scrollY === 0) {
@@ -61,8 +60,8 @@ export class ScrcpyScrollController1_16 implements ScrcpyScrollController {
61
60
  return message;
62
61
  }
63
62
 
64
- public serializeScrollMessage(
65
- message: ScrcpyInjectScrollControlMessage
63
+ serializeScrollMessage(
64
+ message: ScrcpyInjectScrollControlMessage,
66
65
  ): Uint8Array | undefined {
67
66
  const processed = this.processMessage(message);
68
67
  if (!processed) {
@@ -0,0 +1,66 @@
1
+ import type { ScrcpyOptionsInit1_16 } from "./1_16/index.js";
2
+ import { ScrcpyOptions1_16 } from "./1_16/index.js";
3
+ import type { ScrcpyEncoder } from "./types.js";
4
+ import { ScrcpyOptionsBase } from "./types.js";
5
+
6
+ export interface ScrcpyOptionsInit1_17 extends ScrcpyOptionsInit1_16 {
7
+ encoderName?: string | undefined;
8
+ }
9
+
10
+ export class ScrcpyOptions1_17 extends ScrcpyOptionsBase<
11
+ ScrcpyOptionsInit1_17,
12
+ ScrcpyOptions1_16
13
+ > {
14
+ static readonly DEFAULTS = {
15
+ ...ScrcpyOptions1_16.DEFAULTS,
16
+ encoderName: undefined,
17
+ } as const satisfies Required<ScrcpyOptionsInit1_17>;
18
+
19
+ static readonly SERIALIZE_ORDER = [
20
+ ...ScrcpyOptions1_16.SERIALIZE_ORDER,
21
+ "encoderName",
22
+ ] as const satisfies readonly (keyof ScrcpyOptionsInit1_17)[];
23
+
24
+ static parseEncoder(
25
+ line: string,
26
+ encoderNameRegex: RegExp,
27
+ ): ScrcpyEncoder | undefined {
28
+ const match = line.match(encoderNameRegex);
29
+ if (match) {
30
+ return { type: "video", name: match[1]! };
31
+ }
32
+ return undefined;
33
+ }
34
+
35
+ override get defaults(): Required<ScrcpyOptionsInit1_17> {
36
+ return ScrcpyOptions1_17.DEFAULTS;
37
+ }
38
+
39
+ constructor(init: ScrcpyOptionsInit1_17) {
40
+ super(new ScrcpyOptions1_16(init), {
41
+ ...ScrcpyOptions1_17.DEFAULTS,
42
+ ...init,
43
+ });
44
+ }
45
+
46
+ override serialize(): string[] {
47
+ return ScrcpyOptions1_16.serialize(
48
+ this.value,
49
+ ScrcpyOptions1_17.SERIALIZE_ORDER,
50
+ );
51
+ }
52
+
53
+ override setListEncoders() {
54
+ // Set to an invalid value
55
+ // Server will print valid values before crashing
56
+ // (server will crash after opening video and control sockets)
57
+ this.value.encoderName = "_";
58
+ }
59
+
60
+ override parseEncoder(line: string): ScrcpyEncoder | undefined {
61
+ return ScrcpyOptions1_17.parseEncoder(
62
+ line,
63
+ /\s+scrcpy --encoder-name '(.*?)'/,
64
+ );
65
+ }
66
+ }