@yume-chan/scrcpy-decoder-webcodecs 2.1.0 → 2.5.3
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.md +6 -0
- package/README.md +3 -0
- package/esm/video/codec/av1.d.ts +2 -2
- package/esm/video/codec/av1.d.ts.map +1 -1
- package/esm/video/codec/av1.js +34 -7
- package/esm/video/codec/av1.js.map +1 -1
- package/esm/video/codec/h264.d.ts +3 -2
- package/esm/video/codec/h264.d.ts.map +1 -1
- package/esm/video/codec/h264.js +6 -5
- package/esm/video/codec/h264.js.map +1 -1
- package/esm/video/codec/h265.d.ts +3 -2
- package/esm/video/codec/h265.d.ts.map +1 -1
- package/esm/video/codec/h265.js +6 -5
- package/esm/video/codec/h265.js.map +1 -1
- package/esm/video/codec/h26x.d.ts +1 -1
- package/esm/video/codec/h26x.d.ts.map +1 -1
- package/esm/video/codec/h26x.js +54 -16
- package/esm/video/codec/h26x.js.map +1 -1
- package/esm/video/codec/type.d.ts +4 -1
- package/esm/video/codec/type.d.ts.map +1 -1
- package/esm/video/decoder.d.ts +4 -3
- package/esm/video/decoder.d.ts.map +1 -1
- package/esm/video/decoder.js +10 -8
- package/esm/video/decoder.js.map +1 -1
- package/package.json +5 -5
- package/src/video/codec/av1.ts +40 -7
- package/src/video/codec/h264.ts +9 -5
- package/src/video/codec/h265.ts +9 -5
- package/src/video/codec/h26x.ts +74 -18
- package/src/video/codec/type.ts +5 -0
- package/src/video/decoder.ts +18 -9
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/video/codec/h264.ts
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { h264ParseConfiguration } from "@yume-chan/scrcpy";
|
|
2
2
|
|
|
3
3
|
import { H26xDecoder } from "./h26x.js";
|
|
4
|
+
import type { CodecDecoderOptions } from "./type.js";
|
|
4
5
|
import { hexTwoDigits } from "./utils.js";
|
|
5
6
|
|
|
6
7
|
export class H264Decoder extends H26xDecoder {
|
|
7
|
-
#decoder: VideoDecoder;
|
|
8
8
|
#updateSize: (width: number, height: number) => void;
|
|
9
|
+
#options: CodecDecoderOptions | undefined;
|
|
9
10
|
|
|
10
11
|
constructor(
|
|
11
12
|
decoder: VideoDecoder,
|
|
12
13
|
updateSize: (width: number, height: number) => void,
|
|
14
|
+
options?: CodecDecoderOptions,
|
|
13
15
|
) {
|
|
14
16
|
super(decoder);
|
|
15
|
-
this.#decoder = decoder;
|
|
16
17
|
this.#updateSize = updateSize;
|
|
18
|
+
this.#options = options;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
override configure(data: Uint8Array):
|
|
21
|
+
override configure(data: Uint8Array): VideoDecoderConfig {
|
|
20
22
|
const {
|
|
21
23
|
profileIndex,
|
|
22
24
|
constraintSet,
|
|
@@ -34,9 +36,11 @@ export class H264Decoder extends H26xDecoder {
|
|
|
34
36
|
hexTwoDigits(profileIndex) +
|
|
35
37
|
hexTwoDigits(constraintSet) +
|
|
36
38
|
hexTwoDigits(levelIndex);
|
|
37
|
-
|
|
39
|
+
return {
|
|
38
40
|
codec: codec,
|
|
41
|
+
hardwareAcceleration:
|
|
42
|
+
this.#options?.hardwareAcceleration ?? "no-preference",
|
|
39
43
|
optimizeForLatency: true,
|
|
40
|
-
}
|
|
44
|
+
};
|
|
41
45
|
}
|
|
42
46
|
}
|
package/src/video/codec/h265.ts
CHANGED
|
@@ -2,22 +2,24 @@ import { getUint32LittleEndian } from "@yume-chan/no-data-view";
|
|
|
2
2
|
import { h265ParseConfiguration } from "@yume-chan/scrcpy";
|
|
3
3
|
|
|
4
4
|
import { H26xDecoder } from "./h26x.js";
|
|
5
|
+
import type { CodecDecoderOptions } from "./type.js";
|
|
5
6
|
import { hexDigits } from "./utils.js";
|
|
6
7
|
|
|
7
8
|
export class H265Decoder extends H26xDecoder {
|
|
8
|
-
#decoder: VideoDecoder;
|
|
9
9
|
#updateSize: (width: number, height: number) => void;
|
|
10
|
+
#options: CodecDecoderOptions | undefined;
|
|
10
11
|
|
|
11
12
|
constructor(
|
|
12
13
|
decoder: VideoDecoder,
|
|
13
14
|
updateSize: (width: number, height: number) => void,
|
|
15
|
+
options?: CodecDecoderOptions,
|
|
14
16
|
) {
|
|
15
17
|
super(decoder);
|
|
16
|
-
this.#decoder = decoder;
|
|
17
18
|
this.#updateSize = updateSize;
|
|
19
|
+
this.#options = options;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
override configure(data: Uint8Array):
|
|
22
|
+
override configure(data: Uint8Array): VideoDecoderConfig {
|
|
21
23
|
const {
|
|
22
24
|
generalProfileSpace,
|
|
23
25
|
generalProfileIndex,
|
|
@@ -39,12 +41,14 @@ export class H265Decoder extends H26xDecoder {
|
|
|
39
41
|
(generalTierFlag ? "H" : "L") + generalLevelIndex.toString(),
|
|
40
42
|
...Array.from(generalConstraintSet, hexDigits),
|
|
41
43
|
].join(".");
|
|
42
|
-
|
|
44
|
+
return {
|
|
43
45
|
codec,
|
|
44
46
|
// Microsoft Edge requires explicit size to work
|
|
45
47
|
codedWidth: croppedWidth,
|
|
46
48
|
codedHeight: croppedHeight,
|
|
49
|
+
hardwareAcceleration:
|
|
50
|
+
this.#options?.hardwareAcceleration ?? "no-preference",
|
|
47
51
|
optimizeForLatency: true,
|
|
48
|
-
}
|
|
52
|
+
};
|
|
49
53
|
}
|
|
50
54
|
}
|
package/src/video/codec/h26x.ts
CHANGED
|
@@ -1,36 +1,92 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ScrcpyMediaStreamDataPacket,
|
|
3
|
+
ScrcpyMediaStreamPacket,
|
|
4
|
+
} from "@yume-chan/scrcpy";
|
|
2
5
|
|
|
3
6
|
import type { CodecDecoder } from "./type.js";
|
|
4
7
|
|
|
5
8
|
export abstract class H26xDecoder implements CodecDecoder {
|
|
6
|
-
#config: Uint8Array | undefined;
|
|
7
9
|
#decoder: VideoDecoder;
|
|
8
10
|
|
|
11
|
+
#config: (VideoDecoderConfig & { raw: Uint8Array }) | undefined;
|
|
12
|
+
#configured = false;
|
|
13
|
+
|
|
9
14
|
constructor(decoder: VideoDecoder) {
|
|
10
15
|
this.#decoder = decoder;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
abstract configure(data: Uint8Array):
|
|
18
|
+
abstract configure(data: Uint8Array): VideoDecoderConfig;
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
#configureAndDecodeFirstKeyframe(
|
|
21
|
+
config: VideoDecoderConfig & { raw: Uint8Array },
|
|
22
|
+
packet: ScrcpyMediaStreamDataPacket,
|
|
23
|
+
) {
|
|
24
|
+
this.#decoder.configure(config);
|
|
25
|
+
this.#configured = true;
|
|
21
26
|
|
|
22
27
|
// For H.264 and H.265, when the stream is in Annex B format
|
|
23
28
|
// (which Scrcpy uses, as Android MediaCodec produces),
|
|
24
29
|
// configuration data needs to be combined with the first frame data.
|
|
25
30
|
// https://www.w3.org/TR/webcodecs-avc-codec-registration/#encodedvideochunk-type
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const { raw } = config;
|
|
32
|
+
const data = new Uint8Array(raw.length + packet.data.length);
|
|
33
|
+
data.set(raw, 0);
|
|
34
|
+
data.set(packet.data, raw.length);
|
|
35
|
+
|
|
36
|
+
this.#decoder.decode(
|
|
37
|
+
new EncodedVideoChunk({
|
|
38
|
+
type: "key",
|
|
39
|
+
timestamp: 0,
|
|
40
|
+
data,
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
decode(packet: ScrcpyMediaStreamPacket): void {
|
|
46
|
+
if (packet.type === "configuration") {
|
|
47
|
+
this.#config = {
|
|
48
|
+
...this.configure(packet.data),
|
|
49
|
+
raw: packet.data,
|
|
50
|
+
};
|
|
51
|
+
this.#configured = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!this.#config) {
|
|
56
|
+
throw new Error("Decoder not configured");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (packet.keyframe) {
|
|
60
|
+
if (this.#decoder.decodeQueueSize) {
|
|
61
|
+
// If the device is too slow to decode all frames,
|
|
62
|
+
// discard queued frames when next keyframe arrives.
|
|
63
|
+
// (can only do this for keyframes because decoding must start from a keyframe)
|
|
64
|
+
// This limits the maximum latency to 1 keyframe interval
|
|
65
|
+
// (60 frames by default).
|
|
66
|
+
this.#decoder.reset();
|
|
67
|
+
|
|
68
|
+
// `reset` also resets the decoder configuration
|
|
69
|
+
// so we need to re-configure it again.
|
|
70
|
+
this.#configureAndDecodeFirstKeyframe(this.#config, packet);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!this.#configured) {
|
|
75
|
+
this.#configureAndDecodeFirstKeyframe(this.#config, packet);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!this.#configured) {
|
|
81
|
+
if (packet.keyframe === undefined) {
|
|
82
|
+
// Scrcpy <1.23 doesn't send `keyframe` flag
|
|
83
|
+
// Infer the first frame after configuration as keyframe
|
|
84
|
+
// (`VideoDecoder` will throw error if it's not)
|
|
85
|
+
this.#configureAndDecodeFirstKeyframe(this.#config, packet);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error("Expect a keyframe but got a delta frame");
|
|
34
90
|
}
|
|
35
91
|
|
|
36
92
|
this.#decoder.decode(
|
|
@@ -38,7 +94,7 @@ export abstract class H26xDecoder implements CodecDecoder {
|
|
|
38
94
|
// Treat `undefined` as `key`, otherwise won't decode.
|
|
39
95
|
type: packet.keyframe === false ? "delta" : "key",
|
|
40
96
|
timestamp: 0,
|
|
41
|
-
data,
|
|
97
|
+
data: packet.data,
|
|
42
98
|
}),
|
|
43
99
|
);
|
|
44
100
|
}
|
package/src/video/codec/type.ts
CHANGED
|
@@ -4,9 +4,14 @@ export interface CodecDecoder {
|
|
|
4
4
|
decode(packet: ScrcpyMediaStreamPacket): void;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export interface CodecDecoderOptions {
|
|
8
|
+
hardwareAcceleration?: HardwareAcceleration | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
export interface CodecDecoderConstructor {
|
|
8
12
|
new (
|
|
9
13
|
decoder: VideoDecoder,
|
|
10
14
|
updateSize: (width: number, height: number) => void,
|
|
15
|
+
options?: CodecDecoderOptions,
|
|
11
16
|
): CodecDecoder;
|
|
12
17
|
}
|
package/src/video/decoder.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { WritableStreamDefaultController } from "@yume-chan/stream-extra";
|
|
|
9
9
|
import { WritableStream } from "@yume-chan/stream-extra";
|
|
10
10
|
|
|
11
11
|
import { Av1Codec, H264Decoder, H265Decoder } from "./codec/index.js";
|
|
12
|
-
import type { CodecDecoder } from "./codec/type.js";
|
|
12
|
+
import type { CodecDecoder, CodecDecoderOptions } from "./codec/type.js";
|
|
13
13
|
import { Pool } from "./pool.js";
|
|
14
14
|
import type { VideoFrameRenderer } from "./render/index.js";
|
|
15
15
|
import { VideoFrameCapturer } from "./snapshot.js";
|
|
@@ -35,6 +35,13 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|
|
35
35
|
return this.#codec;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
#renderer: VideoFrameRenderer;
|
|
39
|
+
get renderer() {
|
|
40
|
+
return this.#renderer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#options: CodecDecoderOptions;
|
|
44
|
+
|
|
38
45
|
#codecDecoder: CodecDecoder;
|
|
39
46
|
|
|
40
47
|
#writable: WritableStream<ScrcpyMediaStreamPacket>;
|
|
@@ -45,11 +52,6 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|
|
45
52
|
#error: Error | undefined;
|
|
46
53
|
#controller!: WritableStreamDefaultController;
|
|
47
54
|
|
|
48
|
-
#renderer: VideoFrameRenderer;
|
|
49
|
-
get renderer() {
|
|
50
|
-
return this.#renderer;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
55
|
#framesDraw = 0;
|
|
54
56
|
#framesPresented = 0;
|
|
55
57
|
get framesRendered() {
|
|
@@ -87,10 +89,14 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|
|
87
89
|
/**
|
|
88
90
|
* Create a new WebCodecs video decoder.
|
|
89
91
|
*/
|
|
90
|
-
constructor({
|
|
92
|
+
constructor({
|
|
93
|
+
codec,
|
|
94
|
+
renderer,
|
|
95
|
+
...options
|
|
96
|
+
}: WebCodecsVideoDecoder.Options) {
|
|
91
97
|
this.#codec = codec;
|
|
92
|
-
|
|
93
98
|
this.#renderer = renderer;
|
|
99
|
+
this.#options = options;
|
|
94
100
|
|
|
95
101
|
this.#decoder = new VideoDecoder({
|
|
96
102
|
output: (frame) => {
|
|
@@ -119,18 +125,21 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|
|
119
125
|
this.#codecDecoder = new H264Decoder(
|
|
120
126
|
this.#decoder,
|
|
121
127
|
this.#updateSize,
|
|
128
|
+
this.#options,
|
|
122
129
|
);
|
|
123
130
|
break;
|
|
124
131
|
case ScrcpyVideoCodecId.H265:
|
|
125
132
|
this.#codecDecoder = new H265Decoder(
|
|
126
133
|
this.#decoder,
|
|
127
134
|
this.#updateSize,
|
|
135
|
+
this.#options,
|
|
128
136
|
);
|
|
129
137
|
break;
|
|
130
138
|
case ScrcpyVideoCodecId.AV1:
|
|
131
139
|
this.#codecDecoder = new Av1Codec(
|
|
132
140
|
this.#decoder,
|
|
133
141
|
this.#updateSize,
|
|
142
|
+
this.#options,
|
|
134
143
|
);
|
|
135
144
|
break;
|
|
136
145
|
default:
|
|
@@ -232,7 +241,7 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|
|
232
241
|
}
|
|
233
242
|
|
|
234
243
|
export namespace WebCodecsVideoDecoder {
|
|
235
|
-
export interface Options {
|
|
244
|
+
export interface Options extends CodecDecoderOptions {
|
|
236
245
|
/**
|
|
237
246
|
* The video codec to decode
|
|
238
247
|
*/
|