@yume-chan/scrcpy-decoder-webcodecs 2.5.0 → 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 +28 -70
  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 +13 -25
  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 +15 -31
  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 -31
  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 +40 -102
  85. package/src/video/codec/h264.ts +17 -41
  86. package/src/video/codec/h265.ts +18 -49
  87. package/src/video/codec/h26x.ts +41 -36
  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
@@ -0,0 +1,284 @@
1
+ import { EventEmitter, StickyEventEmitter } from "@yume-chan/event";
2
+ import { ScrcpyVideoDecoderPerformanceCounter } from "@yume-chan/scrcpy-decoder-shared";
3
+ import { concatBuffers, TransformStream } from "@yume-chan/stream-extra";
4
+ function decodeFirstKeyFrame(decoder, config, chunk) {
5
+ decoder.decode(new EncodedVideoChunk({
6
+ type: "key",
7
+ timestamp: chunk.timestamp,
8
+ // `lib.dom.d.ts` doesn't allow `duration` to be undefined.
9
+ duration: chunk.duration,
10
+ data: config.raw
11
+ ? concatBuffers([config.raw, chunk.data])
12
+ : chunk.data,
13
+ }));
14
+ }
15
+ function decodeFrame(decoder, chunk) {
16
+ decoder.decode(new EncodedVideoChunk({
17
+ type: chunk.type,
18
+ timestamp: chunk.timestamp,
19
+ // `lib.dom.d.ts` doesn't allow `duration` to be undefined.
20
+ duration: chunk.duration,
21
+ data: chunk.data,
22
+ }));
23
+ }
24
+ export class VideoDecoderStream extends TransformStream {
25
+ #controller;
26
+ #state = { type: "unconfigured" };
27
+ #hardwareAcceleration = new StickyEventEmitter({
28
+ initialValue: "no-preference",
29
+ equals: (a, b) => a === b,
30
+ });
31
+ get hardwareAcceleration() {
32
+ return this.#hardwareAcceleration.value;
33
+ }
34
+ get onHardwareAccelerationChange() {
35
+ return this.#hardwareAcceleration.event;
36
+ }
37
+ /**
38
+ * Gets the number of frames waiting to be decoded.
39
+ */
40
+ get decodeQueueSize() {
41
+ if ("decoder" in this.#state) {
42
+ return this.#state.decoder.decodeQueueSize;
43
+ }
44
+ return 0;
45
+ }
46
+ #onDequeue = new EventEmitter();
47
+ /**
48
+ * Gets an event when a frame is dequeued (either decoded or discarded).
49
+ */
50
+ get onDequeue() {
51
+ return this.#onDequeue.event;
52
+ }
53
+ #counter = new ScrcpyVideoDecoderPerformanceCounter();
54
+ /**
55
+ * Gets the number of frames decoded by the decoder.
56
+ */
57
+ get framesDecoded() {
58
+ return this.#counter.framesDecoded;
59
+ }
60
+ /**
61
+ * Gets the number of frames skipped by the decoder.
62
+ */
63
+ get framesSkippedDecoding() {
64
+ return this.#counter.framesSkippedDecoding;
65
+ }
66
+ /**
67
+ * Gets the number of times the decoder has been reset to catch up new keyframes.
68
+ */
69
+ get decoderResetCount() {
70
+ return this.#counter.decoderResetCount;
71
+ }
72
+ #abortController = new AbortController();
73
+ #skipFramesBefore = -1;
74
+ constructor() {
75
+ let controller;
76
+ super({
77
+ start: (controller_) => {
78
+ // WARN: can't use `this` here
79
+ controller = controller_;
80
+ },
81
+ transform: async (chunk) => {
82
+ if ("codec" in chunk) {
83
+ this.#hardwareAcceleration.fire(chunk.hardwareAcceleration ?? "no-preference");
84
+ // Validate the config by creating decoder right away
85
+ switch (this.#state.type) {
86
+ case "unconfigured":
87
+ case "reclaimed":
88
+ this.#state = {
89
+ type: "configured",
90
+ decoder: this.#createDecoder(chunk),
91
+ config: chunk,
92
+ };
93
+ break;
94
+ // @ts-expect-error: intentional case fallthrough
95
+ case "decoding":
96
+ await this.#state.decoder.flush();
97
+ // fallthrough
98
+ case "configured":
99
+ // Reuse the existing decoder.
100
+ // `decoder` is definitely in "configured" state,
101
+ // otherwise the stream is in error state
102
+ // and no `transform` callbacks will be called.
103
+ this.#state.decoder.configure(chunk);
104
+ this.#state = {
105
+ type: "configured",
106
+ decoder: this.#state.decoder,
107
+ config: chunk,
108
+ };
109
+ break;
110
+ }
111
+ return;
112
+ }
113
+ this.#handleVideoChunk(chunk);
114
+ },
115
+ flush: async () => {
116
+ // only call `flush` when `decoder.state` is "configured".
117
+ if ("decoder" in this.#state &&
118
+ this.#state.decoder.state === "configured") {
119
+ // This also delays closing `readable` and `writable` streams
120
+ // (if they are not in error state) until all frames are decoded.
121
+ await this.#state.decoder.flush();
122
+ }
123
+ this.#dispose();
124
+ },
125
+ cancel: () => {
126
+ // Immediately close the decoder on stream cancel/error
127
+ this.#dispose();
128
+ },
129
+ });
130
+ this.#controller = controller;
131
+ }
132
+ #handleVideoChunk(chunk) {
133
+ switch (this.#state.type) {
134
+ case "unconfigured":
135
+ throw new Error("Decoder not configured");
136
+ case "configured": {
137
+ if (chunk.type === "delta") {
138
+ throw new Error("Expect a keyframe but got a delta frame");
139
+ }
140
+ decodeFirstKeyFrame(this.#state.decoder, this.#state.config, chunk);
141
+ this.#state = {
142
+ type: "decoding",
143
+ decoder: this.#state.decoder,
144
+ config: this.#state.config,
145
+ frames: [chunk],
146
+ };
147
+ break;
148
+ }
149
+ case "decoding":
150
+ if (chunk.type === "key") {
151
+ this.#state.frames.length = 0;
152
+ }
153
+ this.#state.frames.push(chunk);
154
+ decodeFrame(this.#state.decoder, chunk);
155
+ break;
156
+ case "reclaimed": {
157
+ const decoder = this.#createDecoder(this.#state.config);
158
+ if (chunk.type === "key") {
159
+ decodeFirstKeyFrame(decoder, this.#state.config, chunk);
160
+ this.#state = {
161
+ type: "decoding",
162
+ decoder: decoder,
163
+ config: this.#state.config,
164
+ frames: [chunk],
165
+ };
166
+ return;
167
+ }
168
+ if (!this.#state.frames) {
169
+ // Reclaimed when configured but no frames have been decoded yet,
170
+ // so a keyframe is required to start decoding.
171
+ throw new Error("Expect a keyframe but got a delta frame");
172
+ }
173
+ decodeFirstKeyFrame(decoder, this.#state.config, this.#state.frames[0]);
174
+ // `chunk`s must have monotonic timestamps, so we can skip all frames before `chunk`.
175
+ this.#skipFramesBefore = chunk.timestamp;
176
+ this.#state.frames.push(chunk);
177
+ for (const frame of this.#state.frames.slice(1)) {
178
+ decodeFrame(decoder, frame);
179
+ }
180
+ this.#state = {
181
+ type: "decoding",
182
+ decoder: decoder,
183
+ config: this.#state.config,
184
+ frames: this.#state.frames,
185
+ };
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ #createDecoder(config) {
191
+ const decoder = new VideoDecoder({
192
+ output: (frame) => {
193
+ if (frame.timestamp < this.#skipFramesBefore) {
194
+ frame.close();
195
+ return;
196
+ }
197
+ this.#counter.increaseFramesDecoded();
198
+ try {
199
+ this.#controller.enqueue(frame);
200
+ }
201
+ catch {
202
+ // `controller` is closed
203
+ frame.close();
204
+ }
205
+ },
206
+ error: (error) => {
207
+ if (error.name === "QuotaExceededError") {
208
+ // Chrome reclaims inactive `VideoDecoder`'s after 90 seconds
209
+ // (for example due to pausing decoding when the document is hidden).
210
+ // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc;l=181;drc=aba14550ed0b4620deb59e7c5f551cbde8970fb3
211
+ // Reset states, it will be recreated in next `transform` call.
212
+ switch (this.#state.type) {
213
+ case "unconfigured":
214
+ case "reclaimed":
215
+ // Don't throw, report errors to stream
216
+ this.#controller.error(new Error("Unexpected state when reclaiming decoder"));
217
+ this.#dispose();
218
+ return;
219
+ case "configured":
220
+ // Unlike `transform` callback,
221
+ // this doesn't re-create the decoder immediately,
222
+ // because the config has already been validated,
223
+ // and since browser reclaims the decoder to save hardware resources,
224
+ // we don't want to undo that by creating a new decoder immediately.
225
+ this.#state = {
226
+ type: "reclaimed",
227
+ config,
228
+ frames: undefined,
229
+ };
230
+ break;
231
+ case "decoding":
232
+ this.#state = {
233
+ type: "reclaimed",
234
+ config,
235
+ frames: this.#state.frames,
236
+ };
237
+ break;
238
+ }
239
+ return;
240
+ }
241
+ // Maybe the decoder is hardware accelerated but the hardware has an issue,
242
+ // retry with software decoder.
243
+ if (error.name === "EncodingError" &&
244
+ this.#state.type === "decoding" &&
245
+ config.hardwareAcceleration !== "prefer-software") {
246
+ try {
247
+ config.hardwareAcceleration = "prefer-software";
248
+ this.#hardwareAcceleration.fire("prefer-software");
249
+ const decoder = this.#createDecoder(config);
250
+ // Replay frames with rendering skipping
251
+ this.#skipFramesBefore =
252
+ this.#state.frames.at(-1).timestamp;
253
+ decodeFirstKeyFrame(decoder, config, this.#state.frames[0]);
254
+ for (const frame of this.#state.frames.slice(1)) {
255
+ decodeFrame(decoder, frame);
256
+ }
257
+ // Update state
258
+ this.#state.decoder = decoder;
259
+ return;
260
+ }
261
+ catch {
262
+ // ignore, report original error to stream
263
+ }
264
+ }
265
+ // Propagate other decoder errors to stream.
266
+ this.#controller.error(error);
267
+ this.#dispose();
268
+ },
269
+ });
270
+ decoder.addEventListener("dequeue", () => this.#onDequeue.fire(undefined), { signal: this.#abortController.signal });
271
+ decoder.configure(config);
272
+ return decoder;
273
+ }
274
+ #dispose() {
275
+ this.#abortController.abort();
276
+ this.#onDequeue.dispose();
277
+ if ("decoder" in this.#state &&
278
+ this.#state.decoder.state !== "closed") {
279
+ this.#state.decoder.close();
280
+ }
281
+ this.#state = { type: "unconfigured" };
282
+ }
283
+ }
284
+ //# sourceMappingURL=video-decoder-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-decoder-stream.js","sourceRoot":"","sources":["../../../src/video/utils/video-decoder-stream.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yume-chan/scrcpy-decoder-webcodecs",
3
- "version": "2.5.0",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "Raw H.264 stream decoder and renderer using WebCodecs API (requires modern browser).",
5
5
  "keywords": [
6
6
  "adb",
@@ -14,11 +14,11 @@
14
14
  "email": "cnsimonchan@live.com",
15
15
  "url": "https://chensi.moe/blog"
16
16
  },
