pgs.js
Version:
PGS (Presentation Graphic Stream) Subtitle For HTML5 Media Playback
80 lines (67 loc) • 2.6 kB
text/typescript
import { AcquisitionPoint, DisplaySet, TimestampedSegment } from '../../../pgs/type'
import { AcquisitionPointForRender, AcquisitionPointNotRendered, AcquisitionPointRenderedImageBitmap } from '../render';
import PGSFeeder, { PGSFeederOption } from './feeder';
export default class AsyncPGSSupFeeder implements PGSFeeder {
private option: PGSFeederOption;
private acquisitions: Readonly<AcquisitionPointForRender>[] = [];
private donePromise: Promise<boolean>;
public constructor(stream: ReadableStream<ArrayBufferView | ArrayBuffer>, option?: Partial<PGSFeederOption>) {
this.option = {
preload: 'none',
timeshift: 0,
... option
};
this.donePromise = new Promise((resolve) => {
this.prepare(stream, resolve);
})
}
private async prepare(stream: ReadableStream<ArrayBufferView | ArrayBuffer>, resolve?: (result: boolean) => void) {
const iterator = DisplaySet.aggregateAsync(TimestampedSegment.iterateSupFormatAsync(stream));
switch (this.option.preload) {
case 'decode':
for await (const acquisition of AcquisitionPointNotRendered.iterateAsync(AcquisitionPoint.iterateAsync(iterator, true))) {
this.acquisitions.push(acquisition);
}
break;
case 'render':
for await (const acquisition of AcquisitionPointRenderedImageBitmap.iterateAsync(AcquisitionPoint.iterateAsync(iterator, true))) {
this.acquisitions.push(acquisition);
}
break;
case 'none':
default:
for await (const acquisition of AcquisitionPointNotRendered.iterateAsync(AcquisitionPoint.iterateAsync(iterator, false))) {
this.acquisitions.push(acquisition);
}
break;
}
resolve?.(true);
}
public get done(): Promise<boolean> {
return this.donePromise;
}
public content(time: number): Readonly<AcquisitionPointForRender> | null {
time -= this.option.timeshift;
{
const first = this.acquisitions[0];
if (!first) { return null; }
const pts = first.pts / first.timescale;
if (time < pts) { return null; }
}
let begin = 0, end = this.acquisitions.length;
while (begin + 1 < end) {
const middle = Math.floor((begin + end) / 2);
const middle_obj = this.acquisitions[middle];
const middle_pts = middle_obj.pts / middle_obj.timescale;
if (middle_pts <= time) {
begin = middle;
} else {
end = middle;
}
}
return this.acquisitions[begin] ?? null;
}
public onattach(): void {}
public ondetach(): void {}
public onseek(): void {}
}