init commit

This commit is contained in:
Carlos
2026-02-21 10:33:18 +01:00
parent c863a943ed
commit 9d955bf338
9512 changed files with 2015317 additions and 1305 deletions

View File

@@ -0,0 +1,27 @@
import type { MatroskaCue } from '../../containers/webm/seek/format-cues';
import type { MediaParserController } from '../../controller/media-parser-controller';
import type { PrefetchCache } from '../../fetch';
import type { MediaParserLogLevel } from '../../log';
import type { ParseMediaSrc } from '../../options';
import type { MediaParserReaderInterface } from '../../readers/reader';
import type { WebmSeekingHints } from '../../seeking-hints';
export declare const lazyCuesFetch: ({ controller, logLevel, readerInterface, src, prefetchCache, }: {
controller: MediaParserController;
logLevel: MediaParserLogLevel;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
prefetchCache: PrefetchCache;
}) => {
triggerLoad: (position: number, segmentOffset: number) => Promise<MatroskaCue[] | null>;
getLoadedCues: () => Promise<{
cues: MatroskaCue[];
segmentOffset: number;
} | null>;
getIfAlreadyLoaded: () => {
cues: MatroskaCue[];
segmentOffset: number;
} | null;
setFromSeekingHints: (hints: WebmSeekingHints) => void;
};
export type LazyCuesFetch = ReturnType<typeof lazyCuesFetch>;
export type LazyCuesLoadedOrNull = Awaited<ReturnType<LazyCuesFetch['getLoadedCues']>>;

View File

@@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazyCuesFetch = void 0;
const fetch_web_cues_1 = require("../../containers/webm/seek/fetch-web-cues");
const log_1 = require("../../log");
const lazyCuesFetch = ({ controller, logLevel, readerInterface, src, prefetchCache, }) => {
let prom = null;
let sOffset = null;
let result = null;
const triggerLoad = (position, segmentOffset) => {
if (result) {
return Promise.resolve(result);
}
if (prom) {
return prom;
}
if (sOffset && sOffset !== segmentOffset) {
throw new Error('Segment offset mismatch');
}
sOffset = segmentOffset;
log_1.Log.verbose(logLevel, 'Cues box found, trying to lazy load cues');
prom = (0, fetch_web_cues_1.fetchWebmCues)({
controller,
logLevel,
position,
readerInterface,
src,
prefetchCache,
}).then((cues) => {
log_1.Log.verbose(logLevel, 'Cues loaded');
result = cues;
return cues;
});
return prom;
};
const getLoadedCues = async () => {
if (!prom) {
return null;
}
if (result) {
if (!sOffset) {
throw new Error('Segment offset not set');
}
return {
cues: result,
segmentOffset: sOffset,
};
}
const cues = await prom;
if (!cues) {
return null;
}
if (!sOffset) {
throw new Error('Segment offset not set');
}
return {
cues,
segmentOffset: sOffset,
};
};
const getIfAlreadyLoaded = () => {
if (result) {
if (sOffset === null) {
throw new Error('Segment offset not set');
}
return {
cues: result,
segmentOffset: sOffset,
};
}
return null;
};
const setFromSeekingHints = (hints) => {
var _a, _b, _c, _d;
result = (_b = (_a = hints.loadedCues) === null || _a === void 0 ? void 0 : _a.cues) !== null && _b !== void 0 ? _b : null;
sOffset = (_d = (_c = hints.loadedCues) === null || _c === void 0 ? void 0 : _c.segmentOffset) !== null && _d !== void 0 ? _d : null;
};
return {
triggerLoad,
getLoadedCues,
getIfAlreadyLoaded,
setFromSeekingHints,
};
};
exports.lazyCuesFetch = lazyCuesFetch;

View File

@@ -0,0 +1,55 @@
import type { AvcProfileInfo } from '../../containers/avc/parse-avc';
import type { OnTrackEntrySegment } from '../../containers/webm/segments';
import type { TrackInfo } from '../../containers/webm/segments/track-entry';
import type { MediaParserController } from '../../controller/media-parser-controller';
import type { PrefetchCache } from '../../fetch';
import type { BufferIterator } from '../../iterator/buffer-iterator';
import type { MediaParserLogLevel } from '../../log';
import type { ParseMediaSrc } from '../../options';
import type { MediaParserReaderInterface } from '../../readers/reader';
export type SegmentSection = {
start: number;
size: number;
index: number;
};
export type ClusterSection = {
start: number;
size: number;
segment: number;
};
export declare const webmState: ({ controller, logLevel, readerInterface, src, prefetchCache, }: {
controller: MediaParserController;
logLevel: MediaParserLogLevel;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
prefetchCache: PrefetchCache;
}) => {
cues: {
triggerLoad: (position: number, segmentOffset: number) => Promise<import("../../containers/webm/seek/format-cues").MatroskaCue[] | null>;
getLoadedCues: () => Promise<{
cues: import("../../containers/webm/seek/format-cues").MatroskaCue[];
segmentOffset: number;
} | null>;
getIfAlreadyLoaded: () => {
cues: import("../../containers/webm/seek/format-cues").MatroskaCue[];
segmentOffset: number;
} | null;
setFromSeekingHints: (hints: import("../../seeking-hints").WebmSeekingHints) => void;
};
onTrackEntrySegment: OnTrackEntrySegment;
getTrackInfoByNumber: (id: number) => TrackInfo;
setTimestampOffset: (byteOffset: number, timestamp: number) => void;
getTimestampOffsetForByteOffset: (byteOffset: number) => number | undefined;
getTimeStampMapForSeekingHints: () => Map<number, number>;
setTimeStampMapForSeekingHints: (newTimestampMap: Map<number, number>) => void;
getTimescale: () => number;
setTimescale: (newTimescale: number) => void;
addSegment: (seg: Omit<SegmentSection, "index">) => void;
addCluster: (cluster: ClusterSection) => void;
getFirstCluster: () => ClusterSection | undefined;
isInsideSegment: (iterator: BufferIterator) => SegmentSection | null;
isInsideCluster: (offset: number) => ClusterSection | null;
setAvcProfileForTrackNumber: (trackNumber: number, avcProfile: AvcProfileInfo) => void;
getAvcProfileForTrackNumber: (trackNumber: number) => AvcProfileInfo | null;
};
export type WebmState = ReturnType<typeof webmState>;

