@yume-chan/scrcpy 0.0.19 → 0.0.20

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 (252) hide show
  1. package/CHANGELOG.json +15 -0
  2. package/CHANGELOG.md +9 -1
  3. package/README.md +105 -181
  4. package/esm/codec/h264.d.ts +1 -21
  5. package/esm/codec/h264.d.ts.map +1 -1
  6. package/esm/codec/h264.js +13 -234
  7. package/esm/codec/h264.js.map +1 -1
  8. package/esm/codec/h265.d.ts +454 -0
  9. package/esm/codec/h265.d.ts.map +1 -1
  10. package/esm/codec/h265.js +1060 -0
  11. package/esm/codec/h265.js.map +1 -1
  12. package/esm/codec/index.d.ts +1 -0
  13. package/esm/codec/index.d.ts.map +1 -1
  14. package/esm/codec/index.js +1 -0
  15. package/esm/codec/index.js.map +1 -1
  16. package/esm/codec/nalu.d.ts +44 -0
  17. package/esm/codec/nalu.d.ts.map +1 -0
  18. package/esm/codec/nalu.js +310 -0
  19. package/esm/codec/nalu.js.map +1 -0
  20. package/esm/control/back-or-screen-on.d.ts +1 -1
  21. package/esm/control/back-or-screen-on.d.ts.map +1 -1
  22. package/esm/control/index.d.ts +2 -0
  23. package/esm/control/index.d.ts.map +1 -1
  24. package/esm/control/index.js +2 -0
  25. package/esm/control/index.js.map +1 -1
  26. package/esm/control/inject-keycode.d.ts +1 -2
  27. package/esm/control/inject-keycode.d.ts.map +1 -1
  28. package/esm/control/inject-keycode.js +1 -2
  29. package/esm/control/inject-keycode.js.map +1 -1
  30. package/esm/control/inject-text.d.ts +1 -2
  31. package/esm/control/inject-text.d.ts.map +1 -1
  32. package/esm/control/inject-text.js +1 -2
  33. package/esm/control/inject-text.js.map +1 -1
  34. package/esm/control/inject-touch.d.ts +15 -20
  35. package/esm/control/inject-touch.d.ts.map +1 -1
  36. package/esm/control/inject-touch.js +14 -41
  37. package/esm/control/inject-touch.js.map +1 -1
  38. package/esm/control/rotate-device.d.ts +2 -4
  39. package/esm/control/rotate-device.d.ts.map +1 -1
  40. package/esm/control/rotate-device.js +2 -3
  41. package/esm/control/rotate-device.js.map +1 -1
  42. package/esm/control/serializer.d.ts +13 -23
  43. package/esm/control/serializer.d.ts.map +1 -1
  44. package/esm/control/serializer.js +23 -52
  45. package/esm/control/serializer.js.map +1 -1
  46. package/esm/control/set-screen-power-mode.d.ts +1 -2
  47. package/esm/control/set-screen-power-mode.d.ts.map +1 -1
  48. package/esm/control/set-screen-power-mode.js +1 -2
  49. package/esm/control/set-screen-power-mode.js.map +1 -1
  50. package/esm/control/type.d.ts +14 -0
  51. package/esm/control/type.d.ts.map +1 -1
  52. package/esm/control/type.js +22 -0
  53. package/esm/control/type.js.map +1 -1
  54. package/esm/control/writer.d.ts +28 -0
  55. package/esm/control/writer.d.ts.map +1 -0
  56. package/esm/control/writer.js +53 -0
  57. package/esm/control/writer.js.map +1 -0
  58. package/esm/device-message/stream.d.ts.map +1 -1
  59. package/esm/device-message/stream.js +23 -8
  60. package/esm/device-message/stream.js.map +1 -1
  61. package/esm/index.d.ts +1 -2
  62. package/esm/index.d.ts.map +1 -1
  63. package/esm/index.js +1 -2
  64. package/esm/index.js.map +1 -1
  65. package/esm/options/1_16/codec-options.d.ts +2 -3
  66. package/esm/options/1_16/codec-options.d.ts.map +1 -1
  67. package/esm/options/1_16/codec-options.js.map +1 -1
  68. package/esm/options/1_16/index.d.ts +3 -1
  69. package/esm/options/1_16/index.d.ts.map +1 -1
  70. package/esm/options/1_16/index.js +3 -1
  71. package/esm/options/1_16/index.js.map +1 -1
  72. package/esm/options/1_16/options.d.ts +42 -96
  73. package/esm/options/1_16/options.d.ts.map +1 -1
  74. package/esm/options/1_16/options.js +106 -143
  75. package/esm/options/1_16/options.js.map +1 -1
  76. package/esm/options/1_16/scroll.d.ts +1 -2
  77. package/esm/options/1_16/scroll.d.ts.map +1 -1
  78. package/esm/options/1_16/scroll.js +1 -2
  79. package/esm/options/1_16/scroll.js.map +1 -1
  80. package/esm/options/1_17.d.ts +1 -0
  81. package/esm/options/1_17.d.ts.map +1 -1
  82. package/esm/options/1_17.js +7 -1
  83. package/esm/options/1_17.js.map +1 -1
  84. package/esm/options/1_18.d.ts +49 -11
  85. package/esm/options/1_18.d.ts.map +1 -1
  86. package/esm/options/1_18.js +65 -32
  87. package/esm/options/1_18.js.map +1 -1
  88. package/esm/options/1_21.d.ts +26 -7
  89. package/esm/options/1_21.d.ts.map +1 -1
  90. package/esm/options/1_21.js +35 -20
  91. package/esm/options/1_21.js.map +1 -1
  92. package/esm/options/1_22/index.d.ts +1 -0
  93. package/esm/options/1_22/index.d.ts.map +1 -1
  94. package/esm/options/1_22/index.js +1 -0
  95. package/esm/options/1_22/index.js.map +1 -1
  96. package/esm/options/1_22/options.d.ts +32 -26
  97. package/esm/options/1_22/options.d.ts.map +1 -1
  98. package/esm/options/1_22/options.js +27 -16
  99. package/esm/options/1_22/options.js.map +1 -1
  100. package/esm/options/1_22/scroll.d.ts +1 -1
  101. package/esm/options/1_22/scroll.js +1 -1
  102. package/esm/options/1_23.d.ts +32 -8
  103. package/esm/options/1_23.d.ts.map +1 -1
  104. package/esm/options/1_23.js +24 -14
  105. package/esm/options/1_23.js.map +1 -1
  106. package/esm/options/1_24.d.ts +29 -4
  107. package/esm/options/1_24.d.ts.map +1 -1
  108. package/esm/options/1_24.js +17 -7
  109. package/esm/options/1_24.js.map +1 -1
  110. package/esm/options/1_25/options.d.ts +6 -2
  111. package/esm/options/1_25/options.d.ts.map +1 -1
  112. package/esm/options/1_25/options.js +16 -2
  113. package/esm/options/1_25/options.js.map +1 -1
  114. package/esm/options/1_25/scroll.d.ts.map +1 -1
  115. package/esm/options/1_25/scroll.js +2 -1
  116. package/esm/options/1_25/scroll.js.map +1 -1
  117. package/esm/options/2_0.d.ts +12 -8
  118. package/esm/options/2_0.d.ts.map +1 -1
  119. package/esm/options/2_0.js +80 -11
  120. package/esm/options/2_0.js.map +1 -1
  121. package/esm/options/codec.d.ts +4 -4
  122. package/esm/options/codec.d.ts.map +1 -1
  123. package/esm/options/codec.js +3 -3
  124. package/esm/options/codec.js.map +1 -1
  125. package/esm/options/index.d.ts +4 -0
  126. package/esm/options/index.d.ts.map +1 -1
  127. package/esm/options/index.js +4 -0
  128. package/esm/options/index.js.map +1 -1
  129. package/esm/options/types.d.ts +73 -33
  130. package/esm/options/types.d.ts.map +1 -1
  131. package/esm/options/types.js +52 -0
  132. package/esm/options/types.js.map +1 -1
  133. package/package.json +7 -14
  134. package/src/codec/av1.ts +33 -0
  135. package/src/codec/h264.ts +317 -0
  136. package/src/codec/h265.ts +1375 -0
  137. package/src/codec/index.ts +4 -0
  138. package/src/codec/nalu.ts +373 -0
  139. package/src/control/back-or-screen-on.ts +1 -1
  140. package/{esm/control/simple.js → src/control/basic.ts} +3 -3
  141. package/src/control/index.ts +2 -0
  142. package/src/control/inject-keycode.ts +1 -3
  143. package/src/control/inject-text.ts +1 -3
  144. package/src/control/inject-touch.ts +16 -55
  145. package/src/control/rotate-device.ts +2 -7
  146. package/src/control/serializer.ts +42 -101
  147. package/src/control/set-screen-power-mode.ts +1 -3
  148. package/src/control/type.ts +32 -0
  149. package/src/control/writer.ts +87 -0
  150. package/src/device-message/stream.ts +32 -12
  151. package/src/index.ts +1 -2
  152. package/src/options/1_16/codec-options.ts +2 -3
  153. package/src/options/1_16/float-to-uint16.ts +33 -0
  154. package/src/options/1_16/index.ts +3 -1
  155. package/src/options/1_16/init.ts +80 -0
  156. package/src/options/1_16/message.ts +55 -0
  157. package/src/options/1_16/options.ts +146 -255
  158. package/src/options/1_16/scroll.ts +1 -2
  159. package/src/options/1_17.ts +66 -0
  160. package/src/options/1_18.ts +101 -37
  161. package/src/options/1_21.ts +47 -25
  162. package/src/options/1_22/index.ts +1 -0
  163. package/src/options/1_22/init.ts +19 -0
  164. package/src/options/1_22/options.ts +38 -45
  165. package/src/options/1_22/scroll.ts +1 -1
  166. package/src/options/1_23.ts +33 -21
  167. package/src/options/1_24.ts +23 -11
  168. package/src/options/1_25/options.ts +22 -4
  169. package/src/options/1_25/scroll.ts +2 -1
  170. package/src/options/2_0.ts +342 -0
  171. package/src/options/codec.ts +98 -0
  172. package/src/options/index.ts +4 -0
  173. package/src/options/latest.ts +14 -0
  174. package/src/options/types.ts +152 -42
  175. package/tsconfig.build.json +9 -1
  176. package/tsconfig.build.tsbuildinfo +1 -1
  177. package/esm/adb/client.d.ts +0 -41
  178. package/esm/adb/client.d.ts.map +0 -1
  179. package/esm/adb/client.js +0 -224
  180. package/esm/adb/client.js.map +0 -1
  181. package/esm/adb/connection.d.ts +0 -49
  182. package/esm/adb/connection.d.ts.map +0 -1
  183. package/esm/adb/connection.js +0 -122
  184. package/esm/adb/connection.js.map +0 -1
  185. package/esm/adb/index.d.ts +0 -4
  186. package/esm/adb/index.d.ts.map +0 -1
  187. package/esm/adb/index.js +0 -4
  188. package/esm/adb/index.js.map +0 -1
  189. package/esm/adb/options/1_16.d.ts +0 -8
  190. package/esm/adb/options/1_16.d.ts.map +0 -1
  191. package/esm/adb/options/1_16.js +0 -20
  192. package/esm/adb/options/1_16.js.map +0 -1
  193. package/esm/adb/options/1_22.d.ts +0 -8
  194. package/esm/adb/options/1_22.d.ts.map +0 -1
  195. package/esm/adb/options/1_22.js +0 -17
  196. package/esm/adb/options/1_22.js.map +0 -1
  197. package/esm/adb/options/1_26.d.ts +0 -9
  198. package/esm/adb/options/1_26.d.ts.map +0 -1
  199. package/esm/adb/options/1_26.js +0 -11
  200. package/esm/adb/options/1_26.js.map +0 -1
  201. package/esm/adb/options/2_0.d.ts +0 -8
  202. package/esm/adb/options/2_0.d.ts.map +0 -1
  203. package/esm/adb/options/2_0.js +0 -13
  204. package/esm/adb/options/2_0.js.map +0 -1
  205. package/esm/adb/options/index.d.ts +0 -4
  206. package/esm/adb/options/index.d.ts.map +0 -1
  207. package/esm/adb/options/index.js +0 -4
  208. package/esm/adb/options/index.js.map +0 -1
  209. package/esm/adb/options/types.d.ts +0 -24
  210. package/esm/adb/options/types.d.ts.map +0 -1
  211. package/esm/adb/options/types.js +0 -37
  212. package/esm/adb/options/types.js.map +0 -1
  213. package/esm/codec.d.ts +0 -34
  214. package/esm/codec.d.ts.map +0 -1
  215. package/esm/codec.js +0 -37
  216. package/esm/codec.js.map +0 -1
  217. package/esm/control/simple.d.ts +0 -5
  218. package/esm/control/simple.d.ts.map +0 -1
  219. package/esm/control/simple.js.map +0 -1
  220. package/esm/decoder.d.ts +0 -2
  221. package/esm/decoder.d.ts.map +0 -1
  222. package/esm/decoder.js +0 -2
  223. package/esm/decoder.js.map +0 -1
  224. package/esm/options/1.17.d.ts +0 -3
  225. package/esm/options/1.17.d.ts.map +0 -1
  226. package/esm/options/1.17.js +0 -2
  227. package/esm/options/1.17.js.map +0 -1
  228. package/esm/options/1_16/h264-configuration.d.ts +0 -49
  229. package/esm/options/1_16/h264-configuration.d.ts.map +0 -1
  230. package/esm/options/1_16/h264-configuration.js +0 -414
  231. package/esm/options/1_16/h264-configuration.js.map +0 -1
  232. package/esm/options/1_16/sps.d.ts +0 -22
  233. package/esm/options/1_16/sps.d.ts.map +0 -1
  234. package/esm/options/1_16/sps.js +0 -263
  235. package/esm/options/1_16/sps.js.map +0 -1
  236. package/esm/options/1_26.d.ts +0 -57
  237. package/esm/options/1_26.d.ts.map +0 -1
  238. package/esm/options/1_26.js +0 -67
  239. package/esm/options/1_26.js.map +0 -1
  240. package/esm/options/2.d.ts +0 -57
  241. package/esm/options/2.d.ts.map +0 -1
  242. package/esm/options/2.js +0 -67
  243. package/esm/options/2.js.map +0 -1
  244. package/src/adb/client.ts +0 -329
  245. package/src/adb/connection.ts +0 -209
  246. package/src/adb/index.ts +0 -3
  247. package/src/adb/options/1_16.ts +0 -32
  248. package/src/adb/options/1_22.ts +0 -26
  249. package/src/adb/options/index.ts +0 -3
  250. package/src/adb/options/types.ts +0 -75
  251. package/src/codec.ts +0 -35
  252. package/src/options/1_16/h264-configuration.ts +0 -485
