@yume-chan/scrcpy 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +43 -0
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/bin/scrcpy-server +0 -0
- package/bin/version.d.ts +1 -0
- package/bin/version.js +1 -0
- package/esm/client.d.ts +36 -0
- package/esm/client.d.ts.map +1 -0
- package/esm/client.js +209 -0
- package/esm/client.js.map +1 -0
- package/esm/codec.d.ts +34 -0
- package/esm/codec.d.ts.map +1 -0
- package/esm/codec.js +37 -0
- package/esm/codec.js.map +1 -0
- package/esm/connection.d.ts +37 -0
- package/esm/connection.d.ts.map +1 -0
- package/esm/connection.js +103 -0
- package/esm/connection.js.map +1 -0
- package/esm/decoder/index.d.ts +4 -0
- package/esm/decoder/index.d.ts.map +1 -0
- package/esm/decoder/index.js +4 -0
- package/esm/decoder/index.js.map +1 -0
- package/esm/decoder/tinyh264/index.d.ts +20 -0
- package/esm/decoder/tinyh264/index.d.ts.map +1 -0
- package/esm/decoder/tinyh264/index.js +86 -0
- package/esm/decoder/tinyh264/index.js.map +1 -0
- package/esm/decoder/tinyh264/worker.d.ts +2 -0
- package/esm/decoder/tinyh264/worker.d.ts.map +1 -0
- package/esm/decoder/tinyh264/worker.js +3 -0
- package/esm/decoder/tinyh264/worker.js.map +1 -0
- package/esm/decoder/tinyh264/wrapper.d.ts +21 -0
- package/esm/decoder/tinyh264/wrapper.d.ts.map +1 -0
- package/esm/decoder/tinyh264/wrapper.js +67 -0
- package/esm/decoder/tinyh264/wrapper.js.map +1 -0
- package/esm/decoder/types.d.ts +28 -0
- package/esm/decoder/types.d.ts.map +1 -0
- package/esm/decoder/types.js +2 -0
- package/esm/decoder/types.js.map +1 -0
- package/esm/decoder/web-codecs/index.d.ts +22 -0
- package/esm/decoder/web-codecs/index.d.ts.map +1 -0
- package/esm/decoder/web-codecs/index.js +75 -0
- package/esm/decoder/web-codecs/index.js.map +1 -0
- package/esm/index.d.ts +9 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +9 -0
- package/esm/index.js.map +1 -0
- package/esm/message.d.ts +93 -0
- package/esm/message.d.ts.map +1 -0
- package/esm/message.js +92 -0
- package/esm/message.js.map +1 -0
- package/esm/options/1_16/index.d.ts +107 -0
- package/esm/options/1_16/index.d.ts.map +1 -0
- package/esm/options/1_16/index.js +211 -0
- package/esm/options/1_16/index.js.map +1 -0
- package/esm/options/1_16/sps.d.ts +22 -0
- package/esm/options/1_16/sps.d.ts.map +1 -0
- package/esm/options/1_16/sps.js +254 -0
- package/esm/options/1_16/sps.js.map +1 -0
- package/esm/options/1_18.d.ts +19 -0
- package/esm/options/1_18.d.ts.map +1 -0
- package/esm/options/1_18.js +23 -0
- package/esm/options/1_18.js.map +1 -0
- package/esm/options/1_21.d.ts +10 -0
- package/esm/options/1_21.d.ts.map +1 -0
- package/esm/options/1_21.js +23 -0
- package/esm/options/1_21.js.map +1 -0
- package/esm/options/1_22.d.ts +43 -0
- package/esm/options/1_22.d.ts.map +1 -0
- package/esm/options/1_22.js +40 -0
- package/esm/options/1_22.js.map +1 -0
- package/esm/options/common.d.ts +30 -0
- package/esm/options/common.d.ts.map +1 -0
- package/esm/options/common.js +16 -0
- package/esm/options/common.js.map +1 -0
- package/esm/options/index.d.ts +6 -0
- package/esm/options/index.d.ts.map +1 -0
- package/esm/options/index.js +6 -0
- package/esm/options/index.js.map +1 -0
- package/esm/push-server.d.ts +6 -0
- package/esm/push-server.d.ts.map +1 -0
- package/esm/push-server.js +23 -0
- package/esm/push-server.js.map +1 -0
- package/esm/utils.d.ts +4 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +14 -0
- package/esm/utils.js.map +1 -0
- package/package.json +61 -0
- package/scrcpy.LICENSE +203 -0
- package/scripts/fetch-server.cjs +28 -0
- package/src/client.ts +275 -0
- package/src/codec.ts +35 -0
- package/src/connection.ts +123 -0
- package/src/decoder/index.ts +3 -0
- package/src/decoder/tinyh264/index.ts +112 -0
- package/src/decoder/tinyh264/types.d.ts +137 -0
- package/src/decoder/tinyh264/worker.ts +2 -0
- package/src/decoder/tinyh264/wrapper.ts +89 -0
- package/src/decoder/types.ts +35 -0
- package/src/decoder/web-codecs/index.ts +98 -0
- package/src/index.ts +8 -0
- package/src/message.ts +105 -0
- package/src/options/1_16/index.ts +322 -0
- package/src/options/1_16/sps.ts +300 -0
- package/src/options/1_18.ts +41 -0
- package/src/options/1_21.ts +34 -0
- package/src/options/1_22.ts +80 -0
- package/src/options/common.ts +61 -0
- package/src/options/index.ts +5 -0
- package/src/push-server.ts +26 -0
- package/src/utils.ts +16 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// cspell: ignore golomb
|
|
2
|
+
// cspell: ignore qpprime
|
|
3
|
+
|
|
4
|
+
class BitReader {
|
|
5
|
+
private buffer: Uint8Array;
|
|
6
|
+
|
|
7
|
+
private bytePosition = 0;
|
|
8
|
+
|
|
9
|
+
private bitPosition = 0;
|
|
10
|
+
|
|
11
|
+
public constructor(buffer: Uint8Array) {
|
|
12
|
+
this.buffer = buffer;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public read(length: number): number {
|
|
16
|
+
let result = 0;
|
|
17
|
+
for (let i = 0; i < length; i += 1) {
|
|
18
|
+
result = (result << 1) | this.next();
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public next(): number {
|
|
24
|
+
const value = (this.buffer[this.bytePosition]! >> (7 - this.bitPosition)) & 1;
|
|
25
|
+
this.bitPosition += 1;
|
|
26
|
+
if (this.bitPosition === 8) {
|
|
27
|
+
this.bytePosition += 1;
|
|
28
|
+
this.bitPosition = 0;
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public decodeExponentialGolombNumber(): number {
|
|
34
|
+
let length = 0;
|
|
35
|
+
while (this.next() === 0) {
|
|
36
|
+
length += 1;
|
|
37
|
+
}
|
|
38
|
+
if (length === 0) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
return (1 << length | this.read(length)) - 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> {
|
|
46
|
+
// -1 means we haven't found the first start code
|
|
47
|
+
let start = -1;
|
|
48
|
+
let writeIndex = 0;
|
|
49
|
+
|
|
50
|
+
// How many `0x00`s in a row we have counted
|
|
51
|
+
let zeroCount = 0;
|
|
52
|
+
|
|
53
|
+
let inEmulation = false;
|
|
54
|
+
|
|
55
|
+
for (const byte of buffer) {
|
|
56
|
+
buffer[writeIndex] = byte;
|
|
57
|
+
writeIndex += 1;
|
|
58
|
+
|
|
59
|
+
if (inEmulation) {
|
|
60
|
+
if (byte > 0x03) {
|
|
61
|
+
// `0x00000304` or larger are invalid
|
|
62
|
+
throw new Error('Invalid data');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
inEmulation = false;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (byte == 0x00) {
|
|
70
|
+
zeroCount += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const lastZeroCount = zeroCount;
|
|
75
|
+
zeroCount = 0;
|
|
76
|
+
|
|
77
|
+
if (start === -1) {
|
|
78
|
+
// 0x000001 is the start code
|
|
79
|
+
// But it can be preceded by any number of zeros
|
|
80
|
+
// So 2 is the minimal
|
|
81
|
+
if (lastZeroCount >= 2 && byte === 0x01) {
|
|
82
|
+
// Found start of first NAL unit
|
|
83
|
+
writeIndex = 0;
|
|
84
|
+
start = 0;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Not begin with start code
|
|
89
|
+
throw new Error('Invalid data');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (lastZeroCount < 2) {
|
|
93
|
+
// zero or one `0x00`s are acceptable
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (byte === 0x01) {
|
|
98
|
+
// Remove all leading `0x00`s and this `0x01`
|
|
99
|
+
writeIndex -= lastZeroCount + 1;
|
|
100
|
+
|
|
101
|
+
// Found another NAL unit
|
|
102
|
+
yield buffer.subarray(start, writeIndex);
|
|
103
|
+
|
|
104
|
+
start = writeIndex;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (lastZeroCount > 2) {
|
|
109
|
+
// Too much `0x00`s
|
|
110
|
+
throw new Error('Invalid data');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
switch (byte) {
|
|
114
|
+
case 0x02:
|
|
115
|
+
// Didn't find why, but 7.4.1 NAL unit semantics forbids `0x000002` appearing in NAL units
|
|
116
|
+
throw new Error('Invalid data');
|
|
117
|
+
case 0x03:
|
|
118
|
+
// `0x000003` is the "emulation_prevention_three_byte"
|
|
119
|
+
// `0x00000300`, `0x00000301`, `0x00000302` and `0x00000303` represent
|
|
120
|
+
// `0x000000`, `0x000001`, `0x000002` and `0x000003` respectively
|
|
121
|
+
|
|
122
|
+
// Remove current byte
|
|
123
|
+
writeIndex -= 1;
|
|
124
|
+
|
|
125
|
+
inEmulation = true;
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
// `0x000004` or larger are ok
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (inEmulation || zeroCount !== 0) {
|
|
134
|
+
throw new Error('Invalid data');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
yield buffer.subarray(start, writeIndex);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 7.3.2.1.1 Sequence parameter set data syntax
|
|
141
|
+
export function parse_sequence_parameter_set(buffer: ArrayBuffer) {
|
|
142
|
+
for (const nalu of iterateNalu(new Uint8Array(buffer))) {
|
|
143
|
+
const reader = new BitReader(nalu);
|
|
144
|
+
if (reader.next() !== 0) {
|
|
145
|
+
throw new Error('Invalid data');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const nal_ref_idc = reader.read(2);
|
|
149
|
+
const nal_unit_type = reader.read(5);
|
|
150
|
+
|
|
151
|
+
if (nal_unit_type !== 7) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (nal_ref_idc === 0) {
|
|
156
|
+
throw new Error('Invalid data');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const profile_idc = reader.read(8);
|
|
160
|
+
const constraint_set = reader.read(8);
|
|
161
|
+
|
|
162
|
+
const constraint_set_reader = new BitReader(new Uint8Array([constraint_set]));
|
|
163
|
+
const constraint_set0_flag = !!constraint_set_reader.next();
|
|
164
|
+
const constraint_set1_flag = !!constraint_set_reader.next();
|
|
165
|
+
const constraint_set2_flag = !!constraint_set_reader.next();
|
|
166
|
+
const constraint_set3_flag = !!constraint_set_reader.next();
|
|
167
|
+
const constraint_set4_flag = !!constraint_set_reader.next();
|
|
168
|
+
const constraint_set5_flag = !!constraint_set_reader.next();
|
|
169
|
+
|
|
170
|
+
// reserved_zero_2bits
|
|
171
|
+
if (constraint_set_reader.read(2) !== 0) {
|
|
172
|
+
throw new Error('Invalid data');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const level_idc = reader.read(8);
|
|
176
|
+
const seq_parameter_set_id = reader.decodeExponentialGolombNumber();
|
|
177
|
+
|
|
178
|
+
if (profile_idc === 100 || profile_idc === 110 ||
|
|
179
|
+
profile_idc === 122 || profile_idc === 244 || profile_idc === 44 ||
|
|
180
|
+
profile_idc === 83 || profile_idc === 86 || profile_idc === 118 ||
|
|
181
|
+
profile_idc === 128 || profile_idc === 138 || profile_idc === 139 ||
|
|
182
|
+
profile_idc === 134) {
|
|
183
|
+
const chroma_format_idc = reader.decodeExponentialGolombNumber();
|
|
184
|
+
if (chroma_format_idc === 3) {
|
|
185
|
+
// separate_colour_plane_flag
|
|
186
|
+
reader.next();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// bit_depth_luma_minus8
|
|
190
|
+
reader.decodeExponentialGolombNumber();
|
|
191
|
+
// bit_depth_chroma_minus8
|
|
192
|
+
reader.decodeExponentialGolombNumber();
|
|
193
|
+
|
|
194
|
+
// qpprime_y_zero_transform_bypass_flag
|
|
195
|
+
reader.next();
|
|
196
|
+
|
|
197
|
+
const seq_scaling_matrix_present_flag = !!reader.next();
|
|
198
|
+
if (seq_scaling_matrix_present_flag) {
|
|
199
|
+
const seq_scaling_list_present_flag: boolean[] = [];
|
|
200
|
+
for (let i = 0; i < ((chroma_format_idc !== 3) ? 8 : 12); i++) {
|
|
201
|
+
seq_scaling_list_present_flag[i] = !!reader.next();
|
|
202
|
+
if (seq_scaling_list_present_flag[i])
|
|
203
|
+
if (i < 6) {
|
|
204
|
+
// TODO
|
|
205
|
+
// scaling_list( ScalingList4x4[ i ], 16,
|
|
206
|
+
// UseDefaultScalingMatrix4x4Flag[ i ])
|
|
207
|
+
} else {
|
|
208
|
+
// TODO
|
|
209
|
+
// scaling_list( ScalingList8x8[ i − 6 ], 64,
|
|
210
|
+
// UseDefaultScalingMatrix8x8Flag[ i − 6 ] )
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// log2_max_frame_num_minus4
|
|
217
|
+
reader.decodeExponentialGolombNumber();
|
|
218
|
+
const pic_order_cnt_type = reader.decodeExponentialGolombNumber();
|
|
219
|
+
if (pic_order_cnt_type === 0) {
|
|
220
|
+
// log2_max_pic_order_cnt_lsb_minus4
|
|
221
|
+
reader.decodeExponentialGolombNumber();
|
|
222
|
+
} else if (pic_order_cnt_type === 1) {
|
|
223
|
+
// delta_pic_order_always_zero_flag
|
|
224
|
+
reader.next();
|
|
225
|
+
// offset_for_non_ref_pic
|
|
226
|
+
reader.decodeExponentialGolombNumber();
|
|
227
|
+
// offset_for_top_to_bottom_field
|
|
228
|
+
reader.decodeExponentialGolombNumber();
|
|
229
|
+
const num_ref_frames_in_pic_order_cnt_cycle = reader.decodeExponentialGolombNumber();
|
|
230
|
+
const offset_for_ref_frame: number[] = [];
|
|
231
|
+
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
|
232
|
+
offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// max_num_ref_frames
|
|
237
|
+
reader.decodeExponentialGolombNumber();
|
|
238
|
+
// gaps_in_frame_num_value_allowed_flag
|
|
239
|
+
reader.next();
|
|
240
|
+
const pic_width_in_mbs_minus1 = reader.decodeExponentialGolombNumber();
|
|
241
|
+
const pic_height_in_map_units_minus1 = reader.decodeExponentialGolombNumber();
|
|
242
|
+
|
|
243
|
+
const frame_mbs_only_flag = reader.next();
|
|
244
|
+
if (!frame_mbs_only_flag) {
|
|
245
|
+
// mb_adaptive_frame_field_flag
|
|
246
|
+
reader.next();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// direct_8x8_inference_flag
|
|
250
|
+
reader.next();
|
|
251
|
+
|
|
252
|
+
const frame_cropping_flag = !!reader.next();
|
|
253
|
+
let frame_crop_left_offset: number;
|
|
254
|
+
let frame_crop_right_offset: number;
|
|
255
|
+
let frame_crop_top_offset: number;
|
|
256
|
+
let frame_crop_bottom_offset: number;
|
|
257
|
+
if (frame_cropping_flag) {
|
|
258
|
+
frame_crop_left_offset = reader.decodeExponentialGolombNumber();
|
|
259
|
+
frame_crop_right_offset = reader.decodeExponentialGolombNumber();
|
|
260
|
+
frame_crop_top_offset = reader.decodeExponentialGolombNumber();
|
|
261
|
+
frame_crop_bottom_offset = reader.decodeExponentialGolombNumber();
|
|
262
|
+
} else {
|
|
263
|
+
frame_crop_left_offset = 0;
|
|
264
|
+
frame_crop_right_offset = 0;
|
|
265
|
+
frame_crop_top_offset = 0;
|
|
266
|
+
frame_crop_bottom_offset = 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const vui_parameters_present_flag = !!reader.next();
|
|
270
|
+
if (vui_parameters_present_flag) {
|
|
271
|
+
// TODO
|
|
272
|
+
// vui_parameters( )
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
profile_idc,
|
|
277
|
+
constraint_set,
|
|
278
|
+
constraint_set0_flag,
|
|
279
|
+
constraint_set1_flag,
|
|
280
|
+
constraint_set2_flag,
|
|
281
|
+
constraint_set3_flag,
|
|
282
|
+
constraint_set4_flag,
|
|
283
|
+
constraint_set5_flag,
|
|
284
|
+
level_idc,
|
|
285
|
+
seq_parameter_set_id,
|
|
286
|
+
pic_width_in_mbs_minus1,
|
|
287
|
+
pic_height_in_map_units_minus1,
|
|
288
|
+
frame_mbs_only_flag,
|
|
289
|
+
frame_cropping_flag,
|
|
290
|
+
frame_crop_left_offset,
|
|
291
|
+
frame_crop_right_offset,
|
|
292
|
+
frame_crop_top_offset,
|
|
293
|
+
frame_crop_bottom_offset,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
throw new Error('Invalid data');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export type SequenceParameterSet = ReturnType<typeof parse_sequence_parameter_set>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Struct, { placeholder } from "@yume-chan/struct";
|
|
2
|
+
import type { AndroidKeyEventAction } from "../message.js";
|
|
3
|
+
import { ScrcpyBackOrScreenOnEvent1_16, ScrcpyOptions1_16, type ScrcpyOptionsInit1_16 } from "./1_16/index.js";
|
|
4
|
+
|
|
5
|
+
export interface ScrcpyOptionsInit1_18 extends ScrcpyOptionsInit1_16 {
|
|
6
|
+
powerOffOnClose: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ScrcpyBackOrScreenOnEvent1_18 =
|
|
10
|
+
new Struct()
|
|
11
|
+
.fields(ScrcpyBackOrScreenOnEvent1_16)
|
|
12
|
+
.uint8('action', placeholder<AndroidKeyEventAction>());
|
|
13
|
+
|
|
14
|
+
export type ScrcpyBackOrScreenOnEvent1_18 = typeof ScrcpyBackOrScreenOnEvent1_18["TInit"];
|
|
15
|
+
|
|
16
|
+
export class ScrcpyOptions1_18<T extends ScrcpyOptionsInit1_18 = ScrcpyOptionsInit1_18> extends ScrcpyOptions1_16<T> {
|
|
17
|
+
constructor(value: Partial<ScrcpyOptionsInit1_18>) {
|
|
18
|
+
super(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected override getArgumentOrder(): (keyof T)[] {
|
|
22
|
+
return super.getArgumentOrder().concat(['powerOffOnClose']);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected override getDefaultValue(): T {
|
|
26
|
+
return {
|
|
27
|
+
...super.getDefaultValue(),
|
|
28
|
+
powerOffOnClose: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public override getOutputEncoderNameRegex(): RegExp {
|
|
33
|
+
return /\s+scrcpy --encoder '(.*?)'/;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public override serializeBackOrScreenOnControlMessage(
|
|
37
|
+
message: ScrcpyBackOrScreenOnEvent1_18,
|
|
38
|
+
) {
|
|
39
|
+
return ScrcpyBackOrScreenOnEvent1_18.serialize(message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// cspell: ignore autosync
|
|
2
|
+
|
|
3
|
+
import { ScrcpyOptions1_18, type ScrcpyOptionsInit1_18 } from './1_18.js';
|
|
4
|
+
import { toScrcpyOptionValue } from "./common.js";
|
|
5
|
+
|
|
6
|
+
export interface ScrcpyOptionsInit1_21 extends ScrcpyOptionsInit1_18 {
|
|
7
|
+
clipboardAutosync?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function toSnakeCase(input: string): string {
|
|
11
|
+
return input.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ScrcpyOptions1_21<T extends ScrcpyOptionsInit1_21 = ScrcpyOptionsInit1_21> extends ScrcpyOptions1_18<T> {
|
|
15
|
+
public constructor(init: Partial<ScrcpyOptionsInit1_21>) {
|
|
16
|
+
super(init);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override getDefaultValue(): T {
|
|
20
|
+
return {
|
|
21
|
+
...super.getDefaultValue(),
|
|
22
|
+
clipboardAutosync: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public override formatServerArguments(): string[] {
|
|
27
|
+
// 1.21 changed the format of arguments
|
|
28
|
+
// So `getArgumentOrder()` is no longer needed
|
|
29
|
+
return Object.entries(this.value)
|
|
30
|
+
.map(([key, value]) => [key, toScrcpyOptionValue(value, undefined)] as const)
|
|
31
|
+
.filter((pair): pair is [string, string] => pair[1] !== undefined)
|
|
32
|
+
.map(([key, value]) => `${toSnakeCase(key)}=${value}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Adb } from "@yume-chan/adb";
|
|
2
|
+
import Struct from "@yume-chan/struct";
|
|
3
|
+
import { ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnection, type ScrcpyClientConnectionOptions } from "../connection.js";
|
|
4
|
+
import { ScrcpyInjectScrollControlMessage1_16 } from "./1_16/index.js";
|
|
5
|
+
import { ScrcpyOptions1_21, type ScrcpyOptionsInit1_21 } from "./1_21.js";
|
|
6
|
+
|
|
7
|
+
export interface ScrcpyOptionsInit1_22 extends ScrcpyOptionsInit1_21 {
|
|
8
|
+
downsizeOnError: boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Send device name and size
|
|
12
|
+
*
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
sendDeviceMeta: boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Write a byte on start to detect connection issues
|
|
19
|
+
*
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
sendDummyByte: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Implies `sendDeviceMeta: false`, `sendFrameMeta: false` and `sendDummyByte: false`
|
|
26
|
+
*
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
rawVideoStream: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const ScrcpyInjectScrollControlMessage1_22 =
|
|
33
|
+
new Struct()
|
|
34
|
+
.fields(ScrcpyInjectScrollControlMessage1_16)
|
|
35
|
+
.int32("buttons");
|
|
36
|
+
|
|
37
|
+
export type ScrcpyInjectScrollControlMessage1_22 = typeof ScrcpyInjectScrollControlMessage1_22["TInit"];
|
|
38
|
+
|
|
39
|
+
export class ScrcpyOptions1_22<T extends ScrcpyOptionsInit1_22 = ScrcpyOptionsInit1_22> extends ScrcpyOptions1_21<T> {
|
|
40
|
+
public constructor(init: Partial<ScrcpyOptionsInit1_22>) {
|
|
41
|
+
if (init.rawVideoStream) {
|
|
42
|
+
// Set implied options for client-side processing
|
|
43
|
+
init.sendDeviceMeta = false;
|
|
44
|
+
init.sendFrameMeta = false;
|
|
45
|
+
init.sendDummyByte = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
super(init);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected override getDefaultValue(): T {
|
|
52
|
+
return {
|
|
53
|
+
...super.getDefaultValue(),
|
|
54
|
+
downsizeOnError: true,
|
|
55
|
+
sendDeviceMeta: true,
|
|
56
|
+
sendDummyByte: true,
|
|
57
|
+
rawVideoStream: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public override createConnection(device: Adb): ScrcpyClientConnection {
|
|
62
|
+
const defaultValue = this.getDefaultValue();
|
|
63
|
+
const options: ScrcpyClientConnectionOptions = {
|
|
64
|
+
control: this.value.control ?? defaultValue.control,
|
|
65
|
+
sendDummyByte: this.value.sendDummyByte ?? defaultValue.sendDummyByte,
|
|
66
|
+
sendDeviceMeta: this.value.sendDeviceMeta ?? defaultValue.sendDeviceMeta,
|
|
67
|
+
};
|
|
68
|
+
if (this.value.tunnelForward) {
|
|
69
|
+
return new ScrcpyClientForwardConnection(device, options);
|
|
70
|
+
} else {
|
|
71
|
+
return new ScrcpyClientReverseConnection(device, options);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public override serializeInjectScrollControlMessage(
|
|
76
|
+
message: ScrcpyInjectScrollControlMessage1_22,
|
|
77
|
+
): Uint8Array {
|
|
78
|
+
return ScrcpyInjectScrollControlMessage1_22.serialize(message);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Adb, AdbBufferedStream, ReadableStream } from "@yume-chan/adb";
|
|
2
|
+
import type { ScrcpyClientConnection } from "../connection.js";
|
|
3
|
+
import type { H264Configuration } from "../decoder/index.js";
|
|
4
|
+
import type { ScrcpyBackOrScreenOnEvent1_18 } from "./1_18.js";
|
|
5
|
+
import type { ScrcpyInjectScrollControlMessage1_22 } from "./1_22.js";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_SERVER_PATH = '/data/local/tmp/scrcpy-server.jar';
|
|
8
|
+
|
|
9
|
+
export interface ScrcpyOptionValue {
|
|
10
|
+
toOptionValue(): string | undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isScrcpyOptionValue(value: any): value is ScrcpyOptionValue {
|
|
14
|
+
return typeof value === 'object' &&
|
|
15
|
+
value !== null &&
|
|
16
|
+
typeof value.toOptionValue === 'function';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function toScrcpyOptionValue<T>(value: any, empty: T): string | T {
|
|
20
|
+
if (isScrcpyOptionValue(value)) {
|
|
21
|
+
value = value.toOptionValue();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (value === undefined) {
|
|
25
|
+
return empty;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `${value}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface VideoStreamConfigurationPacket {
|
|
32
|
+
type: 'configuration';
|
|
33
|
+
data: H264Configuration;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface VideoStreamFramePacket {
|
|
37
|
+
type: 'frame';
|
|
38
|
+
data: Uint8Array;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type VideoStreamPacket = VideoStreamConfigurationPacket | VideoStreamFramePacket;
|
|
42
|
+
|
|
43
|
+
export interface ScrcpyOptions<T> {
|
|
44
|
+
value: Partial<T>;
|
|
45
|
+
|
|
46
|
+
formatServerArguments(): string[];
|
|
47
|
+
|
|
48
|
+
getOutputEncoderNameRegex(): RegExp;
|
|
49
|
+
|
|
50
|
+
createConnection(adb: Adb): ScrcpyClientConnection;
|
|
51
|
+
|
|
52
|
+
parseVideoStream(stream: AdbBufferedStream): ReadableStream<VideoStreamPacket>;
|
|
53
|
+
|
|
54
|
+
serializeBackOrScreenOnControlMessage(
|
|
55
|
+
message: ScrcpyBackOrScreenOnEvent1_18,
|
|
56
|
+
): Uint8Array | undefined;
|
|
57
|
+
|
|
58
|
+
serializeInjectScrollControlMessage(
|
|
59
|
+
message: ScrcpyInjectScrollControlMessage1_22,
|
|
60
|
+
): Uint8Array;
|
|
61
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Adb, AdbSync, WrapWritableStream, WritableStream } from "@yume-chan/adb";
|
|
2
|
+
import { DEFAULT_SERVER_PATH } from "./options/index.js";
|
|
3
|
+
|
|
4
|
+
export interface PushServerOptions {
|
|
5
|
+
path?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function pushServer(
|
|
9
|
+
device: Adb,
|
|
10
|
+
options: PushServerOptions = {}
|
|
11
|
+
) {
|
|
12
|
+
const { path = DEFAULT_SERVER_PATH } = options;
|
|
13
|
+
|
|
14
|
+
return new WrapWritableStream<Uint8Array, WritableStream<Uint8Array>, AdbSync>({
|
|
15
|
+
async start() {
|
|
16
|
+
const sync = await device.sync();
|
|
17
|
+
return {
|
|
18
|
+
writable: sync.write(path),
|
|
19
|
+
state: sync,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
async close(sync) {
|
|
23
|
+
await sync.dispose();
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function delay(time: number): Promise<void> {
|
|
2
|
+
return new Promise(resolve => {
|
|
3
|
+
(globalThis as any).setTimeout(resolve, time);
|
|
4
|
+
});
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const Utf8Encoder = new TextEncoder();
|
|
8
|
+
const Utf8Decoder = new TextDecoder();
|
|
9
|
+
|
|
10
|
+
export function encodeUtf8(input: string): ArrayBuffer {
|
|
11
|
+
return Utf8Encoder.encode(input).buffer;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function decodeUtf8(buffer: ArrayBuffer): string {
|
|
15
|
+
return Utf8Decoder.decode(buffer);
|
|
16
|
+
}
|