mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 18:29:23 +02:00
refactor(scrcpy): move decoders to own packages
This commit is contained in:
parent
db079b4292
commit
88a3c10aa0
56 changed files with 3002 additions and 2685 deletions
98
libraries/scrcpy-decoder-webcodecs/src/index.ts
Normal file
98
libraries/scrcpy-decoder-webcodecs/src/index.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
import type { H264Configuration, ScrcpyVideoStreamPacket } from '@yume-chan/scrcpy';
|
||||
import { WritableStream } from '@yume-chan/stream-extra';
|
||||
|
||||
function toHex(value: number) {
|
||||
return value.toString(16).padStart(2, '0').toUpperCase();
|
||||
}
|
||||
|
||||
export class WebCodecsDecoder {
|
||||
// Usually, browsers can decode most configurations,
|
||||
// So let device choose best profile and level for itself.
|
||||
public readonly maxProfile = undefined;
|
||||
public readonly maxLevel = undefined;
|
||||
|
||||
private _writable: WritableStream<ScrcpyVideoStreamPacket>;
|
||||
public get writable() { return this._writable; }
|
||||
|
||||
private _renderer: HTMLCanvasElement;
|
||||
public get renderer() { return this._renderer; }
|
||||
|
||||
private _frameRendered = 0;
|
||||
public get frameRendered() { return this._frameRendered; }
|
||||
|
||||
private context: CanvasRenderingContext2D;
|
||||
private decoder: VideoDecoder;
|
||||
|
||||
// Limit FPS to system refresh rate
|
||||
private lastFrame: VideoFrame | undefined;
|
||||
private animationFrame: number = 0;
|
||||
|
||||
public constructor() {
|
||||
this._renderer = document.createElement('canvas');
|
||||
|
||||
this.context = this._renderer.getContext('2d')!;
|
||||
this.decoder = new VideoDecoder({
|
||||
output: (frame) => {
|
||||
if (this.lastFrame) {
|
||||
this.lastFrame.close();
|
||||
}
|
||||
this.lastFrame = frame;
|
||||
|
||||
if (!this.animationFrame) {
|
||||
// Start render loop on first frame
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
error() { },
|
||||
});
|
||||
|
||||
this._writable = new WritableStream<ScrcpyVideoStreamPacket>({
|
||||
write: async (packet) => {
|
||||
switch (packet.type) {
|
||||
case 'configuration':
|
||||
this.configure(packet.data);
|
||||
break;
|
||||
case 'frame':
|
||||
this.decoder.decode(new EncodedVideoChunk({
|
||||
// Treat `undefined` as `key`, otherwise won't decode.
|
||||
type: packet.keyframe === false ? 'delta' : 'key',
|
||||
timestamp: 0,
|
||||
data: packet.data,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private render = () => {
|
||||
if (this.lastFrame) {
|
||||
this._frameRendered += 1;
|
||||
this.context.drawImage(this.lastFrame, 0, 0);
|
||||
this.lastFrame.close();
|
||||
this.lastFrame = undefined;
|
||||
}
|
||||
|
||||
this.animationFrame = requestAnimationFrame(this.render);
|
||||
};
|
||||
|
||||
private configure(config: H264Configuration) {
|
||||
const { profileIndex, constraintSet, levelIndex } = config;
|
||||
|
||||
this._renderer.width = config.croppedWidth;
|
||||
this._renderer.height = config.croppedHeight;
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc6381#section-3.3
|
||||
// ISO Base Media File Format Name Space
|
||||
const codec = `avc1.${[profileIndex, constraintSet, levelIndex].map(toHex).join('')}`;
|
||||
this.decoder.configure({
|
||||
codec: codec,
|
||||
optimizeForLatency: true,
|
||||
});
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
cancelAnimationFrame(this.animationFrame);
|
||||
this.decoder.close();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue