UNPKG

@yume-chan/scrcpy-decoder-tinyh264

Version:

Raw H.264 stream decoder and renderer wrapping TinyH264 and YUVCanvas packages (software decoding).

77 lines 2.39 kB
import { PromiseResolver } from "@yume-chan/async"; import { AutoDisposable, EventEmitter } from "@yume-chan/event"; let worker; let workerReady = false; const pendingResolvers = []; let streamId = 0; const PICTURE_READY_SUBSCRIPTIONS = new Map(); function subscribePictureReady(streamId, handler) { PICTURE_READY_SUBSCRIPTIONS.set(streamId, handler); return { dispose() { PICTURE_READY_SUBSCRIPTIONS.delete(streamId); }, }; } export class TinyH264Wrapper extends AutoDisposable { streamId; #pictureReadyEvent = new EventEmitter(); get onPictureReady() { return this.#pictureReadyEvent.event; } constructor(streamId) { super(); this.streamId = streamId; this.addDisposable(subscribePictureReady(streamId, this.#handlePictureReady)); } #handlePictureReady = (e) => { this.#pictureReadyEvent.fire(e); }; feed(data) { worker.postMessage({ type: "decode", data: data, offset: 0, length: data.byteLength, renderStateId: this.streamId, }, [data]); } dispose() { super.dispose(); worker.postMessage({ type: "release", renderStateId: this.streamId, }); } } export function createTinyH264Wrapper() { if (!worker) { worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module", }); worker.addEventListener("message", ({ data }) => { switch (data.type) { case "decoderReady": workerReady = true; for (const resolver of pendingResolvers) { resolver.resolve(new TinyH264Wrapper(streamId)); streamId += 1; } pendingResolvers.length = 0; break; case "pictureReady": PICTURE_READY_SUBSCRIPTIONS.get(data.renderStateId)?.(data); break; } }); } if (!workerReady) { const resolver = new PromiseResolver(); pendingResolvers.push(resolver); return resolver.promise; } const decoder = new TinyH264Wrapper(streamId); streamId += 1; return Promise.resolve(decoder); } //# sourceMappingURL=wrapper.js.map