@yume-chan/scrcpy-decoder-webcodecs 2.5.3 → 3.0.0-beta.0

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 (117) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/esm/video/codec/av1.d.ts +4 -6
  4. package/esm/video/codec/av1.d.ts.map +1 -1
  5. package/esm/video/codec/av1.js +29 -95
  6. package/esm/video/codec/av1.js.map +1 -1
  7. package/esm/video/codec/h264.d.ts +4 -6
  8. package/esm/video/codec/h264.d.ts.map +1 -1
  9. package/esm/video/codec/h264.js +11 -21
  10. package/esm/video/codec/h264.js.map +1 -1
  11. package/esm/video/codec/h265.d.ts +4 -6
  12. package/esm/video/codec/h265.d.ts.map +1 -1
  13. package/esm/video/codec/h265.js +13 -27
  14. package/esm/video/codec/h265.js.map +1 -1
  15. package/esm/video/codec/h26x.d.ts +9 -6
  16. package/esm/video/codec/h26x.d.ts.map +1 -1
  17. package/esm/video/codec/h26x.js +25 -69
  18. package/esm/video/codec/h26x.js.map +1 -1
  19. package/esm/video/codec/index.d.ts +0 -1
  20. package/esm/video/codec/index.d.ts.map +1 -1
  21. package/esm/video/codec/index.js +0 -1
  22. package/esm/video/codec/index.js.map +1 -1
  23. package/esm/video/codec/type.d.ts +23 -8
  24. package/esm/video/codec/type.d.ts.map +1 -1
  25. package/esm/video/decoder.d.ts +62 -15
  26. package/esm/video/decoder.d.ts.map +1 -1
  27. package/esm/video/decoder.js +170 -134
  28. package/esm/video/decoder.js.map +1 -1
  29. package/esm/video/index.d.ts +1 -0
  30. package/esm/video/index.d.ts.map +1 -1
  31. package/esm/video/index.js +1 -0
  32. package/esm/video/index.js.map +1 -1
  33. package/esm/video/render/auto-canvas.d.ts +26 -0
  34. package/esm/video/render/auto-canvas.d.ts.map +1 -0
  35. package/esm/video/render/auto-canvas.js +80 -0
  36. package/esm/video/render/auto-canvas.js.map +1 -0
  37. package/esm/video/render/bitmap.d.ts +2 -2
  38. package/esm/video/render/bitmap.d.ts.map +1 -1
  39. package/esm/video/render/bitmap.js +18 -8
  40. package/esm/video/render/bitmap.js.map +1 -1
  41. package/esm/video/render/canvas.d.ts +38 -4
  42. package/esm/video/render/canvas.d.ts.map +1 -1
  43. package/esm/video/render/canvas.js +97 -10
  44. package/esm/video/render/canvas.js.map +1 -1
  45. package/esm/video/render/flow-control.d.ts +32 -0
  46. package/esm/video/render/flow-control.d.ts.map +1 -0
  47. package/esm/video/render/flow-control.js +139 -0
  48. package/esm/video/render/flow-control.js.map +1 -0
  49. package/esm/video/render/index.d.ts +2 -0
  50. package/esm/video/render/index.d.ts.map +1 -1
  51. package/esm/video/render/index.js +2 -0
  52. package/esm/video/render/index.js.map +1 -1
  53. package/esm/video/render/insertable-stream.d.ts +15 -3
  54. package/esm/video/render/insertable-stream.d.ts.map +1 -1
  55. package/esm/video/render/insertable-stream.js +29 -11
  56. package/esm/video/render/insertable-stream.js.map +1 -1
  57. package/esm/video/render/redraw.d.ts +31 -0
  58. package/esm/video/render/redraw.d.ts.map +1 -0
  59. package/esm/video/render/redraw.js +75 -0
  60. package/esm/video/render/redraw.js.map +1 -0
  61. package/esm/video/render/type.d.ts +8 -2
  62. package/esm/video/render/type.d.ts.map +1 -1
  63. package/esm/video/render/webgl.d.ts +38 -5
  64. package/esm/video/render/webgl.d.ts.map +1 -1
  65. package/esm/video/render/webgl.js +228 -59
  66. package/esm/video/render/webgl.js.map +1 -1
  67. package/esm/video/utils/index.d.ts +4 -0
  68. package/esm/video/utils/index.d.ts.map +1 -0
  69. package/esm/video/utils/index.js +4 -0
  70. package/esm/video/utils/index.js.map +1 -0
  71. package/esm/video/utils/snapshot.d.ts +2 -0
  72. package/esm/video/utils/snapshot.d.ts.map +1 -0
  73. package/esm/video/utils/snapshot.js +18 -0
  74. package/esm/video/utils/snapshot.js.map +1 -0
  75. package/esm/video/utils/timestamp.d.ts +19 -0
  76. package/esm/video/utils/timestamp.d.ts.map +1 -0
  77. package/esm/video/utils/timestamp.js +87 -0
  78. package/esm/video/utils/timestamp.js.map +1 -0
  79. package/esm/video/utils/video-decoder-stream.d.ts +30 -0
  80. package/esm/video/utils/video-decoder-stream.d.ts.map +1 -0
  81. package/esm/video/utils/video-decoder-stream.js +284 -0
  82. package/esm/video/utils/video-decoder-stream.js.map +1 -0
  83. package/package.json +12 -11
  84. package/src/video/codec/av1.ts +41 -131
  85. package/src/video/codec/h264.ts +15 -37
  86. package/src/video/codec/h265.ts +16 -45
  87. package/src/video/codec/h26x.ts +45 -96
  88. package/src/video/codec/index.ts +0 -1
  89. package/src/video/codec/type.ts +32 -12
  90. package/src/video/decoder.ts +203 -165
  91. package/src/video/index.ts +1 -0
  92. package/src/video/render/auto-canvas.ts +105 -0
  93. package/src/video/render/bitmap.ts +23 -9
  94. package/src/video/render/canvas.ts +157 -12
  95. package/src/video/render/flow-control.ts +182 -0
  96. package/src/video/render/index.ts +2 -0
  97. package/src/video/render/insertable-stream.ts +50 -10
  98. package/src/video/render/redraw.ts +88 -0
  99. package/src/video/render/type.ts +10 -2
  100. package/src/video/render/webgl.ts +316 -80
  101. package/src/video/utils/index.ts +3 -0
  102. package/src/video/utils/snapshot.ts +22 -0
  103. package/src/video/utils/timestamp.ts +108 -0
  104. package/src/video/utils/video-decoder-stream.ts +406 -0
  105. package/tsconfig.build.json +0 -14
  106. package/CHANGELOG.md +0 -140
  107. package/esm/video/codec/utils.d.ts +0 -4
  108. package/esm/video/codec/utils.d.ts.map +0 -1
  109. package/esm/video/codec/utils.js +0 -10
  110. package/esm/video/codec/utils.js.map +0 -1
  111. package/esm/video/snapshot.d.ts +0 -6
  112. package/esm/video/snapshot.d.ts.map +0 -1
  113. package/esm/video/snapshot.js +0 -41
  114. package/esm/video/snapshot.js.map +0 -1
  115. package/src/video/codec/utils.ts +0 -11
  116. package/src/video/snapshot.ts +0 -41
  117. package/tsconfig.build.tsbuildinfo +0 -1
