libpgs
Version:
Renderer for graphical subtitles (PGS) in the browser.
98 lines (87 loc) • 4.02 kB
text/typescript
import {BigEndianBinaryReader} from "../utils/bigEndianBinaryReader";
import {PresentationCompositionSegment} from "./presentationCompositionSegment";
import {PaletteDefinitionSegment} from "./paletteDefinitionSegment";
import {ObjectDefinitionSegment} from "./objectDefinitionSegment";
import {WindowDefinitionSegment} from "./windowDefinitionSegment";
import {SegmentType} from "./segmentType";
import {AsyncBinaryReader} from "../utils/asyncBinaryReader";
/**
* The PGS display set holds all data for the current subtitle update at a given timestamp.
*/
export class DisplaySet {
public presentationTimestamp: number = 0;
public decodingTimestamp: number = 0;
public presentationComposition?: PresentationCompositionSegment;
public paletteDefinitions: PaletteDefinitionSegment[] = [];
public objectDefinitions: ObjectDefinitionSegment[] = [];
public windowDefinitions: WindowDefinitionSegment[] = [];
/**
* Reads a display set from the given binary reader. The current data is cleared.
* @param reader The binary reader to read from.
* @param includeHeader If true, the magic-number and timestamps are read. If false, reading starts at the first
* segment.
*/
public async read(reader: BigEndianBinaryReader, includeHeader: boolean) {
// Clear
this.presentationTimestamp = 0;
this.decodingTimestamp = 0;
this.presentationComposition = undefined;
this.paletteDefinitions = [];
this.objectDefinitions = [];
this.windowDefinitions = [];
// Handles async readers
let asyncReader: AsyncBinaryReader | undefined = undefined;
if ('requestData' in reader.baseReader) {
asyncReader = reader.baseReader as AsyncBinaryReader;
}
while (true)
{
let presentationTimestamp: number = 0;
let decodingTimestamp: number = 0;
// The header is included before every segment. Even for the end segment.
if (includeHeader)
{
await asyncReader?.requestData(10);
const magicNumber = reader.readUInt16();
if (magicNumber != 0x5047) {
throw new Error("Invalid magic number!");
}
presentationTimestamp = reader.readUInt32();
decodingTimestamp = reader.readUInt32();
}
await asyncReader?.requestData(3);
const type = reader.readUInt8();
const size = reader.readUInt16()
await asyncReader?.requestData(size);
switch (type) {
case SegmentType.paletteDefinition:
const pds = new PaletteDefinitionSegment();
pds.read(reader, size);
this.paletteDefinitions.push(pds);
break;
case SegmentType.objectDefinition:
const ods = new ObjectDefinitionSegment();
ods.read(reader, size);
this.objectDefinitions.push(ods);
break;
case SegmentType.presentationComposition:
const pcs = new PresentationCompositionSegment();
pcs.read(reader, size);
this.presentationComposition = pcs;
// SubtitleEdit only writes the relevant timestamp to the PCS.
this.presentationTimestamp = presentationTimestamp;
this.decodingTimestamp = decodingTimestamp;
break;
case SegmentType.windowDefinition:
const wds = new WindowDefinitionSegment();
wds.read(reader, size);
this.windowDefinitions.push(wds);
break;
case SegmentType.end:
return;
default:
throw new Error(`Unsupported segment type ${type}`);
}
}
}
}