@@ -0,0 +1,342 @@
1
+ import type { ReadableStream } from "@yume-chan/stream-extra";
2
+ import {
3
+ BufferedReadableStream,
4
+ PushReadableStream,
5
+ } from "@yume-chan/stream-extra";
6
+ import type { ValueOrPromise } from "@yume-chan/struct";
7
+ import Struct, { NumberFieldType, placeholder } from "@yume-chan/struct";
8
+
9
+ import type {
10
+ AndroidMotionEventAction,
11
+ ScrcpyInjectTouchControlMessage,
12
+ } from "../control/index.js";
13
+
14
+ import {
15
+ CodecOptions,
16
+ ScrcpyFloatToUint16FieldDefinition,
17
+ ScrcpyOptions1_16,
18
+ } from "./1_16/index.js";
19
+ import { ScrcpyOptions1_21 } from "./1_21.js";
20
+ import type { ScrcpyOptionsInit1_24 } from "./1_24.js";
21
+ import { ScrcpyOptions1_24 } from "./1_24.js";
22
+ import { ScrcpyOptions1_25 } from "./1_25/index.js";
23
+ import type { ScrcpyAudioStreamMetadata, ScrcpyVideoStream } from "./codec.js";
24
+ import { ScrcpyAudioCodec, ScrcpyVideoCodecId } from "./codec.js";
25
+ import type {
26
+ ScrcpyDisplay,
27
+ ScrcpyEncoder,
28
+ ScrcpyOptionValue,
29
+ } from "./types.js";
30
+ import { ScrcpyOptionsBase } from "./types.js";
31
+
32
+ export const ScrcpyInjectTouchControlMessage2_0 = new Struct()
33
+ .uint8("type")
34
+ .uint8("action", placeholder<AndroidMotionEventAction>())
35
+ .uint64("pointerId")
36
+ .uint32("pointerX")
37
+ .uint32("pointerY")
38
+ .uint16("screenWidth")
39
+ .uint16("screenHeight")
40
+ .field("pressure", ScrcpyFloatToUint16FieldDefinition)
41
+ .uint32("actionButton")
42
+ .uint32("buttons");
43
+
44
+ export type ScrcpyInjectTouchControlMessage2_0 =
45
+ (typeof ScrcpyInjectTouchControlMessage2_0)["TInit"];
46
+
47
+ export class ScrcpyInstanceId implements ScrcpyOptionValue {
48
+ public static readonly NONE = new ScrcpyInstanceId(-1);
49
+
50
+ public static random(): ScrcpyInstanceId {
51
+ // A random 31-bit unsigned integer
52
+ return new ScrcpyInstanceId((Math.random() * 0x80000000) | 0);
53
+ }
54
+
55
+ public value: number;
56
+
57
+ public constructor(value: number) {
58
+ this.value = value;
59
+ }
60
+
61
+ public toOptionValue(): string | undefined {
62
+ if (this.value < 0) {
63
+ return undefined;
64
+ }
65
+ return this.value.toString(16);
66
+ }
67
+ }
68
+
69
+ export interface ScrcpyOptionsInit2_0
70
+ extends Omit<
71
+ ScrcpyOptionsInit1_24,
72
+ "bitRate" | "codecOptions" | "encoderName"
73
+ > {
74
+ scid?: ScrcpyInstanceId;
75
+
76
+ videoCodec?: "h264" | "h265" | "av1";
77
+ videoBitRate?: number;
78
+ videoCodecOptions?: CodecOptions;
79
+ videoEncoder?: string;
80
+
81
+ audio?: boolean;
82
+ audioCodec?: "opus" | "aac" | "raw";
83
+ audioBitRate?: number;
84
+ audioCodecOptions?: CodecOptions;
85
+ audioEncoder?: string;
86
+
87
+ listEncoders?: boolean;
88
+ listDisplays?: boolean;
89
+ sendCodecMeta?: boolean;
90
+ }
91
+
92
+ function omit<T extends object, K extends keyof T>(
93
+ obj: T,
94
+ keys: K[]
95
+ ): Omit<T, K> {
96
+ const result: Record<PropertyKey, unknown> = {};
97
+ for (const key in obj) {
98
+ if (!keys.includes(key as keyof T as K)) {
99
+ result[key] = obj[key];
100
+ }
101
+ }
102
+ return result as Omit<T, K>;
103
+ }
104
+
105
+ export class ScrcpyOptions2_0 extends ScrcpyOptionsBase<
106
+ ScrcpyOptionsInit2_0,
107
+ ScrcpyOptions1_25
108
+ > {
109
+ public static readonly DEFAULTS = {
110
+ ...omit(ScrcpyOptions1_24.DEFAULTS, [
111
+ "bitRate",
112
+ "codecOptions",
113
+ "encoderName",
114
+ ]),
115
+ scid: ScrcpyInstanceId.NONE,
116
+
117
+ videoCodec: "h264",
118
+ videoBitRate: 8000000,
119
+ videoCodecOptions: new CodecOptions(),
120
+ videoEncoder: "",
121
+
122
+ audio: true,
123
+ audioCodec: "opus",
124
+ audioBitRate: 128000,
125
+ audioCodecOptions: new CodecOptions(),
126
+ audioEncoder: "",
127
+
128
+ listEncoders: false,
129
+ listDisplays: false,
130
+ sendCodecMeta: true,
131
+ } as const satisfies Required<ScrcpyOptionsInit2_0>;
132
+
133
+ public override get defaults(): Required<ScrcpyOptionsInit2_0> {
134
+ return ScrcpyOptions2_0.DEFAULTS;
135
+ }
136
+
137
+ public constructor(init: ScrcpyOptionsInit2_0) {
138
+ super(new ScrcpyOptions1_25(init), {
139
+ ...ScrcpyOptions2_0.DEFAULTS,
140
+ ...init,
141
+ });
142
+ }
143
+
144
+ public override serialize(): string[] {
145
+ return ScrcpyOptions1_21.serialize(this.value, this.defaults);
146
+ }
147
+
148
+ public override setListEncoders(): void {
149
+ this.value.listEncoders = true;
150
+ }
151
+
152
+ public override setListDisplays(): void {
153
+ this.value.listDisplays = true;
154
+ }
155
+
156
+ public override parseEncoder(line: string): ScrcpyEncoder | undefined {
157
+ let match = line.match(
158
+ /\s+--video-codec=(.*)\s+--video-encoder='(.*)'/
159
+ );
160
+ if (match) {
161
+ return {
162
+ type: "video",
163
+ codec: match[1]!,
164
+ name: match[2]!,
165
+ };
166
+ }
167
+
168
+ match = line.match(/\s+--audio-codec=(.*)\s+--audio-encoder='(.*)'/);
169
+ if (match) {
170
+ return {
171
+ type: "audio",
172
+ codec: match[1]!,
173
+ name: match[2]!,
174
+ };
175
+ }
176
+
177
+ return undefined;
178
+ }
179
+
180
+ public override parseDisplay(line: string): ScrcpyDisplay | undefined {
181
+ const match = line.match(/\s+--display=(\d+)\s+\((.*?)\)/);
182
+ if (match) {
183
+ const display: ScrcpyDisplay = {
184
+ id: Number.parseInt(match[1]!, 10),
185
+ };
186
+ if (match[2] !== "size unknown") {
187
+ display.resolution = match[2]!;
188
+ }
189
+ return display;
190
+ }
191
+ return undefined;
192
+ }
193
+
194
+ public override parseVideoStreamMetadata(
195
+ stream: ReadableStream<Uint8Array>
196
+ ): ValueOrPromise<ScrcpyVideoStream> {
197
+ const { sendDeviceMeta, sendCodecMeta } = this.value;
198
+ if (!sendDeviceMeta && !sendCodecMeta) {
199
+ let codec: ScrcpyVideoCodecId;
200
+ switch (this.value.videoCodec) {
201
+ case "h264":
202
+ codec = ScrcpyVideoCodecId.H264;
203
+ break;
204
+ case "h265":
205
+ codec = ScrcpyVideoCodecId.H265;
206
+ break;
207
+ case "av1":
208
+ codec = ScrcpyVideoCodecId.AV1;
209
+ break;
210
+ }
211
+ return { stream, metadata: { codec } };
212
+ }
213
+
214
+ return (async () => {
215
+ const buffered = new BufferedReadableStream(stream);
216
+
217
+ // `sendDeviceMeta` now only contains device name,
218
+ // can't use `super.parseVideoStreamMetadata` here
219
+ let deviceName: string | undefined;
220
+ if (sendDeviceMeta) {
221
+ deviceName = await ScrcpyOptions1_16.parseCString(buffered, 64);
222
+ }
223
+
224
+ let codec: ScrcpyVideoCodecId;
225
+ let width: number | undefined;
226
+ let height: number | undefined;
227
+ if (sendCodecMeta) {
228
+ codec = await ScrcpyOptions1_16.parseUint32BE(buffered);
229
+ width = await ScrcpyOptions1_16.parseUint32BE(buffered);
230
+ height = await ScrcpyOptions1_16.parseUint32BE(buffered);
231
+ } else {
232
+ switch (this.value.videoCodec) {
233
+ case "h264":
234
+ codec = ScrcpyVideoCodecId.H264;
235
+ break;
236
+ case "h265":
237
+ codec = ScrcpyVideoCodecId.H265;
238
+ break;
239
+ case "av1":
240
+ codec = ScrcpyVideoCodecId.AV1;
241
+ break;
242
+ }
243
+ }
244
+
245
+ return {
246
+ stream: buffered.release(),
247
+ metadata: { deviceName, codec, width, height },
248
+ };
249
+ })();
250
+ }
251
+
252
+ public override parseAudioStreamMetadata(
253
+ stream: ReadableStream<Uint8Array>
254
+ ): ValueOrPromise<ScrcpyAudioStreamMetadata> {
255
+ return (async (): Promise<ScrcpyAudioStreamMetadata> => {
256
+ const buffered = new BufferedReadableStream(stream);
257
+ const buffer = await buffered.readExactly(
258
+ NumberFieldType.Uint32.size
259
+ );
260
+
261
+ const codecMetadataValue = NumberFieldType.Uint32.deserialize(
262
+ buffer,
263
+ false
264
+ );
265
+ // Server will send `0x00_00_00_00` and `0x00_00_00_01` even if `sendCodecMeta` is false
266
+ switch (codecMetadataValue) {
267
+ case 0x00_00_00_00:
268
+ return {
269
+ type: "disabled",
270
+ };
271
+ case 0x00_00_00_01:
272
+ return {
273
+ type: "errored",
274
+ };
275
+ }
276
+
277
+ if (this.value.sendCodecMeta) {
278
+ let codec: ScrcpyAudioCodec;
279
+ switch (codecMetadataValue) {
280
+ case ScrcpyAudioCodec.OPUS.metadataValue:
281
+ codec = ScrcpyAudioCodec.OPUS;
282
+ break;
283
+ case ScrcpyAudioCodec.AAC.metadataValue:
284
+ codec = ScrcpyAudioCodec.AAC;
285
+ break;
286
+ case ScrcpyAudioCodec.RAW.metadataValue:
287
+ codec = ScrcpyAudioCodec.RAW;
288
+ break;
289
+ default:
290
+ throw new Error(
291
+ `Unknown audio codec metadata value: ${codecMetadataValue}`
292
+ );
293
+ }
294
+ return {
295
+ type: "success",
296
+ codec,
297
+ stream: buffered.release(),
298
+ };
299
+ }
300
+
301
+ // Infer codec from `audioCodec` option
302
+ let codec: ScrcpyAudioCodec;
303
+ switch (this.value.audioCodec) {
304
+ case "opus":
305
+ codec = ScrcpyAudioCodec.OPUS;
306
+ break;
307
+ case "aac":
308
+ codec = ScrcpyAudioCodec.AAC;
309
+ break;
310
+ case "raw":
311
+ codec = ScrcpyAudioCodec.RAW;
312
+ break;
313
+ }
314
+ return {
315
+ type: "success",
316
+ codec,
317
+ stream: new PushReadableStream<Uint8Array>(
318
+ async (controller) => {
319
+ // Put the first 4 bytes back
320
+ await controller.enqueue(buffer);
321
+
322
+ const stream = buffered.release();
323
+ const reader = stream.getReader();
324
+ while (true) {
325
+ const { done, value } = await reader.read();
326
+ if (done) {
327
+ break;
328
+ }
329
+ await controller.enqueue(value);
330
+ }
331
+ }
332
+ ),
333
+ };
334
+ })();
335
+ }
336
+
337
+ public override serializeInjectTouchControlMessage(
338
+ message: ScrcpyInjectTouchControlMessage
339
+ ): Uint8Array {
340
+ return ScrcpyInjectTouchControlMessage2_0.serialize(message);
341
+ }
342
+ }
@@ -0,0 +1,98 @@
1
+ import type { ReadableStream } from "@yume-chan/stream-extra";
2
+
3
+ import type { ScrcpyOptionValue } from "./types.js";
4
+
5
+ export enum ScrcpyVideoCodecId {
6
+ H264 = 0x68_32_36_34,
7
+ H265 = 0x68_32_36_35,
8
+ AV1 = 0x00_61_76_31,
9
+ }
10
+
11
+ export interface ScrcpyVideoStreamMetadata {
12
+ deviceName?: string | undefined;
13
+ width?: number | undefined;
14
+ height?: number | undefined;
15
+ codec: ScrcpyVideoCodecId;
16
+ }
17
+
18
+ export interface ScrcpyVideoStream {
19
+ readonly stream: ReadableStream<Uint8Array>;
20
+ readonly metadata: ScrcpyVideoStreamMetadata;
21
+ }
22
+
23
+ export class ScrcpyAudioCodec implements ScrcpyOptionValue {
24
+ public static readonly OPUS = new ScrcpyAudioCodec(
25
+ "opus",
26
+ 0x6f_70_75_73,
27
+ "audio/opus",
28
+ "opus"
29
+ );
30
+ public static readonly AAC = new ScrcpyAudioCodec(
31
+ "aac",
32
+ 0x00_61_61_63,
33
+ "audio/aac",
34
+ "mp4a.66"
35
+ );
36
+ public static readonly RAW = new ScrcpyAudioCodec(
37
+ "raw",
38
+ 0x00_72_61_77,
39
+ "audio/raw",
40
+ "1"
41
+ );
42
+
43
+ public readonly optionValue: string;
44
+ public readonly metadataValue: number;
45
+ public readonly mimeType: string;
46
+ public readonly webCodecId: string;
47
+
48
+ public constructor(
49
+ optionValue: string,
50
+ metadataValue: number,
51
+ mimeType: string,
52
+ webCodecId: string
53
+ ) {
54
+ this.optionValue = optionValue;
55
+ this.metadataValue = metadataValue;
56
+ this.mimeType = mimeType;
57
+ this.webCodecId = webCodecId;
58
+ }
59
+
60
+ public toOptionValue(): string {
61
+ return this.optionValue;
62
+ }
63
+ }
64
+
65
+ export interface ScrcpyAudioStreamDisabledMetadata {
66
+ readonly type: "disabled";
67
+ }
68
+
69
+ export interface ScrcpyAudioStreamErroredMetadata {
70
+ readonly type: "errored";
71
+ }
72
+
73
+ export interface ScrcpyAudioStreamSuccessMetadata {
74
+ readonly type: "success";
75
+ readonly codec: ScrcpyAudioCodec;
76
+ readonly stream: ReadableStream<Uint8Array>;
77
+ }
78
+
79
+ export type ScrcpyAudioStreamMetadata =
80
+ | ScrcpyAudioStreamDisabledMetadata
81
+ | ScrcpyAudioStreamErroredMetadata
82
+ | ScrcpyAudioStreamSuccessMetadata;
83
+
84
+ export interface ScrcpyMediaStreamConfigurationPacket {
85
+ type: "configuration";
86
+ data: Uint8Array;
87
+ }
88
+
89
+ export interface ScrcpyMediaStreamDataPacket {
90
+ type: "data";
91
+ keyframe?: boolean;
92
+ pts?: bigint;
93
+ data: Uint8Array;
94
+ }
95
+
96
+ export type ScrcpyMediaStreamPacket =
97
+ | ScrcpyMediaStreamConfigurationPacket
98
+ | ScrcpyMediaStreamDataPacket;
@@ -1,8 +1,12 @@
1
1
  export * from "./1_16/index.js";
