@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.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/esm/video/codec/av1.d.ts +4 -6
- package/esm/video/codec/av1.d.ts.map +1 -1
- package/esm/video/codec/av1.js +28 -70
- package/esm/video/codec/av1.js.map +1 -1
- package/esm/video/codec/h264.d.ts +4 -6
- package/esm/video/codec/h264.d.ts.map +1 -1
- package/esm/video/codec/h264.js +13 -25
- package/esm/video/codec/h264.js.map +1 -1
- package/esm/video/codec/h265.d.ts +4 -6
- package/esm/video/codec/h265.d.ts.map +1 -1
- package/esm/video/codec/h265.js +15 -31
- package/esm/video/codec/h265.js.map +1 -1
- package/esm/video/codec/h26x.d.ts +9 -6
- package/esm/video/codec/h26x.d.ts.map +1 -1
- package/esm/video/codec/h26x.js +25 -31
- package/esm/video/codec/h26x.js.map +1 -1
- package/esm/video/codec/index.d.ts +0 -1
- package/esm/video/codec/index.d.ts.map +1 -1
- package/esm/video/codec/index.js +0 -1
- package/esm/video/codec/index.js.map +1 -1
- package/esm/video/codec/type.d.ts +23 -8
- package/esm/video/codec/type.d.ts.map +1 -1
- package/esm/video/decoder.d.ts +62 -15
- package/esm/video/decoder.d.ts.map +1 -1
- package/esm/video/decoder.js +170 -134
- package/esm/video/decoder.js.map +1 -1
- package/esm/video/index.d.ts +1 -0
- package/esm/video/index.d.ts.map +1 -1
- package/esm/video/index.js +1 -0
- package/esm/video/index.js.map +1 -1
- package/esm/video/render/auto-canvas.d.ts +26 -0
- package/esm/video/render/auto-canvas.d.ts.map +1 -0
- package/esm/video/render/auto-canvas.js +80 -0
- package/esm/video/render/auto-canvas.js.map +1 -0
- package/esm/video/render/bitmap.d.ts +2 -2
- package/esm/video/render/bitmap.d.ts.map +1 -1
- package/esm/video/render/bitmap.js +18 -8
- package/esm/video/render/bitmap.js.map +1 -1
- package/esm/video/render/canvas.d.ts +38 -4
- package/esm/video/render/canvas.d.ts.map +1 -1
- package/esm/video/render/canvas.js +97 -10
- package/esm/video/render/canvas.js.map +1 -1
- package/esm/video/render/flow-control.d.ts +32 -0
- package/esm/video/render/flow-control.d.ts.map +1 -0
- package/esm/video/render/flow-control.js +139 -0
- package/esm/video/render/flow-control.js.map +1 -0
- package/esm/video/render/index.d.ts +2 -0
- package/esm/video/render/index.d.ts.map +1 -1
- package/esm/video/render/index.js +2 -0
- package/esm/video/render/index.js.map +1 -1
- package/esm/video/render/insertable-stream.d.ts +15 -3
- package/esm/video/render/insertable-stream.d.ts.map +1 -1
- package/esm/video/render/insertable-stream.js +29 -11
- package/esm/video/render/insertable-stream.js.map +1 -1
- package/esm/video/render/redraw.d.ts +31 -0
- package/esm/video/render/redraw.d.ts.map +1 -0
- package/esm/video/render/redraw.js +75 -0
- package/esm/video/render/redraw.js.map +1 -0
- package/esm/video/render/type.d.ts +8 -2
- package/esm/video/render/type.d.ts.map +1 -1
- package/esm/video/render/webgl.d.ts +38 -5
- package/esm/video/render/webgl.d.ts.map +1 -1
- package/esm/video/render/webgl.js +228 -59
- package/esm/video/render/webgl.js.map +1 -1
- package/esm/video/utils/index.d.ts +4 -0
- package/esm/video/utils/index.d.ts.map +1 -0
- package/esm/video/utils/index.js +4 -0
- package/esm/video/utils/index.js.map +1 -0
- package/esm/video/utils/snapshot.d.ts +2 -0
- package/esm/video/utils/snapshot.d.ts.map +1 -0
- package/esm/video/utils/snapshot.js +18 -0
- package/esm/video/utils/snapshot.js.map +1 -0
- package/esm/video/utils/timestamp.d.ts +19 -0
- package/esm/video/utils/timestamp.d.ts.map +1 -0
- package/esm/video/utils/timestamp.js +87 -0
- package/esm/video/utils/timestamp.js.map +1 -0
- package/esm/video/utils/video-decoder-stream.d.ts +30 -0
- package/esm/video/utils/video-decoder-stream.d.ts.map +1 -0
- package/esm/video/utils/video-decoder-stream.js +284 -0
- package/esm/video/utils/video-decoder-stream.js.map +1 -0
- package/package.json +12 -11
- package/src/video/codec/av1.ts +40 -102
- package/src/video/codec/h264.ts +17 -41
- package/src/video/codec/h265.ts +18 -49
- package/src/video/codec/h26x.ts +41 -36
- package/src/video/codec/index.ts +0 -1
- package/src/video/codec/type.ts +32 -12
- package/src/video/decoder.ts +203 -165
- package/src/video/index.ts +1 -0
- package/src/video/render/auto-canvas.ts +105 -0
- package/src/video/render/bitmap.ts +23 -9
- package/src/video/render/canvas.ts +157 -12
- package/src/video/render/flow-control.ts +182 -0
- package/src/video/render/index.ts +2 -0
- package/src/video/render/insertable-stream.ts +50 -10
- package/src/video/render/redraw.ts +88 -0
- package/src/video/render/type.ts +10 -2
- package/src/video/render/webgl.ts +316 -80
- package/src/video/utils/index.ts +3 -0
- package/src/video/utils/snapshot.ts +22 -0
- package/src/video/utils/timestamp.ts +108 -0
- package/src/video/utils/video-decoder-stream.ts +406 -0
- package/tsconfig.build.json +0 -14
- package/CHANGELOG.md +0 -140
- package/esm/video/codec/utils.d.ts +0 -4
- package/esm/video/codec/utils.d.ts.map +0 -1
- package/esm/video/codec/utils.js +0 -10
- package/esm/video/codec/utils.js.map +0 -1
- package/esm/video/snapshot.d.ts +0 -6
- package/esm/video/snapshot.d.ts.map +0 -1
- package/esm/video/snapshot.js +0 -41
- package/esm/video/snapshot.js.map +0 -1
- package/src/video/codec/utils.ts +0 -11
- package/src/video/snapshot.ts +0 -41
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/video/render/canvas.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/video/render/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAKzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEpD,8BAAsB,wBAAwB,CAC1C,QAAQ,SAAS,wBAAwB,CAAC,OAAO,GAC7C,wBAAwB,CAAC,OAAO,CACtC,YAAW,kBAAkB;;IAC3B,QAAQ,KAAK,IAAI,IAAI,kBAAkB,CAAC,YAAY,CAAC;IAGrD,IAAI,MAAM,wCAET;IAID,IAAI,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,SAAS,CAE5C;IAcD,IAAI,SAAS,2BAEZ;IAKD,IAAI,QAAQ,+BAEX;gBAGG,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,gBAAgB,CAAC,SAAS,CAAC,EACxD,OAAO,CAAC,EAAE,QAAQ;IAuEtB;;;;;;;OAOG;IACH,MAAM;IAIA,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAOvE,OAAO,IAAI,SAAS;CASvB;AAED,yBAAiB,wBAAwB,CAAC;IACtC,UAAiB,OAAO;QACpB,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;QAE7C;;;;;;;;;;;WAWG;QACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;KACjD;CACJ"}
|
|
@@ -1,22 +1,109 @@
|
|
|
1
|
-
import { createCanvas } from "@yume-chan/scrcpy-decoder-
|
|
1
|
+
import { createCanvas } from "@yume-chan/scrcpy-decoder-shared";
|
|
2
|
+
import { WritableStream } from "@yume-chan/stream-extra";
|
|
3
|
+
import { canvasToBlob } from "../utils/index.js";
|
|
4
|
+
import { RedrawController } from "./redraw.js";
|
|
2
5
|
export class CanvasVideoFrameRenderer {
|
|
3
6
|
#canvas;
|
|
4
7
|
get canvas() {
|
|
5
8
|
return this.#canvas;
|
|
6
9
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
#options;
|
|
11
|
+
#canvasSize;
|
|
12
|
+
get options() {
|
|
13
|
+
return this.#options;
|
|
14
|
+
}
|
|
15
|
+
#resizeObserver;
|
|
16
|
+
#displayWidth = Infinity;
|
|
17
|
+
#displayHeight = Infinity;
|
|
18
|
+
#draw;
|
|
19
|
+
#controller = new RedrawController((frame) => {
|
|
20
|
+
if (this.#canvasSize !== "external") {
|
|
21
|
+
this.#updateSize(frame);
|
|
22
|
+
}
|
|
23
|
+
return this.#draw(frame);
|
|
24
|
+
});
|
|
25
|
+
get lastFrame() {
|
|
26
|
+
return this.#controller.lastFrame;
|
|
27
|
+
}
|
|
28
|
+
#writable = new WritableStream({
|
|
29
|
+
write: (frame) => this.#controller.draw(frame),
|
|
30
|
+
});
|
|
31
|
+
get writable() {
|
|
32
|
+
return this.#writable;
|
|
33
|
+
}
|
|
34
|
+
constructor(draw, options) {
|
|
35
|
+
this.#draw = draw;
|
|
36
|
+
this.#canvas = options?.canvas ?? createCanvas();
|
|
37
|
+
this.#options = options;
|
|
38
|
+
this.#canvasSize = options?.canvasSize ?? "video";
|
|
39
|
+
if (this.#canvasSize === "display") {
|
|
40
|
+
if (typeof HTMLCanvasElement === "undefined" ||
|
|
41
|
+
!(this.#canvas instanceof HTMLCanvasElement)) {
|
|
42
|
+
throw new Error("`canvasSize: display` is only supported for HTMLCanvasElement");
|
|
43
|
+
}
|
|
44
|
+
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
45
|
+
const entry = entries[0];
|
|
46
|
+
if (!entry) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const devicePixelSize = entry.devicePixelContentBoxSize?.[0];
|
|
50
|
+
if (devicePixelSize) {
|
|
51
|
+
this.#setDisplaySize(devicePixelSize.inlineSize, devicePixelSize.blockSize);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const cssSize = entry.contentBoxSize[0];
|
|
55
|
+
if (cssSize) {
|
|
56
|
+
this.#setDisplaySize(Math.round(cssSize.inlineSize * devicePixelRatio), Math.round(cssSize.blockSize * devicePixelRatio));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
this.#resizeObserver.observe(this.#canvas);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
#setDisplaySize(width, height) {
|
|
63
|
+
if (this.#displayWidth === width && this.#displayHeight === height) {
|
|
64
|
+
return;
|
|
10
65
|
}
|
|
11
|
-
|
|
12
|
-
|
|
66
|
+
this.#displayWidth = width;
|
|
67
|
+
this.#displayHeight = height;
|
|
68
|
+
void this.#controller.redraw();
|
|
69
|
+
}
|
|
70
|
+
#updateSize(frame) {
|
|
71
|
+
let { codedWidth: width, codedHeight: height } = frame;
|
|
72
|
+
if (this.#canvasSize === "display") {
|
|
73
|
+
width = Math.min(width, this.#displayWidth);
|
|
74
|
+
height = Math.min(height, this.#displayHeight);
|
|
75
|
+
}
|
|
76
|
+
if (this.#canvas.width === width && this.#canvas.height === height) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
this.#canvas.width = width;
|
|
80
|
+
this.#canvas.height = height;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Redraws the last drawn frame.
|
|
85
|
+
*
|
|
86
|
+
* If a draw or redraw is in progress, it waits for them to finish before redrawing.
|
|
87
|
+
*
|
|
88
|
+
* If a redraw is in progress and another one is in queue,
|
|
89
|
+
* it cancels the queued redraw and redraws the latest frame instead.
|
|
90
|
+
*/
|
|
91
|
+
redraw() {
|
|
92
|
+
return this.#controller.redraw();
|
|
93
|
+
}
|
|
94
|
+
async snapshot(options) {
|
|
95
|
+
if (this.#canvasSize !== "video") {
|
|
96
|
+
return undefined;
|
|
13
97
|
}
|
|
98
|
+
return canvasToBlob(this.#canvas, options);
|
|
14
99
|
}
|
|
15
|
-
|
|
16
|
-
if (this.#
|
|
17
|
-
this.#canvas.width =
|
|
18
|
-
this.#canvas.height =
|
|
100
|
+
dispose() {
|
|
101
|
+
if (this.#canvasSize !== "external") {
|
|
102
|
+
this.#canvas.width = 0;
|
|
103
|
+
this.#canvas.height = 0;
|
|
19
104
|
}
|
|
105
|
+
this.#resizeObserver?.disconnect();
|
|
106
|
+
this.#controller.dispose();
|
|
20
107
|
}
|
|
21
108
|
}
|
|
22
109
|
//# sourceMappingURL=canvas.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/video/render/canvas.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/video/render/canvas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ScrcpyVideoRendererPerformanceCounterInterface } from "@yume-chan/scrcpy-decoder-shared";
|
|
2
|
+
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
|
|
3
|
+
import { WritableStream } from "@yume-chan/stream-extra";
|
|
4
|
+
export declare class RendererController implements TransformStream<VideoFrame, VideoFrame>, ScrcpyVideoRendererPerformanceCounterInterface {
|
|
5
|
+
#private;
|
|
6
|
+
get readable(): ReadableStream<VideoFrame>;
|
|
7
|
+
get writable(): WritableStream<VideoFrame>;
|
|
8
|
+
get captureFrame(): VideoFrame | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Gets the number of frames that have been drawn on the renderer.
|
|
11
|
+
*/
|
|
12
|
+
get framesRendered(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Gets the number of frames that's visible to the user.
|
|
15
|
+
*
|
|
16
|
+
* Multiple frames might be rendered during one vertical sync interval,
|
|
17
|
+
* but only the last of them is represented to the user.
|
|
18
|
+
* This costs some performance but reduces latency by 1 frame.
|
|
19
|
+
*
|
|
20
|
+
* Might be `0` if the renderer is in a nested Web Worker on Chrome due to a Chrome bug.
|
|
21
|
+
* https://issues.chromium.org/issues/41483010
|
|
22
|
+
*/
|
|
23
|
+
get framesDisplayed(): number;
|
|
24
|
+
/**
|
|
25
|
+
* Gets the number of frames that wasn't drawn on the renderer
|
|
26
|
+
* because the renderer can't keep up
|
|
27
|
+
*/
|
|
28
|
+
get framesSkippedRendering(): number;
|
|
29
|
+
constructor();
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=flow-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-control.d.ts","sourceRoot":"","sources":["../../../src/video/render/flow-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8CAA8C,EAAE,MAAM,kCAAkC,CAAC;AAEvG,OAAO,KAAK,EAER,cAAc,EACd,eAAe,EAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGH,cAAc,EACjB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,kBACT,YACI,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,EACvC,8CAA8C;;IAIlD,IAAI,QAAQ,+BAEX;IAID,IAAI,QAAQ,+BAEX;IAGD,IAAI,YAAY,2BAEf;IAOD;;OAEG;IACH,IAAI,cAAc,WAEjB;IACD;;;;;;;;;OASG;IACH,IAAI,eAAe,WAElB;IACD;;;OAGG;IACH,IAAI,sBAAsB,WAEzB;;IAmGD,OAAO;CAgBV"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ScrcpyVideoRendererPerformanceCounter } from "@yume-chan/scrcpy-decoder-shared";
|
|
2
|
+
import { PushReadableStream, tryClose, WritableStream, } from "@yume-chan/stream-extra";
|
|
3
|
+
export class RendererController {
|
|
4
|
+
#readable;
|
|
5
|
+
#readableController;
|
|
6
|
+
get readable() {
|
|
7
|
+
return this.#readable;
|
|
8
|
+
}
|
|
9
|
+
#writable;
|
|
10
|
+
#writableController;
|
|
11
|
+
get writable() {
|
|
12
|
+
return this.#writable;
|
|
13
|
+
}
|
|
14
|
+
#captureFrame;
|
|
15
|
+
get captureFrame() {
|
|
16
|
+
return this.#captureFrame;
|
|
17
|
+
}
|
|
18
|
+
#nextFrame;
|
|
19
|
+
#drawTask;
|
|
20
|
+
#counter = new ScrcpyVideoRendererPerformanceCounter();
|
|
21
|
+
/**
|
|
22
|
+
* Gets the number of frames that have been drawn on the renderer.
|
|
23
|
+
*/
|
|
24
|
+
get framesRendered() {
|
|
25
|
+
return this.#counter.framesRendered;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Gets the number of frames that's visible to the user.
|
|
29
|
+
*
|
|
30
|
+
* Multiple frames might be rendered during one vertical sync interval,
|
|
31
|
+
* but only the last of them is represented to the user.
|
|
32
|
+
* This costs some performance but reduces latency by 1 frame.
|
|
33
|
+
*
|
|
34
|
+
* Might be `0` if the renderer is in a nested Web Worker on Chrome due to a Chrome bug.
|
|
35
|
+
* https://issues.chromium.org/issues/41483010
|
|
36
|
+
*/
|
|
37
|
+
get framesDisplayed() {
|
|
38
|
+
return this.#counter.framesDisplayed;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Gets the number of frames that wasn't drawn on the renderer
|
|
42
|
+
* because the renderer can't keep up
|
|
43
|
+
*/
|
|
44
|
+
get framesSkippedRendering() {
|
|
45
|
+
return this.#counter.framesSkippedRendering;
|
|
46
|
+
}
|
|
47
|
+
constructor() {
|
|
48
|
+
this.#readable = new PushReadableStream((controller) => {
|
|
49
|
+
this.#readableController = controller;
|
|
50
|
+
});
|
|
51
|
+
this.#writable = new WritableStream({
|
|
52
|
+
start: (controller) => {
|
|
53
|
+
this.#writableController = controller;
|
|
54
|
+
// Propagate `readable` error back to `writable`
|
|
55
|
+
const signal = this.#readableController.abortSignal;
|
|
56
|
+
signal.addEventListener("abort", () => controller.error(signal.reason));
|
|
57
|
+
},
|
|
58
|
+
write: (frame) => {
|
|
59
|
+
this.#captureFrame?.close();
|
|
60
|
+
// `#captureFrame` and `#nextFrame` must not be the same object
|
|
61
|
+
// because they need to be closed at different times
|
|
62
|
+
this.#captureFrame = frame.clone();
|
|
63
|
+
// Frame A is drawing, frame B (`#nextFrame`) is waiting,
|
|
64
|
+
// then frame C (`frame`) arrives.
|
|
65
|
+
// Skip frame B and queue frame C
|
|
66
|
+
if (this.#nextFrame) {
|
|
67
|
+
this.#nextFrame.close();
|
|
68
|
+
this.#counter.increaseFramesSkippedRendering();
|
|
69
|
+
}
|
|
70
|
+
this.#nextFrame = frame;
|
|
71
|
+
// Don't `await` because this writable needs to
|
|
72
|
+
// accept incoming frames as fast as produced.
|
|
73
|
+
// The `#draw` method then draws the frames
|
|
74
|
+
// as fast as the renderer can keep up
|
|
75
|
+
void this.#tryStartDrawing();
|
|
76
|
+
},
|
|
77
|
+
close: async () => {
|
|
78
|
+
// Normally `WritableStream` only calls `close` after `write` finishes,
|
|
79
|
+
// but because our `write` doesn't wait for `#draw`,
|
|
80
|
+
// we might still need to send `#nextFrame` to `readable`.
|
|
81
|
+
// So wait for `#drawTask` before closing `readable`
|
|
82
|
+
await this.#drawTask;
|
|
83
|
+
// `#nextFrame` must be `undefined` at this point
|
|
84
|
+
// Propagate `writable` close to `readable`
|
|
85
|
+
this.#readableController.close();
|
|
86
|
+
this.#counter.dispose();
|
|
87
|
+
// Don't close `#captureFrame` to allow using `snapshot` on the last frame
|
|
88
|
+
},
|
|
89
|
+
abort: async (reason) => {
|
|
90
|
+
// See `close` above
|
|
91
|
+
await this.#drawTask;
|
|
92
|
+
// Propagate `writable` error to `readable`
|
|
93
|
+
this.#readableController.error(reason);
|
|
94
|
+
this.#counter.dispose();
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async #draw() {
|
|
99
|
+
// PERF: Draw every frame to minimize latency at cost of performance.
|
|
100
|
+
// When multiple frames are drawn in one vertical sync interval,
|
|
101
|
+
// only the last one is visible to users.
|
|
102
|
+
// But this ensures users can always see the most up-to-date screen.
|
|
103
|
+
// This is also the behavior of official Scrcpy client.
|
|
104
|
+
// https://github.com/Genymobile/scrcpy/issues/3679
|
|
105
|
+
let frame;
|
|
106
|
+
while ((frame = this.#nextFrame)) {
|
|
107
|
+
this.#nextFrame = undefined;
|
|
108
|
+
if (await this.#readableController.enqueue(frame)) {
|
|
109
|
+
// The consumer is responsible for closing `frame`
|
|
110
|
+
this.#counter.increaseFramesRendered();
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// `enqueue` returning `false` means the consumer has called `readable.cancel()`,
|
|
114
|
+
// before consuming the `frame`. So close it here.
|
|
115
|
+
frame.close();
|
|
116
|
+
// Continue looping to close all `#nextFrame`s
|
|
117
|
+
// In the mean time the `writable` is erroring out its source
|
|
118
|
+
// so no more frames will arrive
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.#drawTask = undefined;
|
|
122
|
+
}
|
|
123
|
+
#tryStartDrawing() {
|
|
124
|
+
if (!this.#drawTask) {
|
|
125
|
+
this.#drawTask = this.#draw();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
dispose() {
|
|
129
|
+
this.#captureFrame?.close();
|
|
130
|
+
this.#captureFrame = undefined;
|
|
131
|
+
this.#nextFrame?.close();
|
|
132
|
+
this.#nextFrame = undefined;
|
|
133
|
+
this.#counter.dispose();
|
|
134
|
+
tryClose(this.#readableController);
|
|
135
|
+
// Throw an error similar to native TransformStream
|
|
136
|
+
this.#writableController.error(new TypeError("The transform stream has been terminated"));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=flow-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-control.js","sourceRoot":"","sources":["../../../src/video/render/flow-control.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/video/render/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/video/render/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/video/render/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAE5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/video/render/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAE5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
import type { WritableStream } from "@yume-chan/stream-extra";
|
|
1
2
|
import type { VideoFrameRenderer } from "./type.js";
|
|
2
3
|
export declare class InsertableStreamVideoFrameRenderer implements VideoFrameRenderer {
|
|
3
4
|
#private;
|
|
4
5
|
static get isSupported(): boolean;
|
|
6
|
+
get type(): "hardware";
|
|
5
7
|
get element(): HTMLVideoElement;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
get options(): InsertableStreamVideoFrameRenderer.Options | undefined;
|
|
9
|
+
get writable(): WritableStream<VideoFrame>;
|
|
10
|
+
get stream(): MediaStream;
|
|
11
|
+
constructor(element?: HTMLVideoElement, options?: InsertableStreamVideoFrameRenderer.Options);
|
|
12
|
+
dispose(): undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare namespace InsertableStreamVideoFrameRenderer {
|
|
15
|
+
interface Options {
|
|
16
|
+
/**
|
|
17
|
+
* Whether to update the size of the video element when the size of the video frame changes.
|
|
18
|
+
*/
|
|
19
|
+
updateSize?: boolean;
|
|
20
|
+
}
|
|
9
21
|
}
|
|
10
22
|
//# sourceMappingURL=insertable-stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insertable-stream.d.ts","sourceRoot":"","sources":["../../../src/video/render/insertable-stream.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAQpD,qBAAa,kCAAmC,YAAW,kBAAkB;;IACzE,MAAM,KAAK,WAAW,YAErB;IAGD,IAAI,OAAO,qBAEV;
|
|
1
|
+
{"version":3,"file":"insertable-stream.d.ts","sourceRoot":"","sources":["../../../src/video/render/insertable-stream.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAQpD,qBAAa,kCAAmC,YAAW,kBAAkB;;IACzE,MAAM,KAAK,WAAW,YAErB;IAED,IAAI,IAAI,IACG,UAAU,CACpB;IAGD,IAAI,OAAO,qBAEV;IAGD,IAAI,OAAO,2DAEV;IAGD,IAAI,QAAQ,+BAEX;IAGD,IAAI,MAAM,gBAET;gBAKG,OAAO,CAAC,EAAE,gBAAgB,EAC1B,OAAO,CAAC,EAAE,kCAAkC,CAAC,OAAO;IAuCxD,OAAO,IAAI,SAAS;CAMvB;AAED,yBAAiB,kCAAkC,CAAC;IAChD,UAAiB,OAAO;QACpB;;WAEG;QACH,UAAU,CAAC,EAAE,OAAO,CAAC;KACxB;CACJ"}
|
|
@@ -3,14 +3,27 @@ export class InsertableStreamVideoFrameRenderer {
|
|
|
3
3
|
static get isSupported() {
|
|
4
4
|
return typeof MediaStreamTrackGenerator !== "undefined";
|
|
5
5
|
}
|
|
6
|
+
get type() {
|
|
7
|
+
return "hardware";
|
|
8
|
+
}
|
|
6
9
|
#element;
|
|
7
10
|
get element() {
|
|
8
11
|
return this.#element;
|
|
9
12
|
}
|
|
13
|
+
#options;
|
|
14
|
+
get options() {
|
|
15
|
+
return this.#options;
|
|
16
|
+
}
|
|
10
17
|
#generator;
|
|
11
|
-
|
|
18
|
+
get writable() {
|
|
19
|
+
return this.#generator.writable;
|
|
20
|
+
}
|
|
12
21
|
#stream;
|
|
13
|
-
|
|
22
|
+
get stream() {
|
|
23
|
+
return this.#stream;
|
|
24
|
+
}
|
|
25
|
+
#abortController = new AbortController();
|
|
26
|
+
constructor(element, options) {
|
|
14
27
|
if (element) {
|
|
15
28
|
this.#element = element;
|
|
16
29
|
}
|
|
@@ -22,24 +35,29 @@ export class InsertableStreamVideoFrameRenderer {
|
|
|
22
35
|
}
|
|
23
36
|
this.#element.muted = true;
|
|
24
37
|
this.#element.autoplay = true;
|
|
38
|
+
this.#element.playsInline = true;
|
|
25
39
|
this.#element.disablePictureInPicture = true;
|
|
26
40
|
this.#element.disableRemotePlayback = true;
|
|
41
|
+
this.#options = options;
|
|
42
|
+
if (options?.updateSize) {
|
|
43
|
+
this.#element.addEventListener("resize", () => {
|
|
44
|
+
this.#element.width = this.#element.videoWidth;
|
|
45
|
+
this.#element.height = this.#element.videoHeight;
|
|
46
|
+
}, { signal: this.#abortController.signal });
|
|
47
|
+
}
|
|
27
48
|
// The spec replaced `MediaStreamTrackGenerator` with `VideoTrackGenerator`.
|
|
28
49
|
// But Chrome has not implemented it yet.
|
|
29
50
|
// https://issues.chromium.org/issues/40058895
|
|
30
51
|
this.#generator = new MediaStreamTrackGenerator({ kind: "video" });
|
|
31
|
-
this.#
|
|
52
|
+
this.#generator.contentHint = "motion";
|
|
32
53
|
this.#stream = new MediaStream([this.#generator]);
|
|
33
54
|
this.#element.srcObject = this.#stream;
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
async draw(frame) {
|
|
42
|
-
await this.#writer.write(frame);
|
|
56
|
+
dispose() {
|
|
57
|
+
this.#abortController.abort();
|
|
58
|
+
this.#generator.stop();
|
|
59
|
+
this.#stream.removeTrack(this.#generator);
|
|
60
|
+
this.#element.srcObject = null;
|
|
43
61
|
}
|
|
44
62
|
}
|
|
45
63
|
//# sourceMappingURL=insertable-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insertable-stream.js","sourceRoot":"","sources":["../../../src/video/render/insertable-stream.ts"],"names":[],"mappings":"AAAA,4BAA4B;
|
|
1
|
+
{"version":3,"file":"insertable-stream.js","sourceRoot":"","sources":["../../../src/video/render/insertable-stream.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAY5B,MAAM,OAAO,kCAAkC;IAC3C,MAAM,KAAK,WAAW;QAClB,OAAO,OAAO,yBAAyB,KAAK,WAAW,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI;QACJ,OAAO,UAAmB,CAAC;IAC/B,CAAC;IAED,QAAQ,CAAmB;IAC3B,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,QAAQ,CAAyD;IACjE,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,UAAU,CAA4B;IACtC,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;IACpC,CAAC;IAED,OAAO,CAAc;IACrB,IAAI,MAAM;QACN,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,gBAAgB,GAAG,IAAI,eAAe,EAAE,CAAC;IAEzC,YACI,OAA0B,EAC1B,OAAoD;QAEpD,IAAI,OAAO,EAAE,CAAC;YACV,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC5B,CAAC;aAAM,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;YACzC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CACX,mEAAmE,CACtE,CAAC;QACN,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,QAAQ,CAAC,uBAAuB,GAAG,IAAI,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,qBAAqB,GAAG,IAAI,CAAC;QAE3C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;YACtB,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAC1B,QAAQ,EACR,GAAG,EAAE;gBACD,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC/C,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YACrD,CAAC,EACD,EAAE,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAC3C,CAAC;QACN,CAAC;QAED,4EAA4E;QAC5E,yCAAyC;QACzC,8CAA8C;QAC9C,IAAI,CAAC,UAAU,GAAG,IAAI,yBAAyB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,CAAC,WAAW,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC,OAAO,GAAG,IAAI,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC;IAC3C,CAAC;IAED,OAAO;QACH,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;IACnC,CAAC;CACJ"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
2
|
+
/**
|
|
3
|
+
* Manages drawing and redrawing of video frames.
|
|
4
|
+
*/
|
|
5
|
+
export declare class RedrawController {
|
|
6
|
+
#private;
|
|
7
|
+
get lastFrame(): VideoFrame | undefined;
|
|
8
|
+
constructor(draw: (frame: VideoFrame) => MaybePromiseLike<undefined>);
|
|
9
|
+
/**
|
|
10
|
+
* Draws a new frame.
|
|
11
|
+
*
|
|
12
|
+
* If a redraw is in progress, it waits for the redraw to finish before drawing the new frame.
|
|
13
|
+
*
|
|
14
|
+
* If a redraw is in progress and another one is in queue,
|
|
15
|
+
* it cancels the queued redraw and draws the new frame instead.
|
|
16
|
+
*
|
|
17
|
+
* @param frame A `VideoFrame` to draw. The frame will be closed after drawing.
|
|
18
|
+
*/
|
|
19
|
+
draw(frame: VideoFrame): Promise<undefined>;
|
|
20
|
+
/**
|
|
21
|
+
* Redraws the last drawn frame.
|
|
22
|
+
*
|
|
23
|
+
* If a draw or redraw is in progress, it waits for them to finish before redrawing.
|
|
24
|
+
*
|
|
25
|
+
* If a redraw is in progress and another one is in queue,
|
|
26
|
+
* it cancels the queued redraw and redraws the latest frame instead.
|
|
27
|
+
*/
|
|
28
|
+
redraw(): Promise<undefined>;
|
|
29
|
+
dispose(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=redraw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redraw.d.ts","sourceRoot":"","sources":["../../../src/video/render/redraw.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD;;GAEG;AACH,qBAAa,gBAAgB;;IAOzB,IAAI,SAAS,2BAEZ;gBAEW,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,gBAAgB,CAAC,SAAS,CAAC;IAIpE;;;;;;;;;OASG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU;IAgBtB;;;;;;;OAOG;IACG,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAwBlC,OAAO;CAQV"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { TaskQueue } from "@yume-chan/stream-extra";
|
|
2
|
+
/**
|
|
3
|
+
* Manages drawing and redrawing of video frames.
|
|
4
|
+
*/
|
|
5
|
+
export class RedrawController {
|
|
6
|
+
#draw;
|
|
7
|
+
#queue = new TaskQueue();
|
|
8
|
+
#pendingRedraw;
|
|
9
|
+
#lastFrame;
|
|
10
|
+
get lastFrame() {
|
|
11
|
+
return this.#lastFrame;
|
|
12
|
+
}
|
|
13
|
+
constructor(draw) {
|
|
14
|
+
this.#draw = draw;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Draws a new frame.
|
|
18
|
+
*
|
|
19
|
+
* If a redraw is in progress, it waits for the redraw to finish before drawing the new frame.
|
|
20
|
+
*
|
|
21
|
+
* If a redraw is in progress and another one is in queue,
|
|
22
|
+
* it cancels the queued redraw and draws the new frame instead.
|
|
23
|
+
*
|
|
24
|
+
* @param frame A `VideoFrame` to draw. The frame will be closed after drawing.
|
|
25
|
+
*/
|
|
26
|
+
draw(frame) {
|
|
27
|
+
this.#pendingRedraw?.abort();
|
|
28
|
+
this.#pendingRedraw = undefined;
|
|
29
|
+
this.#lastFrame?.close();
|
|
30
|
+
this.#lastFrame = frame.clone();
|
|
31
|
+
return this.#queue.enqueue(async () => {
|
|
32
|
+
try {
|
|
33
|
+
await this.#draw(frame);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
frame.close();
|
|
37
|
+
}
|
|
38
|
+
}, true);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Redraws the last drawn frame.
|
|
42
|
+
*
|
|
43
|
+
* If a draw or redraw is in progress, it waits for them to finish before redrawing.
|
|
44
|
+
*
|
|
45
|
+
* If a redraw is in progress and another one is in queue,
|
|
46
|
+
* it cancels the queued redraw and redraws the latest frame instead.
|
|
47
|
+
*/
|
|
48
|
+
async redraw() {
|
|
49
|
+
if (!this.#lastFrame || this.#pendingRedraw) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const abortController = new AbortController();
|
|
53
|
+
this.#pendingRedraw = abortController;
|
|
54
|
+
return await this.#queue.enqueue(async () => {
|
|
55
|
+
if (abortController.signal.aborted) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.#pendingRedraw = undefined;
|
|
59
|
+
const frame = this.#lastFrame.clone();
|
|
60
|
+
try {
|
|
61
|
+
await this.#draw(frame);
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
frame.close();
|
|
65
|
+
}
|
|
66
|
+
}, true);
|
|
67
|
+
}
|
|
68
|
+
dispose() {
|
|
69
|
+
this.#pendingRedraw?.abort();
|
|
70
|
+
this.#lastFrame?.close();
|
|
71
|
+
this.#lastFrame = undefined;
|
|
72
|
+
this.#queue.dispose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=redraw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redraw.js","sourceRoot":"","sources":["../../../src/video/render/redraw.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACzB,KAAK,CAAqD;IAE1D,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;IACzB,cAAc,CAA8B;IAE5C,UAAU,CAAyB;IACnC,IAAI,SAAS;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED,YAAY,IAAwD;QAChE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;;;;;;;;OASG;IACH,IAAI,CAAC,KAAiB;QAClB,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAEhC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAEhC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAwB,EAAE;YACtD,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;oBAAS,CAAC;gBACP,KAAK,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,CAAC;IACb,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM;QACR,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,OAAO;QACX,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,cAAc,GAAG,eAAe,CAAC;QAEtC,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAwB,EAAE;YAC5D,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjC,OAAO;YACX,CAAC;YAED,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;YAEhC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAW,CAAC,KAAK,EAAE,CAAC;YACvC,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;oBAAS,CAAC;gBACP,KAAK,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,CAAC;IACb,CAAC;IAED,OAAO;QACH,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC;QAE7B,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAE5B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;CACJ"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
2
|
+
import type { Event } from "@yume-chan/event";
|
|
3
|
+
import type { ScrcpyVideoDecoder } from "@yume-chan/scrcpy-decoder-shared";
|
|
4
|
+
import type { WritableStream } from "@yume-chan/stream-extra";
|
|
2
5
|
export interface VideoFrameRenderer {
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
readonly type: ScrcpyVideoDecoder.RendererType;
|
|
7
|
+
readonly onTypeChanged?: Event<ScrcpyVideoDecoder.RendererType>;
|
|
8
|
+
readonly writable: WritableStream<VideoFrame>;
|
|
9
|
+
snapshot?(options?: ImageEncodeOptions): Promise<Blob | undefined>;
|
|
10
|
+
dispose?(): MaybePromiseLike<undefined>;
|
|
5
11
|
}
|
|
6
12
|
//# sourceMappingURL=type.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/video/render/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/video/render/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC,YAAY,CAAC;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAEhE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAE9C,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;IAEnE,OAAO,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;CAC3C"}
|