@@ -1,46 +1,24 @@
1
- import { h264ParseConfiguration } from "@yume-chan/scrcpy";
1
+ import { H264 } from "@yume-chan/media-codec";
2
2
 
3
- import { H26xDecoder } from "./h26x.js";
4
- import type { CodecDecoderOptions } from "./type.js";
5
- import { hexTwoDigits } from "./utils.js";
3
+ import { H26xTransformStream } from "./h26x.js";
6
4
 
7
- export class H264Decoder extends H26xDecoder {
8
- #updateSize: (width: number, height: number) => void;
9
- #options: CodecDecoderOptions | undefined;
10
-
11
- constructor(
12
- decoder: VideoDecoder,
13
- updateSize: (width: number, height: number) => void,
14
- options?: CodecDecoderOptions,
15
- ) {
16
- super(decoder);
17
- this.#updateSize = updateSize;
18
- this.#options = options;
5
+ export class H264TransformStream extends H26xTransformStream {
6
+ override convertFrameType(
7
+ keyframe: boolean | undefined,
8
+ data: Uint8Array,
9
+ ): EncodedVideoChunkType {
10
+ // Older versions of Scrcpy doesn't have `keyframe` property,
11
+ // so detect it from the frame data.
12
+ return (keyframe ?? H264.containsKeyFrame(data)) ? "key" : "delta";
19
13
  }
20
14
 
21
- override configure(data: Uint8Array): VideoDecoderConfig {
22
- const {
23
- profileIndex,
24
- constraintSet,
25
- levelIndex,
26
- croppedWidth,
27
- croppedHeight,
28
- } = h264ParseConfiguration(data);
29
-
30
- this.#updateSize(croppedWidth, croppedHeight);
15
+ override configure(data: Uint8Array): H26xTransformStream.Config {
16
+ const configuration = H264.parseConfiguration(data);
31
17
 
32
- // https://www.rfc-editor.org/rfc/rfc6381#section-3.3
33
- // ISO Base Media File Format Name Space
34
- const codec =
35
- "avc1." +
36
- hexTwoDigits(profileIndex) +
37
- hexTwoDigits(constraintSet) +
38
- hexTwoDigits(levelIndex);
39
18
  return {
40
- codec: codec,
41
- hardwareAcceleration:
42
- this.#options?.hardwareAcceleration ?? "no-preference",
43
- optimizeForLatency: true,
19
+ codec: H264.toCodecString(configuration),
20
+ codedHeight: configuration.croppedHeight,
21
+ codedWidth: configuration.croppedWidth,
44
22
  };
45
23
  }
46
24
  }
@@ -1,54 +1,25 @@
1
- import { getUint32LittleEndian } from "@yume-chan/no-data-view";
2
- import { h265ParseConfiguration } from "@yume-chan/scrcpy";
1
+ import { H265 } from "@yume-chan/media-codec";
3
2
 
4
- import { H26xDecoder } from "./h26x.js";
5
- import type { CodecDecoderOptions } from "./type.js";
6
- import { hexDigits } from "./utils.js";
3
+ import { H26xTransformStream } from "./h26x.js";
7
4
 
8
- export class H265Decoder extends H26xDecoder {
9
- #updateSize: (width: number, height: number) => void;
10
- #options: CodecDecoderOptions | undefined;
11
-
12
- constructor(
13
- decoder: VideoDecoder,
14
- updateSize: (width: number, height: number) => void,
15
- options?: CodecDecoderOptions,
16
- ) {
17
- super(decoder);
18
- this.#updateSize = updateSize;
19
- this.#options = options;
5
+ export class H265TransformStream extends H26xTransformStream {
6
+ override convertFrameType(
7
+ keyframe: boolean | undefined,
8
+ ): EncodedVideoChunkType {
9
+ // H.265 was added in Scrcpy 2.0 which must have `keyframe` property.
10
+ return keyframe ? "key" : "delta";
20
11
  }
21
12
 
22
- override configure(data: Uint8Array): VideoDecoderConfig {
23
- const {
24
- generalProfileSpace,
25
- generalProfileIndex,
26
- generalProfileCompatibilitySet,
27
- generalTierFlag,
28
- generalLevelIndex,
29
- generalConstraintSet,
30
- croppedWidth,
31
- croppedHeight,
32
- } = h265ParseConfiguration(data);
33
-
34
- this.#updateSize(croppedWidth, croppedHeight);
13
+ override configure(data: Uint8Array): H26xTransformStream.Config {
14
+ const configuration = H265.parseConfiguration(data);
35
15
 
36
- const codec = [
37
- "hev1",
38
- ["", "A", "B", "C"][generalProfileSpace]! +
39
- generalProfileIndex.toString(),
40
- hexDigits(getUint32LittleEndian(generalProfileCompatibilitySet, 0)),
41
- (generalTierFlag ? "H" : "L") + generalLevelIndex.toString(),
42
- ...Array.from(generalConstraintSet, hexDigits),
43
- ].join(".");
44
16
  return {
45
- codec,
46
- // Microsoft Edge requires explicit size to work
47
- codedWidth: croppedWidth,
48
- codedHeight: croppedHeight,
49
- hardwareAcceleration:
50
- this.#options?.hardwareAcceleration ?? "no-preference",
51
- optimizeForLatency: true,
17
+ codec: H265.toCodecString(configuration),
18
+ // Microsoft Edge on Windows requires explicit size,
19
+ // otherwise it returns frames in incorrect size.
20
+ // And it needs cropped size, as opposed to the option name.
21
+ codedWidth: configuration.croppedWidth,
22
+ codedHeight: configuration.croppedHeight,
52
23
  };
53
24
  }
54
25
  }
@@ -1,101 +1,50 @@
1
- import type {
2
- ScrcpyMediaStreamDataPacket,
3
- ScrcpyMediaStreamPacket,
4
- } from "@yume-chan/scrcpy";
5
-
6
- import type { CodecDecoder } from "./type.js";
7
-
8
- export abstract class H26xDecoder implements CodecDecoder {
9
- #decoder: VideoDecoder;
10
-
11
- #config: (VideoDecoderConfig & { raw: Uint8Array }) | undefined;
12
- #configured = false;
13
-
14
- constructor(decoder: VideoDecoder) {
15
- this.#decoder = decoder;
1
+ import { TransformStream } from "@yume-chan/stream-extra";
2
+
3
+ import type { CodecTransformStream } from "./type.js";
4
+
5
+ export abstract class H26xTransformStream
6
+ extends TransformStream<
7
+ CodecTransformStream.Input,
8
+ CodecTransformStream.Output
9
+ >
10
+ implements CodecTransformStream
11
+ {
12
+ constructor() {
13
+ super({
14
+ transform: (packet, controller) => {
15
+ if (packet.type === "configuration") {
16
+ controller.enqueue(this.#configure(packet.data));
17
+ return;
18
+ }
19
+
20
+ controller.enqueue({
21
+ type: this.convertFrameType(packet.keyframe, packet.data),
22
+ timestamp: packet.timestamp,
23
+ data: packet.data,
24
+ });
25
+ },
26
+ });
16
27
  }
17
28
 
18
- abstract configure(data: Uint8Array): VideoDecoderConfig;
19
-
20
- #configureAndDecodeFirstKeyframe(
21
- config: VideoDecoderConfig & { raw: Uint8Array },
22
- packet: ScrcpyMediaStreamDataPacket,
23
- ) {
24
- this.#decoder.configure(config);
25
- this.#configured = true;
26
-
27
- // For H.264 and H.265, when the stream is in Annex B format
28
- // (which Scrcpy uses, as Android MediaCodec produces),
29
- // configuration data needs to be combined with the first frame data.
30
- // https://www.w3.org/TR/webcodecs-avc-codec-registration/#encodedvideochunk-type
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
- );
29
+ abstract convertFrameType(
30
+ keyframe: boolean | undefined,
31
+ data: Uint8Array,
32
+ ): EncodedVideoChunkType;
33
+
34
+ abstract configure(data: Uint8Array): H26xTransformStream.Config;
35
+
36
+ #configure(data: Uint8Array): CodecTransformStream.Config {
37
+ return {
38
+ ...this.configure(data),
39
+ // For H.264 and H.265, when the stream is in Annex B format
40
+ // (which Scrcpy uses, as Android MediaCodec produces),
41
+ // configuration data needs to be combined with the first frame data.
42
+ // https://www.w3.org/TR/webcodecs-avc-codec-registration/#encodedvideochunk-type
43
+ raw: data,
44
+ };
43
45
  }
46
+ }
44
47
 
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");
90
- }
91
-
92
- this.#decoder.decode(
93
- new EncodedVideoChunk({
94
- // Treat `undefined` as `key`, otherwise won't decode.
95
- type: packet.keyframe === false ? "delta" : "key",
96
- timestamp: 0,
97
- data: packet.data,
98
- }),
99
- );
100
- }
48
+ export namespace H26xTransformStream {
49
+ export type Config = Omit<CodecTransformStream.Config, "raw">;
101
50
  }
@@ -3,4 +3,3 @@ export * from "./h264.js";
3
3
  export * from "./h265.js";
4
4
  export * from "./h26x.js";
5
5
  export * from "./type.js";
6
- export * from "./utils.js";
@@ -1,17 +1,37 @@
1
- import type { ScrcpyMediaStreamPacket } from "@yume-chan/scrcpy";
1
+ import type {
2
+ ScrcpyMediaStreamConfigurationPacket,
3
+ ScrcpyMediaStreamDataPacket,
4
+ } from "@yume-chan/scrcpy";
5
+ import type { TransformStream } from "@yume-chan/stream-extra";
2
6
 
3
- export interface CodecDecoder {
4
- decode(packet: ScrcpyMediaStreamPacket): void;
5
- }
7
+ export type CodecTransformStream = TransformStream<
8
+ CodecTransformStream.Input,
9
+ CodecTransformStream.Output
10
+ >;
11
+
12
+ export namespace CodecTransformStream {
13
+ export type Input =
14
+ | ScrcpyMediaStreamConfigurationPacket
15
+ | (ScrcpyMediaStreamDataPacket & { timestamp: number });
16
+
17
+ export type Config = VideoDecoderConfig & {
18
+ codedWidth: number;
19
+ codedHeight: number;
20
+ /**
21
+ * Sets an optional raw buffer what will be prepended with the first key frame for decoding.
22
+ *
23
+ * Some codecs (e.g. H.264 and H.265 in Annex B format)
24
+ * send configuration in separate packet,
25
+ * but the configuration also needs to be feed into the decoder.
26
+ */
27
+ raw?: AllowSharedBufferSource;
28
+ };
29
+
30
+ export type VideoChunk = EncodedVideoChunkInit;
6
31
 
7
- export interface CodecDecoderOptions {
8
- hardwareAcceleration?: HardwareAcceleration | undefined;
32
+ export type Output = Config | VideoChunk;
9
33
  }
10
34
 
11
- export interface CodecDecoderConstructor {
12
- new (
13
- decoder: VideoDecoder,
14
- updateSize: (width: number, height: number) => void,
15
- options?: CodecDecoderOptions,
16
- ): CodecDecoder;
35
+ export interface CodecTransformStreamConstructor {
36
+ new (): CodecTransformStream;
17
37
  }