@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.
- package/CHANGELOG.json +45 -0
- package/CHANGELOG.md +25 -1
- package/LICENSE +21 -21
- package/README.md +90 -89
- package/bin/scrcpy-server +0 -0
- package/bin/version.d.ts +1 -1
- package/bin/version.js +1 -1
- package/esm/client.d.ts +14 -2
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +275 -143
- package/esm/client.js.map +1 -1
- package/esm/connection.d.ts.map +1 -1
- package/esm/connection.js +74 -86
- package/esm/connection.js.map +1 -1
- package/esm/decoder/tinyh264/index.js +48 -48
- package/esm/decoder/tinyh264/index.js.map +1 -1
- package/esm/decoder/tinyh264/wrapper.js +3 -2
- package/esm/decoder/tinyh264/wrapper.js.map +1 -1
- package/esm/decoder/web-codecs/index.js +26 -21
- package/esm/decoder/web-codecs/index.js.map +1 -1
- package/esm/message.d.ts +13 -8
- package/esm/message.d.ts.map +1 -1
- package/esm/message.js +14 -8
- package/esm/message.js.map +1 -1
- package/esm/options/1_16/index.d.ts +8 -4
- package/esm/options/1_16/index.d.ts.map +1 -1
- package/esm/options/1_16/index.js +44 -18
- package/esm/options/1_16/index.js.map +1 -1
- package/esm/options/1_16/sps.js +3 -2
- package/esm/options/1_16/sps.js.map +1 -1
- package/esm/options/1_18.d.ts +3 -2
- package/esm/options/1_18.d.ts.map +1 -1
- package/esm/options/1_18.js +24 -1
- package/esm/options/1_18.js.map +1 -1
- package/esm/options/1_21.js +4 -1
- package/esm/options/1_21.js.map +1 -1
- package/esm/options/1_22.d.ts +3 -3
- package/esm/options/1_22.d.ts.map +1 -1
- package/esm/options/1_22.js +9 -6
- package/esm/options/1_22.js.map +1 -1
- package/esm/options/1_23.js +4 -1
- package/esm/options/1_23.js.map +1 -1
- package/esm/options/1_24.d.ts +9 -0
- package/esm/options/1_24.d.ts.map +1 -0
- package/esm/options/1_24.js +13 -0
- package/esm/options/1_24.js.map +1 -0
- package/esm/options/common.d.ts +2 -0
- package/esm/options/common.d.ts.map +1 -1
- package/esm/options/common.js.map +1 -1
- package/esm/options/index.d.ts +1 -0
- package/esm/options/index.d.ts.map +1 -1
- package/esm/options/index.js +1 -0
- package/esm/options/index.js.map +1 -1
- package/esm/push-server.js +5 -10
- package/esm/push-server.js.map +1 -1
- package/package.json +8 -8
- package/scrcpy.LICENSE +203 -203
- package/scripts/fetch-server.cjs +28 -28
- package/src/client.ts +435 -275
- package/src/codec.ts +35 -35
- package/src/connection.ts +148 -145
- package/src/decoder/index.ts +3 -3
- package/src/decoder/tinyh264/index.ts +112 -112
- package/src/decoder/tinyh264/types.d.ts +137 -137
- package/src/decoder/tinyh264/worker.ts +2 -2
- package/src/decoder/tinyh264/wrapper.ts +89 -89
- package/src/decoder/types.ts +35 -35
- package/src/decoder/web-codecs/index.ts +99 -99
- package/src/index.ts +8 -8
- package/src/message.ts +113 -105
- package/src/options/1_16/index.ts +345 -315
- package/src/options/1_16/sps.ts +300 -300
- package/src/options/1_18.ts +61 -41
- package/src/options/1_21.ts +34 -34
- package/src/options/1_22.ts +78 -80
- package/src/options/1_24.ts +18 -0
- package/src/options/common.ts +66 -63
- package/src/options/index.ts +7 -6
- package/src/push-server.ts +24 -24
- package/src/utils.ts +5 -5
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
import { WritableStream } from '@yume-chan/adb';
|
|
2
|
-
import type { VideoStreamPacket } from "../../options/index.js";
|
|
3
|
-
import type { H264Configuration, H264Decoder } from "../types.js";
|
|
4
|
-
|
|
5
|
-
function toHex(value: number) {
|
|
6
|
-
return value.toString(16).padStart(2, '0').toUpperCase();
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class WebCodecsDecoder implements H264Decoder {
|
|
10
|
-
// Usually, browsers can decode most configurations,
|
|
11
|
-
// So let device choose best profile and level for itself.
|
|
12
|
-
public readonly maxProfile = undefined;
|
|
13
|
-
public readonly maxLevel = undefined;
|
|
14
|
-
|
|
15
|
-
private _writable: WritableStream<VideoStreamPacket>;
|
|
16
|
-
public get writable() { return this._writable; }
|
|
17
|
-
|
|
18
|
-
private _renderer: HTMLCanvasElement;
|
|
19
|
-
public get renderer() { return this._renderer; }
|
|
20
|
-
|
|
21
|
-
private _frameRendered = 0;
|
|
22
|
-
public get frameRendered() { return this._frameRendered; }
|
|
23
|
-
|
|
24
|
-
private context: CanvasRenderingContext2D;
|
|
25
|
-
private decoder: VideoDecoder;
|
|
26
|
-
|
|
27
|
-
// Limit FPS to system refresh rate
|
|
28
|
-
private lastFrame: VideoFrame | undefined;
|
|
29
|
-
private animationFrame: number = 0;
|
|
30
|
-
|
|
31
|
-
public constructor() {
|
|
32
|
-
this._renderer = document.createElement('canvas');
|
|
33
|
-
|
|
34
|
-
this.context = this._renderer.getContext('2d')!;
|
|
35
|
-
this.decoder = new VideoDecoder({
|
|
36
|
-
output: (frame) => {
|
|
37
|
-
if (this.lastFrame) {
|
|
38
|
-
this.lastFrame.close();
|
|
39
|
-
}
|
|
40
|
-
this.lastFrame = frame;
|
|
41
|
-
|
|
42
|
-
if (!this.animationFrame) {
|
|
43
|
-
// Start render loop on first frame
|
|
44
|
-
this.render();
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
error() { },
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
this._writable = new WritableStream<VideoStreamPacket>({
|
|
51
|
-
write: async (packet) => {
|
|
52
|
-
switch (packet.type) {
|
|
53
|
-
case 'configuration':
|
|
54
|
-
this.configure(packet.data);
|
|
55
|
-
break;
|
|
56
|
-
case 'frame':
|
|
57
|
-
this.decoder.decode(new EncodedVideoChunk({
|
|
58
|
-
// Treat `undefined` as `key`, otherwise won't decode.
|
|
59
|
-
type: packet.keyframe === false ? 'delta' : 'key',
|
|
60
|
-
timestamp: 0,
|
|
61
|
-
data: packet.data,
|
|
62
|
-
}));
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private render = () => {
|
|
70
|
-
if (this.lastFrame) {
|
|
71
|
-
this._frameRendered += 1;
|
|
72
|
-
this.context.drawImage(this.lastFrame, 0, 0);
|
|
73
|
-
this.lastFrame.close();
|
|
74
|
-
this.lastFrame = undefined;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this.animationFrame = requestAnimationFrame(this.render);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
private configure(config: H264Configuration) {
|
|
81
|
-
const { profileIndex, constraintSet, levelIndex } = config;
|
|
82
|
-
|
|
83
|
-
this._renderer.width = config.croppedWidth;
|
|
84
|
-
this._renderer.height = config.croppedHeight;
|
|
85
|
-
|
|
86
|
-
// https://www.rfc-editor.org/rfc/rfc6381#section-3.3
|
|
87
|
-
// ISO Base Media File Format Name Space
|
|
88
|
-
const codec = `avc1.${[profileIndex, constraintSet, levelIndex].map(toHex).join('')}`;
|
|
89
|
-
this.decoder.configure({
|
|
90
|
-
codec: codec,
|
|
91
|
-
optimizeForLatency: true,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public dispose() {
|
|
96
|
-
cancelAnimationFrame(this.animationFrame);
|
|
97
|
-
this.decoder.close();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
1
|
+
import { WritableStream } from '@yume-chan/adb';
|
|
2
|
+
import type { VideoStreamPacket } from "../../options/index.js";
|
|
3
|
+
import type { H264Configuration, H264Decoder } from "../types.js";
|
|
4
|
+
|
|
5
|
+
function toHex(value: number) {
|
|
6
|
+
return value.toString(16).padStart(2, '0').toUpperCase();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class WebCodecsDecoder implements H264Decoder {
|
|
10
|
+
// Usually, browsers can decode most configurations,
|
|
11
|
+
// So let device choose best profile and level for itself.
|
|
12
|
+
public readonly maxProfile = undefined;
|
|
13
|
+
public readonly maxLevel = undefined;
|
|
14
|
+
|
|
15
|
+
private _writable: WritableStream<VideoStreamPacket>;
|
|
16
|
+
public get writable() { return this._writable; }
|
|
17
|
+
|
|
18
|
+
private _renderer: HTMLCanvasElement;
|
|
19
|
+
public get renderer() { return this._renderer; }
|
|
20
|
+
|
|
21
|
+
private _frameRendered = 0;
|
|
22
|
+
public get frameRendered() { return this._frameRendered; }
|
|
23
|
+
|
|
24
|
+
private context: CanvasRenderingContext2D;
|
|
25
|
+
private decoder: VideoDecoder;
|
|
26
|
+
|
|
27
|
+
// Limit FPS to system refresh rate
|
|
28
|
+
private lastFrame: VideoFrame | undefined;
|
|
29
|
+
private animationFrame: number = 0;
|
|
30
|
+
|
|
31
|
+
public constructor() {
|
|
32
|
+
this._renderer = document.createElement('canvas');
|
|
33
|
+
|
|
34
|
+
this.context = this._renderer.getContext('2d')!;
|
|
35
|
+
this.decoder = new VideoDecoder({
|
|
36
|
+
output: (frame) => {
|
|
37
|
+
if (this.lastFrame) {
|
|
38
|
+
this.lastFrame.close();
|
|
39
|
+
}
|
|
40
|
+
this.lastFrame = frame;
|
|
41
|
+
|
|
42
|
+
if (!this.animationFrame) {
|
|
43
|
+
// Start render loop on first frame
|
|
44
|
+
this.render();
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
error() { },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this._writable = new WritableStream<VideoStreamPacket>({
|
|
51
|
+
write: async (packet) => {
|
|
52
|
+
switch (packet.type) {
|
|
53
|
+
case 'configuration':
|
|
54
|
+
this.configure(packet.data);
|
|
55
|
+
break;
|
|
56
|
+
case 'frame':
|
|
57
|
+
this.decoder.decode(new EncodedVideoChunk({
|
|
58
|
+
// Treat `undefined` as `key`, otherwise won't decode.
|
|
59
|
+
type: packet.keyframe === false ? 'delta' : 'key',
|
|
60
|
+
timestamp: 0,
|
|
61
|
+
data: packet.data,
|
|
62
|
+
}));
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private render = () => {
|
|
70
|
+
if (this.lastFrame) {
|
|
71
|
+
this._frameRendered += 1;
|
|
72
|
+
this.context.drawImage(this.lastFrame, 0, 0);
|
|
73
|
+
this.lastFrame.close();
|
|
74
|
+
this.lastFrame = undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.animationFrame = requestAnimationFrame(this.render);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
private configure(config: H264Configuration) {
|
|
81
|
+
const { profileIndex, constraintSet, levelIndex } = config;
|
|
82
|
+
|
|
83
|
+
this._renderer.width = config.croppedWidth;
|
|
84
|
+
this._renderer.height = config.croppedHeight;
|
|
85
|
+
|
|
86
|
+
// https://www.rfc-editor.org/rfc/rfc6381#section-3.3
|
|
87
|
+
// ISO Base Media File Format Name Space
|
|
88
|
+
const codec = `avc1.${[profileIndex, constraintSet, levelIndex].map(toHex).join('')}`;
|
|
89
|
+
this.decoder.configure({
|
|
90
|
+
codec: codec,
|
|
91
|
+
optimizeForLatency: true,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public dispose() {
|
|
96
|
+
cancelAnimationFrame(this.animationFrame);
|
|
97
|
+
this.decoder.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from './client.js';
|
|
2
|
-
export * from './codec.js';
|
|
3
|
-
export * from './connection.js';
|
|
4
|
-
export * from './decoder/index.js';
|
|
5
|
-
export * from './message.js';
|
|
6
|
-
export * from './options/index.js';
|
|
7
|
-
export * from './push-server.js';
|
|
8
|
-
export * from './utils.js';
|
|
1
|
+
export * from './client.js';
|
|
2
|
+
export * from './codec.js';
|
|
3
|
+
export * from './connection.js';
|
|
4
|
+
export * from './decoder/index.js';
|
|
5
|
+
export * from './message.js';
|
|
6
|
+
export * from './options/index.js';
|
|
7
|
+
export * from './push-server.js';
|
|
8
|
+
export * from './utils.js';
|
package/src/message.ts
CHANGED
|
@@ -1,105 +1,113 @@
|
|
|
1
|
-
import Struct, { placeholder } from '@yume-chan/struct';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.
|
|
44
|
-
.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.
|
|
51
|
-
.uint32('
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
export
|
|
105
|
-
|
|
1
|
+
import Struct, { placeholder } from '@yume-chan/struct';
|
|
2
|
+
|
|
3
|
+
// https://github.com/Genymobile/scrcpy/blob/fa5b2a29e983a46b49531def9cf3d80c40c3de37/app/src/control_msg.h#L23
|
|
4
|
+
// For their message bodies, see https://github.com/Genymobile/scrcpy/blob/5c62f3419d252d10cd8c9cbb7c918b358b81f2d0/app/src/control_msg.c#L92
|
|
5
|
+
export enum ScrcpyControlMessageType {
|
|
6
|
+
InjectKeycode,
|
|
7
|
+
InjectText,
|
|
8
|
+
InjectTouch,
|
|
9
|
+
InjectScroll,
|
|
10
|
+
BackOrScreenOn,
|
|
11
|
+
ExpandNotificationPanel,
|
|
12
|
+
ExpandSettingPanel,
|
|
13
|
+
CollapseNotificationPanel,
|
|
14
|
+
GetClipboard,
|
|
15
|
+
SetClipboard,
|
|
16
|
+
SetScreenPowerMode,
|
|
17
|
+
RotateDevice,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// https://developer.android.com/reference/android/view/MotionEvent#constants_1
|
|
21
|
+
export enum AndroidMotionEventAction {
|
|
22
|
+
Down,
|
|
23
|
+
Up,
|
|
24
|
+
Move,
|
|
25
|
+
Cancel,
|
|
26
|
+
Outside,
|
|
27
|
+
PointerDown,
|
|
28
|
+
PointerUp,
|
|
29
|
+
HoverMove,
|
|
30
|
+
Scroll,
|
|
31
|
+
HoverEnter,
|
|
32
|
+
HoverExit,
|
|
33
|
+
ButtonPress,
|
|
34
|
+
ButtonRelease,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const ScrcpySimpleControlMessage =
|
|
38
|
+
new Struct()
|
|
39
|
+
.uint8('type');
|
|
40
|
+
|
|
41
|
+
export const ScrcpyInjectTouchControlMessage =
|
|
42
|
+
new Struct()
|
|
43
|
+
.fields(ScrcpySimpleControlMessage)
|
|
44
|
+
.uint8('action', placeholder<AndroidMotionEventAction>())
|
|
45
|
+
.uint64('pointerId')
|
|
46
|
+
.uint32('pointerX')
|
|
47
|
+
.uint32('pointerY')
|
|
48
|
+
.uint16('screenWidth')
|
|
49
|
+
.uint16('screenHeight')
|
|
50
|
+
.uint16('pressure')
|
|
51
|
+
.uint32('buttons');
|
|
52
|
+
|
|
53
|
+
export type ScrcpyInjectTouchControlMessage = typeof ScrcpyInjectTouchControlMessage['TInit'];
|
|
54
|
+
|
|
55
|
+
export const ScrcpyInjectTextControlMessage =
|
|
56
|
+
new Struct()
|
|
57
|
+
.fields(ScrcpySimpleControlMessage)
|
|
58
|
+
.uint32('length')
|
|
59
|
+
.string('text', { lengthField: 'length' });
|
|
60
|
+
|
|
61
|
+
export type ScrcpyInjectTextControlMessage =
|
|
62
|
+
typeof ScrcpyInjectTextControlMessage['TInit'];
|
|
63
|
+
|
|
64
|
+
export enum AndroidKeyEventAction {
|
|
65
|
+
Down = 0,
|
|
66
|
+
Up = 1,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export enum AndroidKeyCode {
|
|
70
|
+
Home = 3,
|
|
71
|
+
Back = 4,
|
|
72
|
+
A = 29,
|
|
73
|
+
B,
|
|
74
|
+
C,
|
|
75
|
+
D,
|
|
76
|
+
E,
|
|
77
|
+
F,
|
|
78
|
+
G,
|
|
79
|
+
H,
|
|
80
|
+
I,
|
|
81
|
+
J,
|
|
82
|
+
K,
|
|
83
|
+
L,
|
|
84
|
+
M,
|
|
85
|
+
N,
|
|
86
|
+
O,
|
|
87
|
+
P,
|
|
88
|
+
Q,
|
|
89
|
+
R,
|
|
90
|
+
S,
|
|
91
|
+
T,
|
|
92
|
+
U,
|
|
93
|
+
V,
|
|
94
|
+
W,
|
|
95
|
+
X,
|
|
96
|
+
Y,
|
|
97
|
+
Z,
|
|
98
|
+
Space = 62,
|
|
99
|
+
Enter = 66,
|
|
100
|
+
Delete = 67,
|
|
101
|
+
AppSwitch = 187,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const ScrcpyInjectKeyCodeControlMessage =
|
|
105
|
+
new Struct()
|
|
106
|
+
.fields(ScrcpySimpleControlMessage)
|
|
107
|
+
.uint8('action', placeholder<AndroidKeyEventAction>())
|
|
108
|
+
.uint32('keyCode')
|
|
109
|
+
.uint32('repeat')
|
|
110
|
+
.uint32('metaState');
|
|
111
|
+
|
|
112
|
+
export type ScrcpyInjectKeyCodeControlMessage =
|
|
113
|
+
typeof ScrcpyInjectKeyCodeControlMessage['TInit'];
|