@yume-chan/scrcpy 0.0.13 → 0.0.16

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 (80) hide show
  1. package/CHANGELOG.json +45 -0
  2. package/CHANGELOG.md +25 -1
  3. package/LICENSE +21 -21
  4. package/README.md +90 -89
  5. package/bin/scrcpy-server +0 -0
  6. package/bin/version.d.ts +1 -1
  7. package/bin/version.js +1 -1
  8. package/esm/client.d.ts +14 -2
  9. package/esm/client.d.ts.map +1 -1
  10. package/esm/client.js +275 -143
  11. package/esm/client.js.map +1 -1
  12. package/esm/connection.d.ts.map +1 -1
  13. package/esm/connection.js +74 -86
  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.js +26 -21
  20. package/esm/decoder/web-codecs/index.js.map +1 -1
  21. package/esm/message.d.ts +13 -8
  22. package/esm/message.d.ts.map +1 -1
  23. package/esm/message.js +14 -8
  24. package/esm/message.js.map +1 -1
  25. package/esm/options/1_16/index.d.ts +8 -4
  26. package/esm/options/1_16/index.d.ts.map +1 -1
  27. package/esm/options/1_16/index.js +44 -18
  28. package/esm/options/1_16/index.js.map +1 -1
  29. package/esm/options/1_16/sps.js +3 -2
  30. package/esm/options/1_16/sps.js.map +1 -1
  31. package/esm/options/1_18.d.ts +3 -2
  32. package/esm/options/1_18.d.ts.map +1 -1
  33. package/esm/options/1_18.js +24 -1
  34. package/esm/options/1_18.js.map +1 -1
  35. package/esm/options/1_21.js +4 -1
  36. package/esm/options/1_21.js.map +1 -1
  37. package/esm/options/1_22.d.ts +3 -3
  38. package/esm/options/1_22.d.ts.map +1 -1
  39. package/esm/options/1_22.js +9 -6
  40. package/esm/options/1_22.js.map +1 -1
  41. package/esm/options/1_23.js +4 -1
  42. package/esm/options/1_23.js.map +1 -1
  43. package/esm/options/1_24.d.ts +9 -0
  44. package/esm/options/1_24.d.ts.map +1 -0
  45. package/esm/options/1_24.js +13 -0
  46. package/esm/options/1_24.js.map +1 -0
  47. package/esm/options/common.d.ts +2 -0
  48. package/esm/options/common.d.ts.map +1 -1
  49. package/esm/options/common.js.map +1 -1
  50. package/esm/options/index.d.ts +1 -0
  51. package/esm/options/index.d.ts.map +1 -1
  52. package/esm/options/index.js +1 -0
  53. package/esm/options/index.js.map +1 -1
  54. package/esm/push-server.js +5 -10
  55. package/esm/push-server.js.map +1 -1
  56. package/package.json +8 -8
  57. package/scrcpy.LICENSE +203 -203
  58. package/scripts/fetch-server.cjs +28 -28
  59. package/src/client.ts +435 -275
  60. package/src/codec.ts +35 -35
  61. package/src/connection.ts +148 -145
  62. package/src/decoder/index.ts +3 -3
  63. package/src/decoder/tinyh264/index.ts +112 -112
  64. package/src/decoder/tinyh264/types.d.ts +137 -137
  65. package/src/decoder/tinyh264/worker.ts +2 -2
  66. package/src/decoder/tinyh264/wrapper.ts +89 -89
  67. package/src/decoder/types.ts +35 -35
  68. package/src/decoder/web-codecs/index.ts +99 -99
  69. package/src/index.ts +8 -8
  70. package/src/message.ts +113 -105
  71. package/src/options/1_16/index.ts +345 -315
  72. package/src/options/1_16/sps.ts +300 -300
  73. package/src/options/1_18.ts +61 -41
  74. package/src/options/1_21.ts +34 -34
  75. package/src/options/1_22.ts +78 -80
  76. package/src/options/1_24.ts +18 -0
  77. package/src/options/common.ts +66 -63
  78. package/src/options/index.ts +7 -6
  79. package/src/push-server.ts +24 -24
  80. package/src/utils.ts +5 -5
