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,25 @@
import type { SamplePosition } from '../../get-sample-positions';
import type { ParserState } from '../parser-state';
type TrackIdAndSamplePositions = {
trackId: number;
samplePositions: SamplePosition[];
};
export declare const calculateSamplePositions: ({ state, mediaSectionStart, trackIds, }: {
state: ParserState;
mediaSectionStart: number;
trackIds: number[];
}) => TrackIdAndSamplePositions[];
export declare const cachedSamplePositionsState: () => {
getSamples: (mdatStart: number) => TrackIdAndSamplePositions[] | null;
setSamples: (mdatStart: number, samples: TrackIdAndSamplePositions[]) => void;
setCurrentSampleIndex: (mdatStart: number, trackId: number, index: number) => void;
getCurrentSampleIndices: (mdatStart: number) => Record<number, number>;
updateAfterSeek: (seekedByte: number) => void;
};
type Lowest = {
samplePosition: SamplePosition;
trackId: number;
index: number;
};
export declare const getSampleWithLowestDts: (samplePositions: TrackIdAndSamplePositions[], currentSampleIndexMap: Record<number, number>) => Lowest[];
export {};

View File

@@ -0,0 +1,137 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSampleWithLowestDts = exports.cachedSamplePositionsState = exports.calculateSamplePositions = void 0;
const are_samples_complete_1 = require("../../containers/iso-base-media/are-samples-complete");
const get_sample_positions_from_track_1 = require("../../containers/iso-base-media/get-sample-positions-from-track");
const traversal_1 = require("../../containers/iso-base-media/traversal");
const get_tracks_1 = require("../../get-tracks");
const log_1 = require("../../log");
const precomputed_tfra_1 = require("./precomputed-tfra");
const calculateSamplePositions = ({ state, mediaSectionStart, trackIds, }) => {
var _a, _b;
const tracks = (0, get_tracks_1.getTracks)(state, true);
const moofBoxes = (0, traversal_1.getMoofBoxes)(state.structure.getIsoStructure().boxes);
const tfraBoxes = (0, precomputed_tfra_1.deduplicateTfraBoxesByOffset)([
...state.iso.tfra.getTfraBoxes(),
...(0, traversal_1.getTfraBoxes)(state.structure.getIsoStructure().boxes),
]);
const moofComplete = (0, are_samples_complete_1.areSamplesComplete)({ moofBoxes, tfraBoxes });
const relevantMoofBox = moofBoxes.find((moofBox) => moofBox.offset + moofBox.size + 8 === mediaSectionStart);
if (moofBoxes.length > 0 && !relevantMoofBox) {
throw new Error('No relevant moof box found');
}
const moov = (0, traversal_1.getMoovBoxFromState)({
structureState: state.structure,
isoState: state.iso,
mp4HeaderSegment: (_b = (_a = state.m3uPlaylistContext) === null || _a === void 0 ? void 0 : _a.mp4HeaderSegment) !== null && _b !== void 0 ? _b : null,
mayUsePrecomputed: true,
});
if (!moov) {
throw new Error('No moov box found');
}
const trackIdAndSamplePositions = [];
for (const track of tracks) {
const trakBox = (0, traversal_1.getTrakBoxByTrackId)(moov, track.trackId);
if (!trackIds.includes(track.trackId)) {
log_1.Log.verbose(state.logLevel, 'Skipping calculating sample positions for track', track.trackId);
continue;
}
if (!trakBox) {
throw new Error('No trak box found');
}
const { samplePositions } = (0, get_sample_positions_from_track_1.getSamplePositionsFromTrack)({
trakBox,
moofBoxes: relevantMoofBox ? [relevantMoofBox] : [],
moofComplete,
trexBoxes: (0, traversal_1.getTrexBoxes)(moov),
});
trackIdAndSamplePositions.push({
trackId: track.trackId,
samplePositions,
});
}
return trackIdAndSamplePositions;
};
exports.calculateSamplePositions = calculateSamplePositions;
const updateSampleIndicesAfterSeek = ({ samplePositionsForMdatStart, seekedByte, }) => {
const currentSampleIndices = {};
const keys = Object.keys(samplePositionsForMdatStart).map(Number).sort();
const mdat = keys.find((key) => seekedByte >= key);
if (!mdat) {
return currentSampleIndices;
}
const samplePositions = samplePositionsForMdatStart[mdat];
if (!samplePositions) {
return currentSampleIndices;
}
for (const track of samplePositions) {
const currentSampleIndex = track.samplePositions.findIndex((sample) => sample.offset >= seekedByte);
if (!currentSampleIndices[mdat]) {
currentSampleIndices[mdat] = {};
}
if (!currentSampleIndices[mdat][track.trackId]) {
currentSampleIndices[mdat][track.trackId] = 0;
}
if (currentSampleIndex === -1) {
currentSampleIndices[mdat][track.trackId] = track.samplePositions.length;
}
else {
currentSampleIndices[mdat][track.trackId] = currentSampleIndex;
}
}
return currentSampleIndices;
};
const cachedSamplePositionsState = () => {
// offset -> sample positions
const samplePositionsForMdatStart = {};
let currentSampleIndex = {};
return {
getSamples: (mdatStart) => {
var _a;
return (_a = samplePositionsForMdatStart[mdatStart]) !== null && _a !== void 0 ? _a : null;
},
setSamples: (mdatStart, samples) => {
samplePositionsForMdatStart[mdatStart] = samples;
},
setCurrentSampleIndex: (mdatStart, trackId, index) => {
if (!currentSampleIndex[mdatStart]) {
currentSampleIndex[mdatStart] = {};
}
if (!currentSampleIndex[mdatStart][trackId]) {
currentSampleIndex[mdatStart][trackId] = 0;
}
currentSampleIndex[mdatStart][trackId] = index;
},
getCurrentSampleIndices: (mdatStart) => {
var _a;
return (_a = currentSampleIndex[mdatStart]) !== null && _a !== void 0 ? _a : {};
},
updateAfterSeek: (seekedByte) => {
currentSampleIndex = updateSampleIndicesAfterSeek({
samplePositionsForMdatStart,
seekedByte,
});
},
};
};
exports.cachedSamplePositionsState = cachedSamplePositionsState;
const getSampleWithLowestDts = (samplePositions, currentSampleIndexMap) => {
var _a;
const lowestDts = [];
for (const track of samplePositions) {
const currentSampleIndex = (_a = currentSampleIndexMap[track.trackId]) !== null && _a !== void 0 ? _a : 0;
const currentSample = track.samplePositions[currentSampleIndex];
if (currentSample &&
(lowestDts.length === 0 ||
currentSample.decodingTimestamp <=
lowestDts[0].samplePosition.decodingTimestamp)) {
lowestDts.push({
samplePosition: currentSample,
trackId: track.trackId,
index: currentSampleIndex,
});
}
}
return lowestDts;
};
exports.getSampleWithLowestDts = getSampleWithLowestDts;