2
+ export * from "./1_17.js";
2
3
  export * from "./1_18.js";
3
4
  export * from "./1_21.js";
4
5
  export * from "./1_22/index.js";
5
6
  export * from "./1_23.js";
6
7
  export * from "./1_24.js";
7
8
  export * from "./1_25/index.js";
9
+ export * from "./2_0.js";
10
+ export * from "./codec.js";
11
+ export * from "./latest.js";
8
12
  export * from "./types.js";
@@ -0,0 +1,14 @@
1
+ import { ScrcpyLogLevel1_18, ScrcpyVideoOrientation1_18 } from "./1_18.js";
2
+ import type { ScrcpyOptionsInit2_0 } from "./2_0.js";
3
+ import { ScrcpyOptions2_0 } from "./2_0.js";
4
+
5
+ export const ScrcpyLogLevel = ScrcpyLogLevel1_18;
6
+ export type ScrcpyLogLevel = ScrcpyLogLevel1_18;
7
+
8
+ export const ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
9
+ export type ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
10
+
11
+ export type ScrcpyOptionsInitLatest = ScrcpyOptionsInit2_0;
12
+ export class ScrcpyOptionsLatest extends ScrcpyOptions2_0 {}
13
+
14
+ export const ScrcpyLatestVersion = "2.0";
@@ -1,12 +1,19 @@
1
- import type { TransformStream } from "@yume-chan/stream-extra";
1
+ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
2
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
3
 