@@ -1,315 +1,345 @@
1
- import { StructDeserializeStream, TransformStream, type Adb } from "@yume-chan/adb";
2
- import Struct, { placeholder } from "@yume-chan/struct";
3
- import type { AndroidCodecLevel, AndroidCodecProfile } from "../../codec.js";
4
- import { ScrcpyClientConnection, ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnectionOptions } from "../../connection.js";
5
- import { AndroidKeyEventAction, ScrcpyControlMessageType } from "../../message.js";
6
- import type { ScrcpyBackOrScreenOnEvent1_18 } from "../1_18.js";
7
- import type { ScrcpyInjectScrollControlMessage1_22 } from "../1_22.js";
8
- import { toScrcpyOptionValue, type ScrcpyOptions, type ScrcpyOptionValue, type VideoStreamPacket } from "../common.js";
9
- import { parse_sequence_parameter_set } from "./sps.js";
10
-
11
- export enum ScrcpyLogLevel {
12
- Verbose = 'verbose',
13
- Debug = 'debug',
14
- Info = 'info',
15
- Warn = 'warn',
16
- Error = 'error',
17
- }
18
-
19
- export enum ScrcpyScreenOrientation {
20
- Initial = -2,
21
- Unlocked = -1,
22
- Portrait = 0,
23
- Landscape = 1,
24
- PortraitFlipped = 2,
25
- LandscapeFlipped = 3,
26
- }
27
-
28
- export interface CodecOptionsInit {
29
- profile: AndroidCodecProfile;
30
-
31
- level: AndroidCodecLevel;
32
- }
33
-
34
- export class CodecOptions implements ScrcpyOptionValue {
35
- public value: Partial<CodecOptionsInit>;
36
-
37
- public constructor(value: Partial<CodecOptionsInit>) {
38
- this.value = value;
39
- }
40
-
41
- public toOptionValue(): string | undefined {
42
- const entries = Object.entries(this.value)
43
- .filter(([key, value]) => value !== undefined);
44
-
45
- if (entries.length === 0) {
46
- return undefined;
47
- }
48
-
49
- return entries
50
- .map(([key, value]) => `${key}=${value}`)
51
- .join(',');
52
- }
53
- }
54
-
55
- export interface ScrcpyOptionsInit1_16 {
56
- logLevel: ScrcpyLogLevel;
57
-
58
- /**
59
- * The maximum value of both width and height.
60
- */
61
- maxSize: number;
62
-
63
- bitRate: number;
64
-
65
- /**
66
- * 0 for unlimited.
67
- *
68
- * @default 0
69
- */
70
- maxFps: number;
71
-
72
- /**
73
- * The orientation of the video stream.
74
- *
75
- * It will not keep the device screen in specific orientation,
76
- * only the captured video will in this orientation.
77
- */
78
- lockVideoOrientation: ScrcpyScreenOrientation;
79
-
80
- tunnelForward: boolean;
81
-
82
- crop: string;
83
-
84
- /**
85
- * Send PTS so that the client may record properly
86
- *
87
- * Note: When `sendFrameMeta: false` is specified,
88
- * `onChangeEncoding` event won't fire and `onVideoData` event doesn't
89
- * merge sps/pps frame and first video frame. Which means you can't use
90
- * the shipped decoders to render the video
91
- * (You can still record the stream into a file).
92
- *
93
- * @default true
94
- */
95
- sendFrameMeta: boolean;
96
-
97
- /**
98
- * @default true
99
- */
100
- control: boolean;
101
-
102
- displayId: number;
103
-
104
- showTouches: boolean;
105
-
106
- stayAwake: boolean;
107
-
108
- codecOptions: CodecOptions;
109
-
110
- encoderName: string;
111
- }
112
-
113
- export const VideoPacket =
114
- new Struct()
115
- .uint64('pts')
116
- .uint32('size')
117
- .uint8Array('data', { lengthField: 'size' });
118
-
119
- export const NO_PTS = BigInt(1) << BigInt(63);
120
-
121
- export const ScrcpyBackOrScreenOnEvent1_16 =
122
- new Struct()
123
- .uint8('type', placeholder<ScrcpyControlMessageType.BackOrScreenOn>());
124
-
125
- export const ScrcpyInjectScrollControlMessage1_16 =
126
- new Struct()
127
- .uint8('type', ScrcpyControlMessageType.InjectScroll as const)
128
- .uint32('pointerX')
129
- .uint32('pointerY')
130
- .uint16('screenWidth')
131
- .uint16('screenHeight')
132
- .int32('scrollX')
133
- .int32('scrollY');
134
-
135
- export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16> implements ScrcpyOptions<T> {
136
- public value: Partial<T>;
137
-
138
- public constructor(value: Partial<ScrcpyOptionsInit1_16>) {
139
- if (new.target === ScrcpyOptions1_16 &&
140
- value.logLevel === ScrcpyLogLevel.Verbose) {
141
- value.logLevel = ScrcpyLogLevel.Debug;
142
- }
143
-
144
- if (new.target === ScrcpyOptions1_16 &&
145
- value.lockVideoOrientation === ScrcpyScreenOrientation.Initial) {
146
- value.lockVideoOrientation = ScrcpyScreenOrientation.Unlocked;
147
- }
148
-
149
- this.value = value as Partial<T>;
150
- }
151
-
152
- protected getArgumentOrder(): (keyof T)[] {
153
- return [
154
- 'logLevel',
155
- 'maxSize',
156
- 'bitRate',
157
- 'maxFps',
158
- 'lockVideoOrientation',
159
- 'tunnelForward',
160
- 'crop',
161
- 'sendFrameMeta',
162
- 'control',
163
- 'displayId',
164
- 'showTouches',
165
- 'stayAwake',
166
- 'codecOptions',
167
- 'encoderName',
168
- ];
169
- }
170
-
171
- protected getDefaultValue(): T {
172
- return {
173
- logLevel: ScrcpyLogLevel.Debug,
174
- maxSize: 0,
175
- bitRate: 8_000_000,
176
- maxFps: 0,
177
- lockVideoOrientation: ScrcpyScreenOrientation.Unlocked,
178
- tunnelForward: false,
179
- crop: '-',
180
- sendFrameMeta: true,
181
- control: true,
182
- displayId: 0,
183
- showTouches: false,
184
- stayAwake: false,
185
- codecOptions: new CodecOptions({}),
186
- encoderName: '-',
187
- } as T;
188
- }
189
-
190
- public formatServerArguments(): string[] {
191
- const defaults = this.getDefaultValue();
192
- return this.getArgumentOrder()
193
- .map(key => toScrcpyOptionValue(this.value[key] || defaults[key], '-'));
194
- }
195
-
196
- public createConnection(adb: Adb): ScrcpyClientConnection {
197
- const options: ScrcpyClientConnectionOptions = {
198
- // Old scrcpy connection always have control stream no matter what the option is
199
- control: true,
200
- sendDummyByte: true,
201
- sendDeviceMeta: true,
202
- };
203
- if (this.value.tunnelForward) {
204
- return new ScrcpyClientForwardConnection(adb, options);
205
- } else {
206
- return new ScrcpyClientReverseConnection(adb, options);
207
- }
208
- }
209
-
210
- public getOutputEncoderNameRegex(): RegExp {
211
- return /\s+scrcpy --encoder-name '(.*?)'/;
212
- }
213
-
214
- public createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket> {
215
- // Optimized path for video frames only
216
- if (this.value.sendFrameMeta === false) {
217
- return new TransformStream({
218
- transform(chunk, controller) {
219
- controller.enqueue({
220
- type: 'frame',
221
- data: chunk,
222
- });
223
- },
224
- });
225
- }
226
-
227
- let header: Uint8Array | undefined;
228
-
229
- let deserializeStream = new StructDeserializeStream(VideoPacket);
230
- return {
231
- writable: deserializeStream.writable,
232
- readable: deserializeStream.readable.pipeThrough(new TransformStream({
233
- transform(packet, controller) {
234
- if (packet.pts === NO_PTS) {
235
- const sequenceParameterSet = parse_sequence_parameter_set(packet.data.slice().buffer);
236
-
237
- const {
238
- profile_idc: profileIndex,
239
- constraint_set: constraintSet,
240
- level_idc: levelIndex,
241
- pic_width_in_mbs_minus1,
242
- pic_height_in_map_units_minus1,
243
- frame_mbs_only_flag,
244
- frame_crop_left_offset,
245
- frame_crop_right_offset,
246
- frame_crop_top_offset,
247
- frame_crop_bottom_offset,
248
- } = sequenceParameterSet;
249
-
250
- const encodedWidth = (pic_width_in_mbs_minus1 + 1) * 16;
251
- const encodedHeight = (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag) * 16;
252
- const cropLeft = frame_crop_left_offset * 2;
253
- const cropRight = frame_crop_right_offset * 2;
254
- const cropTop = frame_crop_top_offset * 2;
255
- const cropBottom = frame_crop_bottom_offset * 2;
256
-
257
- const croppedWidth = encodedWidth - cropLeft - cropRight;
258
- const croppedHeight = encodedHeight - cropTop - cropBottom;
259
-
260
- header = packet.data;
261
- controller.enqueue({
262
- type: 'configuration',
263
- data: {
264
- profileIndex,
265
- constraintSet,
266
- levelIndex,
267
- encodedWidth,
268
- encodedHeight,
269
- cropLeft,
270
- cropRight,
271
- cropTop,
272
- cropBottom,
273
- croppedWidth,
274
- croppedHeight,
275
- }
276
- });
277
- return;
278
- }
279
-
280
- let frameData: Uint8Array;
281
- if (header) {
282
- frameData = new Uint8Array(header.byteLength + packet.data.byteLength);
283
- frameData.set(header);
284
- frameData.set(packet.data, header.byteLength);
285
- header = undefined;
286
- } else {
287
- frameData = packet.data;
288
- }
289
-
290
- controller.enqueue({
291
- type: 'frame',
292
- pts: packet.pts,
293
- data: frameData,
294
- });
295
- }
296
- }))
297
- };
298
- }
299
-
300
- public serializeBackOrScreenOnControlMessage(
301
- message: ScrcpyBackOrScreenOnEvent1_18,
302
- ) {
303
- if (message.action === AndroidKeyEventAction.Down) {
304
- return ScrcpyBackOrScreenOnEvent1_16.serialize(message);
305
- }
306
-
307
- return undefined;
308
- }
309
-
310
- public serializeInjectScrollControlMessage(
311
- message: ScrcpyInjectScrollControlMessage1_22,
312
- ): Uint8Array {
313
- return ScrcpyInjectScrollControlMessage1_16.serialize(message);
314
- }
315
- }
1
+ import { StructDeserializeStream, TransformStream, type Adb } from "@yume-chan/adb";
2
+ import Struct from "@yume-chan/struct";
3
+ import type { AndroidCodecLevel, AndroidCodecProfile } from "../../codec.js";
4
+ import { ScrcpyClientConnection, ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnectionOptions } from "../../connection.js";
5
+ import { AndroidKeyEventAction, ScrcpyControlMessageType, ScrcpySimpleControlMessage } from "../../message.js";
6
+ import type { ScrcpyBackOrScreenOnEvent1_18 } from "../1_18.js";
7
+ import type { ScrcpyInjectScrollControlMessage1_22 } from "../1_22.js";
8
+ import { toScrcpyOptionValue, type ScrcpyOptions, type ScrcpyOptionValue, type VideoStreamPacket } from "../common.js";
9
+ import { parse_sequence_parameter_set } from "./sps.js";
10
+
11
+ export enum ScrcpyLogLevel {
12
+ Verbose = 'verbose',
13
+ Debug = 'debug',
14
+ Info = 'info',
15
+ Warn = 'warn',
16
+ Error = 'error',
17
+ }
18
+
19
+ export enum ScrcpyVideoOrientation {
20
+ Initial = -2,
21
+ Unlocked = -1,
22
+ Portrait = 0,
23
+ Landscape = 1,
24
+ PortraitFlipped = 2,
25
+ LandscapeFlipped = 3,
26
+ }
27
+
28
+ export interface CodecOptionsInit {
29
+ profile: AndroidCodecProfile;
30
+ level: AndroidCodecLevel;
31
+
32
+ iFrameInterval: number;
33
+ repeatPreviousFrameAfter: number;
34
+ maxPtsGapToEncoder: number;
35
+ }
36
+
37
+ function toDashCase(input: string) {
38
+ return input.replace(/([A-Z])/g, '-$1').toLowerCase();
39
+ }
40
+
41
+ const CODEC_OPTION_TYPES: Partial<Record<keyof CodecOptionsInit, 'long' | 'float' | 'string'>> = {
42
+ repeatPreviousFrameAfter: 'long',
43
+ maxPtsGapToEncoder: 'long',
44
+ };
45
+
46
+ export class CodecOptions implements ScrcpyOptionValue {
47
+ public value: Partial<CodecOptionsInit>;
48
+
49
+ public constructor(value: Partial<CodecOptionsInit>) {
50
+ this.value = value;
51
+ }
52
+
53
+ public toOptionValue(): string | undefined {
54
+ const entries = Object.entries(this.value)
55
+ .filter(([key, value]) => value !== undefined);
56
+
57
+ if (entries.length === 0) {
58
+ return undefined;
59
+ }
60
+
61
+ return entries
62
+ .map(([key, value]) => {
63
+ const type = CODEC_OPTION_TYPES[key as keyof CodecOptionsInit];
64
+ return `${toDashCase(key)}${type ? `:${type}` : ''}=${value}`;
65
+ })
66
+ .join(',');
67
+ }
68
+ }
69
+
70
+ export interface ScrcpyOptionsInit1_16 {
71
+ logLevel: ScrcpyLogLevel;
72
+
73
+ /**
74
+ * The maximum value of both width and height.
75
+ */
76
+ maxSize: number;
77
+
78
+ bitRate: number;
79
+
80
+ /**
81
+ * 0 for unlimited.
82
+ *
83
+ * @default 0
84
+ */
85
+ maxFps: number;
86
+
87
+ /**
88
+ * The orientation of the video stream.
89
+ *
90
+ * It will not keep the device screen in specific orientation,
91
+ * only the captured video will in this orientation.
92
+ */
93
+ lockVideoOrientation: ScrcpyVideoOrientation;
94
+
95
+ tunnelForward: boolean;
96
+
97
+ crop: string;
98
+
99
+ /**
100
+ * Send PTS so that the client may record properly
101
+ *
102
+ * Note: When `sendFrameMeta: false` is specified,
103
+ * `onChangeEncoding` event won't fire and `onVideoData` event doesn't
104
+ * merge sps/pps frame and first video frame. Which means you can't use
105
+ * the shipped decoders to render the video
106
+ * (You can still record the stream into a file).
107
+ *
108
+ * @default true
109
+ */
110
+ sendFrameMeta: boolean;
111
+
112
+ /**
113
+ * @default true
114
+ */
115
+ control: boolean;
116
+
117
+ displayId: number;
118
+
119
+ showTouches: boolean;
120
+
121
+ stayAwake: boolean;
122
+
123
+ codecOptions: CodecOptions;
124
+
125
+ encoderName: string;
126
+ }
127
+
128
+ export const VideoPacket =
129
+ new Struct()
130
+ .uint64('pts')
131
+ .uint32('size')
132
+ .uint8Array('data', { lengthField: 'size' });
133
+
134
+ export const NO_PTS = BigInt(1) << BigInt(63);
135
+
136
+ export const ScrcpyBackOrScreenOnEvent1_16 =
137
+ ScrcpySimpleControlMessage;
138
+
139
+ export const ScrcpyInjectScrollControlMessage1_16 =
140
+ new Struct()
141
+ .fields(ScrcpySimpleControlMessage)
142
+ .uint32('pointerX')
143
+ .uint32('pointerY')
144
+ .uint16('screenWidth')
145
+ .uint16('screenHeight')
146
+ .int32('scrollX')
147
+ .int32('scrollY');
148
+
149
+ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16> implements ScrcpyOptions<T> {
150
+ public value: Partial<T>;
151
+
152
+ public constructor(value: Partial<ScrcpyOptionsInit1_16>) {
153
+ if (new.target === ScrcpyOptions1_16 &&
154
+ value.logLevel === ScrcpyLogLevel.Verbose) {
155
+ value.logLevel = ScrcpyLogLevel.Debug;
156
+ }
157
+
158
+ if (new.target === ScrcpyOptions1_16 &&
159
+ value.lockVideoOrientation === ScrcpyVideoOrientation.Initial) {
160
+ value.lockVideoOrientation = ScrcpyVideoOrientation.Unlocked;
161
+ }
162
+
163
+ this.value = value as Partial<T>;
164
+ }
165
+
166
+ protected getArgumentOrder(): (keyof T)[] {
167
+ return [
168
+ 'logLevel',
169
+ 'maxSize',
170
+ 'bitRate',
171
+ 'maxFps',
172
+ 'lockVideoOrientation',
173
+ 'tunnelForward',
174
+ 'crop',
175
+ 'sendFrameMeta',
176
+ 'control',
177
+ 'displayId',
178
+ 'showTouches',
179
+ 'stayAwake',
180
+ 'codecOptions',
181
+ 'encoderName',
182
+ ];
183
+ }
184
+
185
+ protected getDefaultValue(): T {
186
+ return {
187
+ logLevel: ScrcpyLogLevel.Debug,
188
+ maxSize: 0,
189
+ bitRate: 8_000_000,
190
+ maxFps: 0,
191
+ lockVideoOrientation: ScrcpyVideoOrientation.Unlocked,
192
+ tunnelForward: false,
193
+ crop: '-',
194
+ sendFrameMeta: true,
195
+ control: true,
196
+ displayId: 0,
197
+ showTouches: false,
198
+ stayAwake: false,
199
+ codecOptions: new CodecOptions({}),
200
+ encoderName: '-',
201
+ } as T;
202
+ }
203
+
204
+ public formatServerArguments(): string[] {
205
+ const defaults = this.getDefaultValue();
206
+ return this.getArgumentOrder()
207
+ .map(key => toScrcpyOptionValue(this.value[key] || defaults[key], '-'));
208
+ }
209
+
210
+ public createConnection(adb: Adb): ScrcpyClientConnection {
211
+ const options: ScrcpyClientConnectionOptions = {
212
+ // Old scrcpy connection always have control stream no matter what the option is
213
+ control: true,
214
+ sendDummyByte: true,
215
+ sendDeviceMeta: true,
216
+ };
217
+ if (this.value.tunnelForward) {
218
+ return new ScrcpyClientForwardConnection(adb, options);
219
+ } else {
220
+ return new ScrcpyClientReverseConnection(adb, options);
221
+ }
222
+ }
223
+
224
+ public getOutputEncoderNameRegex(): RegExp {
225
+ return /\s+scrcpy --encoder-name '(.*?)'/;
226
+ }
227
+
228
+ public createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket> {
229
+ // Optimized path for video frames only
230
+ if (this.value.sendFrameMeta === false) {
231
+ return new TransformStream({
232
+ transform(chunk, controller) {
233
+ controller.enqueue({
234
+ type: 'frame',
235
+ data: chunk,
236
+ });
237
+ },
238
+ });
239
+ }
240
+
241
+ let header: Uint8Array | undefined;
242
+
243
+ let deserializeStream = new StructDeserializeStream(VideoPacket);
244
+ return {
245
+ writable: deserializeStream.writable,
246
+ readable: deserializeStream.readable.pipeThrough(new TransformStream({
247
+ transform(packet, controller) {
248
+ if (packet.pts === NO_PTS) {
249
+ const sequenceParameterSet = parse_sequence_parameter_set(packet.data.slice().buffer);
250
+
251
+ const {
252
+ profile_idc: profileIndex,
253
+ constraint_set: constraintSet,
254
+ level_idc: levelIndex,
255
+ pic_width_in_mbs_minus1,
256
+ pic_height_in_map_units_minus1,
257
+ frame_mbs_only_flag,
258
+ frame_crop_left_offset,
259
+ frame_crop_right_offset,
260
+ frame_crop_top_offset,
261
+ frame_crop_bottom_offset,
262
+ } = sequenceParameterSet;
263
+
264
+ const encodedWidth = (pic_width_in_mbs_minus1 + 1) * 16;
265
+ const encodedHeight = (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag) * 16;
266
+ const cropLeft = frame_crop_left_offset * 2;
267
+ const cropRight = frame_crop_right_offset * 2;
268
+ const cropTop = frame_crop_top_offset * 2;
269
+ const cropBottom = frame_crop_bottom_offset * 2;
270
+
271
+ const croppedWidth = encodedWidth - cropLeft - cropRight;
272
+ const croppedHeight = encodedHeight - cropTop - cropBottom;
273
+
274
+ header = packet.data;
275
+ controller.enqueue({
276
+ type: 'configuration',
277
+ data: {
278
+ profileIndex,
279
+ constraintSet,
280
+ levelIndex,
281
+ encodedWidth,
282
+ encodedHeight,
283
+ cropLeft,
284
+ cropRight,
285
+ cropTop,
286
+ cropBottom,
287
+ croppedWidth,
288
+ croppedHeight,
289
+ }
290
+ });
291
+ return;
292
+ }
293
+
294
+ let frameData: Uint8Array;
295
+ if (header) {
296
+ frameData = new Uint8Array(header.byteLength + packet.data.byteLength);
297
+ frameData.set(header);
298
+ frameData.set(packet.data, header.byteLength);
299
+ header = undefined;
300
+ } else {
301
+ frameData = packet.data;
302
+ }
303
+
304
+ controller.enqueue({
305
+ type: 'frame',
306
+ pts: packet.pts,
307
+ data: frameData,
308
+ });
309
+ }
310
+ }))
311
+ };
312
+ }
313
+
314
+ public getControlMessageTypes(): ScrcpyControlMessageType[] {
315
+ return [
316
+ /* 0 */ ScrcpyControlMessageType.InjectKeycode,
317
+ /* 1 */ ScrcpyControlMessageType.InjectText,
318
+ /* 2 */ ScrcpyControlMessageType.InjectTouch,
319
+ /* 3 */ ScrcpyControlMessageType.InjectScroll,
320
+ /* 4 */ ScrcpyControlMessageType.BackOrScreenOn,
321
+ /* 5 */ ScrcpyControlMessageType.ExpandNotificationPanel,
322
+ /* 6 */ ScrcpyControlMessageType.CollapseNotificationPanel,
323
+ /* 7 */ ScrcpyControlMessageType.GetClipboard,
324
+ /* 8 */ ScrcpyControlMessageType.SetClipboard,
325
+ /* 9 */ ScrcpyControlMessageType.SetScreenPowerMode,
326
+ /* 10 */ ScrcpyControlMessageType.RotateDevice,
327
+ ];
328
+ }
329
+
330
+ public serializeBackOrScreenOnControlMessage(
331
+ message: ScrcpyBackOrScreenOnEvent1_18,
332
+ ) {
333
+ if (message.action === AndroidKeyEventAction.Down) {
334
+ return ScrcpyBackOrScreenOnEvent1_16.serialize(message);
335
+ }
336
+
337
+ return undefined;
338
+ }
339
+
340
+ public serializeInjectScrollControlMessage(
341
+ message: ScrcpyInjectScrollControlMessage1_22,
342
+ ): Uint8Array {
343
+ return ScrcpyInjectScrollControlMessage1_16.serialize(message);
344
+ }
345
+ }