17
- "homepage": "https://github.com/yume-chan/ya-webadb/tree/main/packages/scrcpy-decoder-webcodecs#readme",
17
+ "homepage": "https://github.com/yume-chan/ya-webadb/tree/main/libraries/scrcpy-decoder-webcodecs#readme",
18
18
  "repository": {
19
19
  "type": "git",
20
20
  "url": "git+https://github.com/yume-chan/ya-webadb.git",
21
- "directory": "packages/scrcpy-decoder-webcodecs"
21
+ "directory": "libraries/scrcpy-decoder-webcodecs"
22
22
  },
23
23
  "bugs": {
24
24
  "url": "https://github.com/yume-chan/ya-webadb/issues"
@@ -29,20 +29,21 @@
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
31
  "@yume-chan/async": "^4.1.3",
32
- "@yume-chan/event": "^2.0.0",
33
- "@yume-chan/no-data-view": "^2.0.0",
34
- "@yume-chan/scrcpy": "^2.3.0",
35
- "@yume-chan/scrcpy-decoder-tinyh264": "^2.1.0",
36
- "@yume-chan/stream-extra": "^2.1.0"
32
+ "@yume-chan/media-codec": "3.0.0-beta.0",
33
+ "@yume-chan/event": "3.0.0-beta.0",
34
+ "@yume-chan/no-data-view": "3.0.0-beta.0",
35
+ "@yume-chan/scrcpy": "3.0.0-beta.0",
36
+ "@yume-chan/scrcpy-decoder-shared": "3.0.0-beta.0",
37
+ "@yume-chan/stream-extra": "3.0.0-beta.0"
37
38
  },