View File

@@ -0,0 +1,130 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.webmState = void 0;
const traversal_1 = require("../../containers/webm/traversal");
const lazy_cues_fetch_1 = require("./lazy-cues-fetch");
const webmState = ({ controller, logLevel, readerInterface, src, prefetchCache, }) => {
const trackEntries = {};
const onTrackEntrySegment = (trackEntry) => {
var _a;
const trackId = (0, traversal_1.getTrackId)(trackEntry);
if (!trackId) {
throw new Error('Expected track id');
}
if (trackEntries[trackId]) {
return;
}
const codec = (0, traversal_1.getTrackCodec)(trackEntry);
if (!codec) {
throw new Error('Expected codec');
}
const trackTimescale = (0, traversal_1.getTrackTimestampScale)(trackEntry);
trackEntries[trackId] = {
codec: codec.value,
trackTimescale: (_a = trackTimescale === null || trackTimescale === void 0 ? void 0 : trackTimescale.value) !== null && _a !== void 0 ? _a : null,
};
};
let timestampMap = new Map();
const getTimestampOffsetForByteOffset = (byteOffset) => {
const entries = Array.from(timestampMap.entries());
const sortedByByteOffset = entries
.sort((a, b) => {
return a[0] - b[0];
})
.reverse();
for (const [offset, timestamp] of sortedByByteOffset) {
if (offset >= byteOffset) {
continue;
}
return timestamp;
}
return timestampMap.get(byteOffset);
};
const setTimestampOffset = (byteOffset, timestamp) => {
timestampMap.set(byteOffset, timestamp);
};
let timescale = null;
const setTimescale = (newTimescale) => {
timescale = newTimescale;
};
const getTimescale = () => {
// https://www.matroska.org/technical/notes.html
// When using the default value of TimestampScale of “1,000,000”, one Segment Tick represents one millisecond.
if (timescale === null) {
return 1000000;
}
return timescale;
};
const segments = [];
const clusters = [];
const avcProfilesMap = {};
const setAvcProfileForTrackNumber = (trackNumber, avcProfile) => {
avcProfilesMap[trackNumber] = avcProfile;
};
const getAvcProfileForTrackNumber = (trackNumber) => {
var _a;
return (_a = avcProfilesMap[trackNumber]) !== null && _a !== void 0 ? _a : null;
};
const cues = (0, lazy_cues_fetch_1.lazyCuesFetch)({
controller,
logLevel,
readerInterface,
src,
prefetchCache,
});
const getTimeStampMapForSeekingHints = () => {
return timestampMap;
};
const setTimeStampMapForSeekingHints = (newTimestampMap) => {
timestampMap = newTimestampMap;
};
return {
cues,
onTrackEntrySegment,
getTrackInfoByNumber: (id) => trackEntries[id],
setTimestampOffset,
getTimestampOffsetForByteOffset,
getTimeStampMapForSeekingHints,
setTimeStampMapForSeekingHints,
getTimescale,
setTimescale,
addSegment: (seg) => {
const segment = {
...seg,
index: segments.length,
};
segments.push(segment);
},
addCluster: (cluster) => {
const exists = clusters.some((existingCluster) => existingCluster.start === cluster.start);
if (!exists) {
clusters.push(cluster);
}
},
getFirstCluster: () => {
return clusters.find((cluster) => cluster.segment === 0);
},
isInsideSegment: (iterator) => {
var _a;
const offset = iterator.counter.getOffset();
const insideClusters = segments.filter((cluster) => {
return (offset >= cluster.start && offset <= cluster.start + cluster.size);
});
if (insideClusters.length > 1) {
throw new Error('Expected to only be inside 1 cluster');
}
return (_a = insideClusters[0]) !== null && _a !== void 0 ? _a : null;
},
isInsideCluster: (offset) => {
for (const cluster of clusters) {
if (offset >= cluster.start && offset < cluster.start + cluster.size) {
return cluster;
}
}
return null;
},
setAvcProfileForTrackNumber,
getAvcProfileForTrackNumber,
};
};
exports.webmState = webmState;