View File

@@ -0,0 +1,55 @@
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';
export declare const isoBaseMediaState: ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }: {
contentLength: number;
controller: MediaParserController;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
logLevel: MediaParserLogLevel;
prefetchCache: PrefetchCache;
}) => {
flatSamples: {
getSamples: (mdatStart: number) => {
trackId: number;
samplePositions: import("../../get-sample-positions").SamplePosition[];
}[] | null;
setSamples: (mdatStart: number, samples: {
trackId: number;
samplePositions: import("../../get-sample-positions").SamplePosition[];
}[]) => void;
setCurrentSampleIndex: (mdatStart: number, trackId: number, index: number) => void;
getCurrentSampleIndices: (mdatStart: number) => Record<number, number>;
updateAfterSeek: (seekedByte: number) => void;
};
moov: {
setMoovBox: (moov: {
moovBox: import("../../containers/iso-base-media/moov/moov").MoovBox;
precomputed: boolean;
}) => void;
getMoovBoxAndPrecomputed: () => {
moovBox: import("../../containers/iso-base-media/moov/moov").MoovBox;
precomputed: boolean;
} | null;
};
mfra: {
triggerLoad: () => Promise<import("../../containers/iso-base-media/base-media-box").IsoBaseMediaBox[] | null>;
getIfAlreadyLoaded: () => import("../../containers/iso-base-media/base-media-box").IsoBaseMediaBox[] | null;
setFromSeekingHints: (hints: import("../../seeking-hints").IsoBaseMediaSeekingHints) => void;
};
moof: {
getMoofBoxes: () => import("./precomputed-moof").MoofBox[];
setMoofBoxes: (boxes: import("./precomputed-moof").MoofBox[]) => void;
};
tfra: {
getTfraBoxes: () => import("../../containers/iso-base-media/mfra/tfra").TfraBox[];
setTfraBoxes: (boxes: import("../../containers/iso-base-media/mfra/tfra").TfraBox[]) => void;
};
movieTimeScale: {
getTrackTimescale: () => number | null;
setTrackTimescale: (timescale: number) => void;
};
};
export type IsoBaseMediaState = ReturnType<typeof isoBaseMediaState>;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isoBaseMediaState = void 0;
const cached_sample_positions_1 = require("./cached-sample-positions");
const lazy_mfra_load_1 = require("./lazy-mfra-load");
const moov_box_1 = require("./moov-box");
const precomputed_moof_1 = require("./precomputed-moof");
const precomputed_tfra_1 = require("./precomputed-tfra");
const timescale_state_1 = require("./timescale-state");
const isoBaseMediaState = ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }) => {
return {
flatSamples: (0, cached_sample_positions_1.cachedSamplePositionsState)(),
moov: (0, moov_box_1.moovState)(),
mfra: (0, lazy_mfra_load_1.lazyMfraLoad)({
contentLength,
controller,
readerInterface,
src,
logLevel,
prefetchCache,
}),
moof: (0, precomputed_moof_1.precomputedMoofState)(),
tfra: (0, precomputed_tfra_1.precomputedTfraState)(),
movieTimeScale: (0, timescale_state_1.movieTimeScaleState)(),
};
};
exports.isoBaseMediaState = isoBaseMediaState;