38
39
  "devDependencies": {
39
- "prettier": "^3.6.2",
40
- "typescript": "^5.9.2",
40
+ "prettier": "^3.8.4",
41
+ "typescript": "^6.0.3",
41
42
  "@yume-chan/eslint-config": "^1.0.0",
42
43
  "@yume-chan/tsconfig": "^1.0.0"
43
44
  },
44
45
  "scripts": {
45
- "build": "tsc -b tsconfig.build.json",
46
+ "build": "tsc -p tsconfig.build.json",
46
47
  "lint": "run-eslint && prettier src/**/*.ts --write --tab-width 4"
47
48
  }
48
49
  }
@@ -1,106 +1,44 @@
1
- import type { ScrcpyMediaStreamPacket } from "@yume-chan/scrcpy";
2
- import { Av1 } from "@yume-chan/scrcpy";
3
-
4
- import type { CodecDecoder, CodecDecoderOptions } from "./type.js";
5
- import { decimalTwoDigits } from "./utils.js";
6
-
7
- export class Av1Codec implements CodecDecoder {
8
- #decoder: VideoDecoder;
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
- this.#decoder = decoder;
18
- this.#updateSize = updateSize;
19
- this.#options = options;
20
- }
21
-
22
- #configure(data: Uint8Array) {
23
- const parser = new Av1(data);
24
- const sequenceHeader = parser.searchSequenceHeaderObu();
25
-
26
- if (!sequenceHeader) {
27
- return;
28
- }
29
-
30
- const {
31
- seq_profile: seqProfile,
32
- seq_level_idx: [seqLevelIdx = 0],
33
- max_frame_width_minus_1,
34
- max_frame_height_minus_1,
35
- color_config: {
36
- BitDepth,
37
- mono_chrome: monoChrome,
38
- subsampling_x: subsamplingX,
39
- subsampling_y: subsamplingY,
40
- chroma_sample_position: chromaSamplePosition,
41
- color_description_present_flag,
1
+ import { Av1 } from "@yume-chan/media-codec";
2
+ import { TransformStream } from "@yume-chan/stream-extra";
3
+
4
+ import type { CodecTransformStream } from "./type.js";
5
+
6
+ export class Av1TransformStream
7
+ extends TransformStream<
8
+ CodecTransformStream.Input,
9
+ CodecTransformStream.Output
10
+ >
11
+ implements CodecTransformStream
12
+ {
13
+ constructor() {
14
+ super({
15
+ transform: (packet, controller) => {
16
+ if (packet.type === "configuration") {
17
+ // AV1 decoder doesn't need configuration packets
18
+ return;
19
+ }
20
+
21
+ const parser = new Av1(packet.data);
22
+ const sequenceHeader = parser.searchSequenceHeaderObu();
23
+
24
+ if (sequenceHeader) {
25
+ const width = sequenceHeader.max_frame_width_minus_1 + 1;
26
+ const height = sequenceHeader.max_frame_height_minus_1 + 1;
27
+
28
+ controller.enqueue({
29
+ codec: Av1.toCodecString(sequenceHeader),
30
+ codedWidth: width,
31
+ codedHeight: height,
32
+ });
33
+ }
34
+
35
+ controller.enqueue({
36
+ // AV1 was added in Scrcpy 2.0 which must have `keyframe` property.
37
+ type: packet.keyframe ? "key" : "delta",
38
+ timestamp: packet.timestamp,
39
+ data: packet.data,
40
+ });
42
41
  },
43
- } = sequenceHeader;
44
-
45
- let colorPrimaries: Av1.ColorPrimaries;
46
- let transferCharacteristics: Av1.TransferCharacteristics;
47
- let matrixCoefficients: Av1.MatrixCoefficients;
48
- let colorRange: boolean;
49
- if (color_description_present_flag) {
50
- ({
51
- color_primaries: colorPrimaries,
52
- transfer_characteristics: transferCharacteristics,
53
- matrix_coefficients: matrixCoefficients,
54
- color_range: colorRange,
55
- } = sequenceHeader.color_config);
56
- } else {
57
- colorPrimaries = Av1.ColorPrimaries.Bt709;
58
- transferCharacteristics = Av1.TransferCharacteristics.Bt709;
59
- matrixCoefficients = Av1.MatrixCoefficients.Bt709;
60
- colorRange = false;
61
- }
62
-
63
- const width = max_frame_width_minus_1 + 1;
64
- const height = max_frame_height_minus_1 + 1;
65
-
66
- this.#updateSize(width, height);
67
-
68
- const codec = [
69
- "av01",
70
- seqProfile.toString(16),
71
- decimalTwoDigits(seqLevelIdx) +
72
- (sequenceHeader.seq_tier[0] ? "H" : "M"),
73
- decimalTwoDigits(BitDepth),
74
- monoChrome ? "1" : "0",
75
- (subsamplingX ? "1" : "0") +
76
- (subsamplingY ? "1" : "0") +
77
- chromaSamplePosition.toString(),
78
- decimalTwoDigits(colorPrimaries),
79
- decimalTwoDigits(transferCharacteristics),
80
- decimalTwoDigits(matrixCoefficients),
81
- colorRange ? "1" : "0",
82
- ].join(".");
83
- this.#decoder.configure({
84
- codec,
85
- hardwareAcceleration:
86
- this.#options?.hardwareAcceleration ?? "no-preference",
87
- optimizeForLatency: true,
88
42
  });
89
43
  }
90
-
91
- decode(packet: ScrcpyMediaStreamPacket): void {
92
- if (packet.type === "configuration") {
93
- return;
94
- }
95
-
96
- this.#configure(packet.data);
97
- this.#decoder.decode(
98
- new EncodedVideoChunk({
99
- // Treat `undefined` as `key`, otherwise it won't decode.
100
- type: packet.keyframe === false ? "delta" : "key",
101
- timestamp: 0,
102
- data: packet.data,
103
- }),
104
- );
105
- }
106
44
  }