3
4
  import type {
4
5
  ScrcpyBackOrScreenOnControlMessage,
5
6
  ScrcpyControlMessageType,
7
+ ScrcpyInjectTouchControlMessage,
6
8
  ScrcpySetClipboardControlMessage,
7
9
  } from "../control/index.js";
8
10
 
9
11
  import type { ScrcpyScrollController } from "./1_16/scroll.js";
12
+ import type {
13
+ ScrcpyAudioStreamMetadata,
14
+ ScrcpyMediaStreamPacket,
15
+ ScrcpyVideoStream,
16
+ } from "./codec.js";
10
17
 
11
18
  export const DEFAULT_SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
12
19
 
@@ -38,65 +45,168 @@ export function toScrcpyOptionValue<T>(value: unknown, empty: T): string | T {
38
45
  return String(value);
39
46
  }
40
47
 
41
- export interface H264Configuration {
42
- profileIndex: number;
43
- constraintSet: number;
44
- levelIndex: number;
48
+ export interface ScrcpyEncoder {
49
+ type: "video" | "audio";
50
+ codec?: string;
51
+ name: string;
52
+ }
45
53
 
46
- encodedWidth: number;
47
- encodedHeight: number;
54
+ export interface ScrcpyDisplay {
55
+ id: number;
56
+ resolution?: string;
57
+ }
48
58
 
49
- cropLeft: number;
50
- cropRight: number;
59
+ export interface ScrcpyOptions<T extends object> {
60
+ readonly defaults: Required<T>;
61
+
62
+ readonly controlMessageTypes: readonly ScrcpyControlMessageType[];
63
+
64
+ readonly value: Required<T>;
65
+
66
+ serialize(): string[];
67
+
68
+ /**
69
+ * Set the essential options to let Scrcpy server print out available encoders.
70
+ */
71
+ setListEncoders(): void;
72
+
73
+ /**
74
+ * Set the essential options to let Scrcpy server print out available displays.
75
+ */
76
+ setListDisplays(): void;
77
+
78
+ /**
79
+ * Parse encoder information from Scrcpy server output
80
+ * @param line One line of Scrcpy server output
81
+ */
82
+ parseEncoder(line: string): ScrcpyEncoder | undefined;
83
+
84
+ /**
85
+ * Parse display information from Scrcpy server output
86
+ * @param line One line of Scrcpy server output
87
+ */
88
+ parseDisplay(line: string): ScrcpyDisplay | undefined;
89
+
90
+ /**
91
+ * Parse the device metadata from video stream according to the current version and options.
92
+ * @param stream The video stream.
93
+ * @returns
94
+ * A tuple of the video stream and the metadata.
95
+ *
96
+ * The returned video stream may be different from the input stream, and should be used for further processing.
97
+ */
98
+ parseVideoStreamMetadata(
99
+ stream: ReadableStream<Uint8Array>
100
+ ): ValueOrPromise<ScrcpyVideoStream>;
101
+
102
+ parseAudioStreamMetadata(
103
+ stream: ReadableStream<Uint8Array>
104
+ ): ValueOrPromise<ScrcpyAudioStreamMetadata>;
105
+
106
+ createMediaStreamTransformer(): TransformStream<
107
+ Uint8Array,
108
+ ScrcpyMediaStreamPacket
109
+ >;
51
110
 
52
- cropTop: number;
53
- cropBottom: number;
111
+ serializeInjectTouchControlMessage(
112
+ message: ScrcpyInjectTouchControlMessage
113
+ ): Uint8Array;
54
114
 
55
- croppedWidth: number;
56
- croppedHeight: number;
57
- }
115
+ serializeBackOrScreenOnControlMessage(
116
+ message: ScrcpyBackOrScreenOnControlMessage
117
+ ): Uint8Array | undefined;
58
118
 
59
- export interface ScrcpyVideoStreamConfigurationPacket {
60
- type: "configuration";
61
- sequenceParameterSet: Uint8Array;
62
- pictureParameterSet: Uint8Array;
63
- data: H264Configuration;
64
- }
119
+ serializeSetClipboardControlMessage(
120
+ message: ScrcpySetClipboardControlMessage
121
+ ): Uint8Array;
65
122
 
66
- export interface ScrcpyVideoStreamFramePacket {
67
- type: "frame";
68
- keyframe?: boolean;
69
- pts?: bigint;
70
- data: Uint8Array;
123
+ createScrollController(): ScrcpyScrollController;
71
124
  }
72
125
 
73
- export type ScrcpyVideoStreamPacket =
74
- | ScrcpyVideoStreamConfigurationPacket
75
- | ScrcpyVideoStreamFramePacket;
126
+ export abstract class ScrcpyOptionsBase<
127
+ T extends object,
128
+ B extends ScrcpyOptions<object>
129
+ > implements ScrcpyOptions<T>
130
+ {
131
+ protected _base: B;
76
132
 
77
- export interface ScrcpyOptions<T extends object> {
78
- value: Partial<T>;
133
+ public abstract get defaults(): Required<T>;
79
134
 
80
- getDefaultValue(): T;
135
+ public get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
136
+ return this._base.controlMessageTypes;
137
+ }
138
+
139
+ public readonly value: Required<T>;
81
140
 
82
- formatServerArguments(): string[];
141
+ public constructor(base: B, value: Required<T>) {
142
+ this._base = base;
143
+ this.value = value;
144
+ }
83
145
 
84
- getOutputEncoderNameRegex(): RegExp;
146
+ public abstract serialize(): string[];
147
+
148
+ public setListEncoders(): void {
149
+ this._base.setListEncoders();
150
+ }
85
151
 
86
- createVideoStreamTransformer(): TransformStream<
152
+ public setListDisplays(): void {
153
+ this._base.setListDisplays();
154
+ }
155
+
156
+ public parseEncoder(line: string): ScrcpyEncoder | undefined {
157
+ return this._base.parseEncoder(line);
158
+ }
159
+
160
+ public parseDisplay(line: string): ScrcpyDisplay | undefined {
161
+ return this._base.parseDisplay(line);
162
+ }
163
+
164
+ /**
165
+ * Parse the device metadata from video stream according to the current version and options.
166
+ * @param stream The video stream.
167
+ * @returns
168
+ * A tuple of the video stream and the metadata.
169
+ *
170
+ * The returned video stream may be different from the input stream, and should be used for further processing.
171
+ */
172
+ public parseVideoStreamMetadata(
173
+ stream: ReadableStream<Uint8Array>
174
+ ): ValueOrPromise<ScrcpyVideoStream> {
175
+ return this._base.parseVideoStreamMetadata(stream);
176
+ }
177
+
178
+ public parseAudioStreamMetadata(
179
+ stream: ReadableStream<Uint8Array>
180
+ ): ValueOrPromise<ScrcpyAudioStreamMetadata> {
181
+ return this._base.parseAudioStreamMetadata(stream);
182
+ }
183
+
184
+ public createMediaStreamTransformer(): TransformStream<
87
185
  Uint8Array,
88
- ScrcpyVideoStreamPacket
89
- >;
186
+ ScrcpyMediaStreamPacket
187
+ > {
188
+ return this._base.createMediaStreamTransformer();
189
+ }
90
190
 
91
- getControlMessageTypes(): ScrcpyControlMessageType[];
191
+ public serializeInjectTouchControlMessage(
192
+ message: ScrcpyInjectTouchControlMessage
193
+ ): Uint8Array {
194
+ return this._base.serializeInjectTouchControlMessage(message);
195
+ }
92
196
 
93
- serializeBackOrScreenOnControlMessage(
197
+ public serializeBackOrScreenOnControlMessage(
94
198
  message: ScrcpyBackOrScreenOnControlMessage
95
- ): Uint8Array | undefined;
199
+ ): Uint8Array | undefined {
200
+ return this._base.serializeBackOrScreenOnControlMessage(message);
201
+ }
96
202
 
97
- serializeSetClipboardControlMessage(
203
+ public serializeSetClipboardControlMessage(
98
204
  message: ScrcpySetClipboardControlMessage
99
- ): Uint8Array;
205
+ ): Uint8Array {
206
+ return this._base.serializeSetClipboardControlMessage(message);
207
+ }
100
208
 
101
- getScrollController(): ScrcpyScrollController;
209
+ public createScrollController(): ScrcpyScrollController {
210
+ return this._base.createScrollController();
211
+ }
102
212
  }