View File

@@ -0,0 +1,3 @@
import type { IsoBaseMediaBox } from '../../containers/iso-base-media/base-media-box';
export declare const getLastMoofBox: (boxes: IsoBaseMediaBox[]) => number | null | undefined;
export declare const getMaxFirstMoofOffset: (boxes: IsoBaseMediaBox[]) => number;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMaxFirstMoofOffset = exports.getLastMoofBox = void 0;
const truthy_1 = require("../../truthy");
const getLastMoofBox = (boxes) => {
if (boxes) {
const tfras = boxes.filter((b) => b.type === 'tfra-box');
const lastMoofOffsets = tfras.map((f) => {
if (f.entries.length <= 1) {
return null;
}
return f.entries[f.entries.length - 1].moofOffset;
});
if (lastMoofOffsets.length > 0) {
const maxOffset = Math.max(...lastMoofOffsets.filter(truthy_1.truthy));
return maxOffset;
}
return null;
}
};
exports.getLastMoofBox = getLastMoofBox;
const getMaxFirstMoofOffset = (boxes) => {
const tfras = boxes.filter((b) => b.type === 'tfra-box');
const firstMoofOffsets = tfras.map((f) => {
return f.entries[0].moofOffset;
});
return Math.max(...firstMoofOffsets.filter(truthy_1.truthy));
};
exports.getMaxFirstMoofOffset = getMaxFirstMoofOffset;

View File

@@ -0,0 +1,19 @@
import type { IsoBaseMediaBox } from '../../containers/iso-base-media/base-media-box';
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 { IsoBaseMediaSeekingHints } from '../../seeking-hints';
export declare const lazyMfraLoad: ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }: {
contentLength: number;
controller: MediaParserController;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
logLevel: MediaParserLogLevel;
prefetchCache: PrefetchCache;
}) => {
triggerLoad: () => Promise<IsoBaseMediaBox[] | null>;
getIfAlreadyLoaded: () => IsoBaseMediaBox[] | null;
setFromSeekingHints: (hints: IsoBaseMediaSeekingHints) => void;
};

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazyMfraLoad = void 0;
const get_mfra_seeking_box_1 = require("../../containers/iso-base-media/get-mfra-seeking-box");
const log_1 = require("../../log");
const lazyMfraLoad = ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }) => {
let prom = null;
let result = null;
const triggerLoad = () => {
if (prom) {
return prom;
}
log_1.Log.verbose(logLevel, 'Moof box found, trying to lazy load mfra');
prom = (0, get_mfra_seeking_box_1.getMfraSeekingBox)({
contentLength,
controller,
readerInterface,
src,
logLevel,
prefetchCache,
}).then((boxes) => {
log_1.Log.verbose(logLevel, boxes ? 'Lazily found mfra atom.' : 'No mfra atom found.');
result = boxes;
return boxes;
});
return prom;
};
const getIfAlreadyLoaded = () => {
if (result) {
return result;
}
return null;
};
const setFromSeekingHints = (hints) => {
result = hints.mfraAlreadyLoaded;
};
return {
triggerLoad,
getIfAlreadyLoaded,
setFromSeekingHints,
};
};
exports.lazyMfraLoad = lazyMfraLoad;