@@ -1,48 +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
- #decoder: VideoDecoder;
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.#decoder = decoder;
19
- this.#updateSize = updateSize;
20
- 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";
21
13
  }
22
14
 
23
- override configure(data: Uint8Array): void {
24
- const {
25
- profileIndex,
26
- constraintSet,
27
- levelIndex,
28
- croppedWidth,
29
- croppedHeight,
30
- } = h264ParseConfiguration(data);
31
-
32
- this.#updateSize(croppedWidth, croppedHeight);
15
+ override configure(data: Uint8Array): H26xTransformStream.Config {
16
+ const configuration = H264.parseConfiguration(data);
33
17
 
34
- // https://www.rfc-editor.org/rfc/rfc6381#section-3.3
35
- // ISO Base Media File Format Name Space
36
- const codec =
37
- "avc1." +
38
- hexTwoDigits(profileIndex) +
39
- hexTwoDigits(constraintSet) +
40
- hexTwoDigits(levelIndex);
41
- this.#decoder.configure({
42
- codec: codec,
43
- hardwareAcceleration:
44
- this.#options?.hardwareAcceleration ?? "no-preference",
45
- optimizeForLatency: true,
46
- });
18
+ return {
19
+ codec: H264.toCodecString(configuration),
20
+ codedHeight: configuration.croppedHeight,
21
+ codedWidth: configuration.croppedWidth,
22
+ };
47
23
  }