View File

@@ -0,0 +1,10 @@
import type { MoovBox } from '../../containers/iso-base-media/moov/moov';
type MoovBoxAndPrecomputed = {
moovBox: MoovBox;
precomputed: boolean;
};
export declare const moovState: () => {
setMoovBox: (moov: MoovBoxAndPrecomputed) => void;
getMoovBoxAndPrecomputed: () => MoovBoxAndPrecomputed | null;
};
export {};

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.moovState = void 0;
const moovState = () => {
let moovBox = null;
return {
setMoovBox: (moov) => {
moovBox = moov;
},
getMoovBoxAndPrecomputed: () => moovBox,
};
};
exports.moovState = moovState;

View File

@@ -0,0 +1,12 @@
import type { IsoBaseMediaBox } from '../../containers/iso-base-media/base-media-box';
export type MoofBox = {
offset: number;
size: number;
trafBoxes: IsoBaseMediaBox[];
};
export declare const precomputedMoofState: () => {
getMoofBoxes: () => MoofBox[];
setMoofBoxes: (boxes: MoofBox[]) => void;
};
export declare const toMoofBox: (box: IsoBaseMediaBox) => MoofBox;
export declare const deduplicateMoofBoxesByOffset: (moofBoxes: MoofBox[]) => MoofBox[];

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deduplicateMoofBoxesByOffset = exports.toMoofBox = exports.precomputedMoofState = void 0;
// Note: May be duplicated!
const precomputedMoofState = () => {
let moofBoxes = [];
return {
getMoofBoxes: () => moofBoxes,
setMoofBoxes: (boxes) => {
moofBoxes = boxes;
},
};
};
exports.precomputedMoofState = precomputedMoofState;
const toMoofBox = (box) => {
if (box.type !== 'regular-box') {
throw new Error('expected regular bpx');
}
return {
offset: box.offset,
trafBoxes: box.children.filter((c) => c.type === 'regular-box' && c.boxType === 'traf'),
size: box.boxSize,
};
};
exports.toMoofBox = toMoofBox;
const deduplicateMoofBoxesByOffset = (moofBoxes) => {
return moofBoxes.filter((m, i, arr) => i === arr.findIndex((t) => t.offset === m.offset));
};
exports.deduplicateMoofBoxesByOffset = deduplicateMoofBoxesByOffset;

View File

@@ -0,0 +1,6 @@
import type { TfraBox } from '../../containers/iso-base-media/mfra/tfra';
export declare const precomputedTfraState: () => {
getTfraBoxes: () => TfraBox[];
setTfraBoxes: (boxes: TfraBox[]) => void;
};
export declare const deduplicateTfraBoxesByOffset: (tfraBoxes: TfraBox[]) => TfraBox[];

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deduplicateTfraBoxesByOffset = exports.precomputedTfraState = void 0;
const precomputedTfraState = () => {
let tfraBoxes = [];
return {
getTfraBoxes: () => tfraBoxes,
setTfraBoxes: (boxes) => {
tfraBoxes = boxes;
},
};
};
exports.precomputedTfraState = precomputedTfraState;
const deduplicateTfraBoxesByOffset = (tfraBoxes) => {
return tfraBoxes.filter((m, i, arr) => i === arr.findIndex((t) => t.offset === m.offset));
};
exports.deduplicateTfraBoxesByOffset = deduplicateTfraBoxesByOffset;

View File

@@ -0,0 +1,5 @@
export declare const movieTimeScaleState: () => {
getTrackTimescale: () => number | null;
setTrackTimescale: (timescale: number) => void;
};
export type MovieTimeScaleState = ReturnType<typeof movieTimeScaleState>;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.movieTimeScaleState = void 0;
const movieTimeScaleState = () => {
let trackTimescale = null;
return {
getTrackTimescale: () => trackTimescale,
setTrackTimescale: (timescale) => {
trackTimescale = timescale;
},
};
};
exports.movieTimeScaleState = movieTimeScaleState;