48
24
  }
@@ -1,56 +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
- #decoder: VideoDecoder;
10
- #updateSize: (width: number, height: number) => void;
11
- #options: CodecDecoderOptions | undefined;
12
-
13
- constructor(
14
- decoder: VideoDecoder,
15
- updateSize: (width: number, height: number) => void,
16
- options?: CodecDecoderOptions,
17
- ) {
18
- super(decoder);
19
- this.#decoder = decoder;
20
- this.#updateSize = updateSize;
21
- 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";
22
11
  }
23
12
 
24
- override configure(data: Uint8Array): void {
25
- const {
26
- generalProfileSpace,
27
- generalProfileIndex,
28
- generalProfileCompatibilitySet,
29
- generalTierFlag,
30
- generalLevelIndex,
31
- generalConstraintSet,
32
- croppedWidth,
33
- croppedHeight,
34
- } = h265ParseConfiguration(data);
35
-
36
- this.#updateSize(croppedWidth, croppedHeight);
13
+ override configure(data: Uint8Array): H26xTransformStream.Config {
14
+ const configuration = H265.parseConfiguration(data);
37
15
 
38
- const codec = [
39
- "hev1",
40
- ["", "A", "B", "C"][generalProfileSpace]! +
41
- generalProfileIndex.toString(),
42
- hexDigits(getUint32LittleEndian(generalProfileCompatibilitySet, 0)),
43
- (generalTierFlag ? "H" : "L") + generalLevelIndex.toString(),
44
- ...Array.from(generalConstraintSet, hexDigits),
45
- ].join(".");
46
- this.#decoder.configure({
47
- codec,
48
- // Microsoft Edge requires explicit size to work
49
- codedWidth: croppedWidth,
50
- codedHeight: croppedHeight,
51
- hardwareAcceleration:
52
- this.#options?.hardwareAcceleration ?? "no-preference",
53
- optimizeForLatency: true,
54
- });
16
+ return {
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,
23
+ };
55
24
  }
56
25
  }