Add .gitignore to exclude all node packages and lock files

This commit is contained in:
Adolfo Reyna
2026-02-23 21:56:04 -05:00
parent faae96c9ed
commit dcc5c6c044
9747 changed files with 1555105 additions and 2 deletions
@@ -0,0 +1,19 @@
type AacSamplePosition = {
offset: number;
index: number;
size: number;
};
export declare const aacState: () => {
addSample: ({ offset, size }: {
offset: number;
size: number;
}) => AacSamplePosition;
getSamples: () => AacSamplePosition[];
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
export type AacState = ReturnType<typeof aacState>;
export {};
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aacState = void 0;
const audio_sample_map_1 = require("./audio-sample-map");
const aacState = () => {
const samples = [];
// seems redunant, we could deduplicate this
const audioSamples = (0, audio_sample_map_1.audioSampleMapState)();
return {
addSample: ({ offset, size }) => {
const index = samples.findIndex((s) => s.offset === offset);
if (index !== -1) {
return samples[index];
}
samples.push({ offset, index: samples.length, size });
return samples[samples.length - 1];
},
getSamples: () => samples,
audioSamples,
};
};
exports.aacState = aacState;
@@ -0,0 +1,11 @@
export type AudioSampleOffset = {
timeInSeconds: number;
offset: number;
durationInSeconds: number;
};
export declare const audioSampleMapState: () => {
addSample: (audioSampleOffset: AudioSampleOffset) => void;
getSamples: () => AudioSampleOffset[];
setFromSeekingHints: (newMap: AudioSampleOffset[]) => void;
};
export type AudioSampleMapState = ReturnType<typeof audioSampleMapState>;
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.audioSampleMapState = void 0;
const audioSampleMapState = () => {
// {[]}
let map = [];
const addSample = (audioSampleOffset) => {
if (map.find((m) => m.offset === audioSampleOffset.offset)) {
return;
}
map.push(audioSampleOffset);
};
return {
addSample,
getSamples: () => map,
setFromSeekingHints: (newMap) => {
map = newMap;
},
};
};
exports.audioSampleMapState = audioSampleMapState;
@@ -0,0 +1,12 @@
import type { SpsInfo } from '../../containers/avc/parse-avc';
export declare const avcState: () => {
getPrevPicOrderCntLsb(): number;
getPrevPicOrderCntMsb(): number;
setPrevPicOrderCntLsb(value: number): void;
setPrevPicOrderCntMsb(value: number): void;
setSps(value: SpsInfo): void;
getSps(): SpsInfo | null;
getMaxFramesInBuffer(): number | null;
clear(): void;
};
export type AvcState = ReturnType<typeof avcState>;
@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.avcState = void 0;
const max_buffer_size_1 = require("../../containers/avc/max-buffer-size");
const avcState = () => {
let prevPicOrderCntLsb = 0;
let prevPicOrderCntMsb = 0;
let sps = null;
let maxFramesInBuffer = null;
return {
getPrevPicOrderCntLsb() {
return prevPicOrderCntLsb;
},
getPrevPicOrderCntMsb() {
return prevPicOrderCntMsb;
},
setPrevPicOrderCntLsb(value) {
prevPicOrderCntLsb = value;
},
setPrevPicOrderCntMsb(value) {
prevPicOrderCntMsb = value;
},
setSps(value) {
const macroblockBufferSize = (0, max_buffer_size_1.macroBlocksPerFrame)(value);
const maxBufferSize = (0, max_buffer_size_1.maxMacroblockBufferSize)(value);
const maxFrames = Math.min(16, Math.floor(maxBufferSize / macroblockBufferSize));
maxFramesInBuffer = maxFrames;
sps = value;
},
getSps() {
return sps;
},
getMaxFramesInBuffer() {
return maxFramesInBuffer;
},
clear() {
maxFramesInBuffer = null;
sps = null;
prevPicOrderCntLsb = 0;
prevPicOrderCntMsb = 0;
},
};
};
exports.avcState = avcState;
@@ -0,0 +1,17 @@
import type { Options, ParseMediaFields } from '../fields';
import type { MediaParserStructureUnstable } from '../parse-result';
import type { StructureState } from './structure';
export declare const needsTracksForField: ({ field, structure, }: {
field: keyof Options<ParseMediaFields>;
structure: MediaParserStructureUnstable | null;
}) => boolean;
export declare const makeCanSkipTracksState: ({ hasAudioTrackHandlers, fields, hasVideoTrackHandlers, structure, }: {
hasAudioTrackHandlers: boolean;
hasVideoTrackHandlers: boolean;
fields: Options<ParseMediaFields>;
structure: StructureState;
}) => {
doFieldsNeedTracks: () => boolean;
canSkipTracks: () => boolean;
};
export type CanSkipTracksState = ReturnType<typeof makeCanSkipTracksState>;
@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeCanSkipTracksState = exports.needsTracksForField = void 0;
const needsTracksForField = ({ field, structure, }) => {
if (field === 'dimensions') {
if ((structure === null || structure === void 0 ? void 0 : structure.type) === 'riff') {
return false;
}
return true;
}
if (field === 'audioCodec' ||
field === 'durationInSeconds' ||
field === 'slowDurationInSeconds' ||
field === 'slowFps' ||
field === 'fps' ||
field === 'isHdr' ||
field === 'rotation' ||
field === 'slowStructure' ||
field === 'tracks' ||
field === 'unrotatedDimensions' ||
field === 'videoCodec' ||
field === 'metadata' ||
field === 'location' ||
field === 'slowKeyframes' ||
field === 'slowNumberOfFrames' ||
field === 'keyframes' ||
field === 'images' ||
field === 'sampleRate' ||
field === 'numberOfAudioChannels' ||
field === 'slowAudioBitrate' ||
field === 'slowVideoBitrate' ||
field === 'm3uStreams') {
return true;
}
if (field === 'container' ||
field === 'internalStats' ||
field === 'mimeType' ||
field === 'name' ||
field === 'size') {
return false;
}
throw new Error(`field not implemeted ${field}`);
};
exports.needsTracksForField = needsTracksForField;
const makeCanSkipTracksState = ({ hasAudioTrackHandlers, fields, hasVideoTrackHandlers, structure, }) => {
const doFieldsNeedTracks = () => {
const keys = Object.keys(fields !== null && fields !== void 0 ? fields : {});
const selectedKeys = keys.filter((k) => fields[k]);
return selectedKeys.some((k) => (0, exports.needsTracksForField)({
field: k,
structure: structure.getStructureOrNull(),
}));
};
return {
doFieldsNeedTracks,
canSkipTracks: () => {
if (hasAudioTrackHandlers || hasVideoTrackHandlers) {
return false;
}
return !doFieldsNeedTracks();
},
};
};
exports.makeCanSkipTracksState = makeCanSkipTracksState;
@@ -0,0 +1,6 @@
import type { Reader } from '../readers/reader';
export declare const currentReader: (initialReader: Reader) => {
getCurrent: () => Reader;
setCurrent: (newReader: Reader) => void;
};
export type CurrentReader = ReturnType<typeof currentReader>;
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.currentReader = void 0;
const currentReader = (initialReader) => {
let current = initialReader;
return {
getCurrent: () => current,
setCurrent: (newReader) => {
current = newReader;
},
};
};
exports.currentReader = currentReader;
@@ -0,0 +1,2 @@
import type { AllOptions, ParseMediaFields } from '../fields';
export declare const emittedState: () => AllOptions<ParseMediaFields>;
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.emittedState = void 0;
const emittedState = () => {
const emittedFields = {
audioCodec: false,
container: false,
dimensions: false,
durationInSeconds: false,
fps: false,
internalStats: false,
isHdr: false,
location: false,
metadata: false,
mimeType: false,
name: false,
rotation: false,
size: false,
slowStructure: false,
tracks: false,
videoCodec: false,
unrotatedDimensions: false,
slowDurationInSeconds: false,
slowFps: false,
slowKeyframes: false,
slowNumberOfFrames: false,
keyframes: false,
images: false,
numberOfAudioChannels: false,
sampleRate: false,
slowAudioBitrate: false,
slowVideoBitrate: false,
m3uStreams: false,
};
return emittedFields;
};
exports.emittedState = emittedState;
@@ -0,0 +1,10 @@
export declare const flacState: () => {
setBlockingBitStrategy: (strategy: number) => void;
getBlockingBitStrategy: () => number | undefined;
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
export type FlacState = ReturnType<typeof flacState>;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.flacState = void 0;
const audio_sample_map_1 = require("./audio-sample-map");
const flacState = () => {
let blockingBitStrategy;
const audioSamples = (0, audio_sample_map_1.audioSampleMapState)();
return {
setBlockingBitStrategy: (strategy) => {
blockingBitStrategy = strategy;
},
getBlockingBitStrategy: () => blockingBitStrategy,
audioSamples,
};
};
exports.flacState = flacState;
@@ -0,0 +1,14 @@
import type { Options, ParseMediaFields } from '../fields';
import type { MediaParserTrack } from '../get-tracks';
import type { MediaParserLogLevel } from '../log';
import type { ParseMediaSrc } from '../options';
import type { CanSkipTracksState } from './can-skip-tracks';
export declare const makeTracksSectionState: (canSkipTracksState: CanSkipTracksState, src: ParseMediaSrc) => {
hasAllTracks: () => boolean;
getIsDone: () => boolean;
setIsDone: (logLevel: MediaParserLogLevel) => void;
addTrack: (track: MediaParserTrack) => void;
getTracks: () => MediaParserTrack[];
ensureHasTracksAtEnd: (fields: Options<ParseMediaFields>) => void;
};
export type TracksState = ReturnType<typeof makeTracksSectionState>;
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeTracksSectionState = void 0;
const log_1 = require("../log");
const makeTracksSectionState = (canSkipTracksState, src) => {
const tracks = [];
let doneWithTracks = false;
return {
hasAllTracks: () => doneWithTracks,
getIsDone: () => doneWithTracks,
setIsDone: (logLevel) => {
if (doneWithTracks) {
throw new Error('Error in Media Parser: Tracks have already been parsed');
}
log_1.Log.verbose(logLevel, 'All tracks have been parsed');
doneWithTracks = true;
},
addTrack: (track) => {
tracks.push(track);
},
getTracks: () => {
return tracks;
},
ensureHasTracksAtEnd: (fields) => {
if (canSkipTracksState.canSkipTracks()) {
return;
}
if (!fields.tracks) {
return;
}
if (!doneWithTracks) {
throw new Error('Error in Media Parser: End of parsing of ' +
src +
' has been reached, but no tracks have been found ');
}
},
};
};
exports.makeTracksSectionState = makeTracksSectionState;
@@ -0,0 +1,9 @@
export type MediaParserEmbeddedImage = {
description: string | null;
mimeType: string | null;
data: Uint8Array;
};
export declare const imagesState: () => {
images: MediaParserEmbeddedImage[];
addImage: (image: MediaParserEmbeddedImage) => void;
};
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.imagesState = void 0;
const imagesState = () => {
const images = [];
const addImage = (image) => {
images.push(image);
};
return {
images,
addImage,
};
};
exports.imagesState = imagesState;
@@ -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 {};
@@ -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;
@@ -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>;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
};
@@ -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;
@@ -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 {};
@@ -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;
@@ -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[];
@@ -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;
@@ -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[];
@@ -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;
@@ -0,0 +1,5 @@
export declare const movieTimeScaleState: () => {
getTrackTimescale: () => number | null;
setTrackTimescale: (timescale: number) => void;
};
export type MovieTimeScaleState = ReturnType<typeof movieTimeScaleState>;
@@ -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;
@@ -0,0 +1,7 @@
import type { MediaParserKeyframe } from '../options';
export declare const keyframesState: () => {
addKeyframe: (keyframe: MediaParserKeyframe) => void;
getKeyframes: () => MediaParserKeyframe[];
setFromSeekingHints: (keyframesFromHints: MediaParserKeyframe[]) => void;
};
export type KeyframesState = ReturnType<typeof keyframesState>;
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.keyframesState = void 0;
const keyframesState = () => {
const keyframes = [];
const addKeyframe = (keyframe) => {
if (keyframes.find((k) => k.positionInBytes === keyframe.positionInBytes)) {
return;
}
keyframes.push(keyframe);
};
const getKeyframes = () => {
keyframes.sort((a, b) => a.positionInBytes - b.positionInBytes);
return keyframes;
};
const setFromSeekingHints = (keyframesFromHints) => {
for (const keyframe of keyframesFromHints) {
addKeyframe(keyframe);
}
};
return {
addKeyframe,
getKeyframes,
setFromSeekingHints,
};
};
exports.keyframesState = keyframesState;
@@ -0,0 +1,64 @@
import type { M3uAssociatedPlaylist, M3uStream } from '../containers/m3u/get-streams';
import type { MediaParserLogLevel } from '../log';
import type { IsoBaseMediaStructure } from '../parse-result';
import type { MediaParserOnAudioSample, MediaParserOnVideoSample } from '../webcodec-sample-types';
export type M3uStreamOrInitialUrl = {
type: 'selected-stream';
stream: M3uStream;
} | {
type: 'initial-url';
url: string;
};
export type M3uRun = {
continue: () => Promise<M3uRun | null>;
abort: () => void;
};
type M3uSeek = {
targetTime: number;
};
export declare const m3uState: (logLevel: MediaParserLogLevel) => {
setSelectedMainPlaylist: (stream: M3uStreamOrInitialUrl) => void;
getSelectedMainPlaylist: () => M3uStreamOrInitialUrl | null;
setHasEmittedVideoTrack: (src: string, callback: MediaParserOnVideoSample | null) => void;
hasEmittedVideoTrack: (src: string) => false | MediaParserOnVideoSample | null;
setHasEmittedAudioTrack: (src: string, callback: MediaParserOnAudioSample | null) => void;
hasEmittedAudioTrack: (src: string) => false | MediaParserOnAudioSample | null;
setHasEmittedDoneWithTracks: (src: string) => void;
hasEmittedDoneWithTracks: (src: string) => boolean;
setReadyToIterateOverM3u: () => void;
isReadyToIterateOverM3u: () => boolean;
setAllChunksProcessed: (src: string) => void;
clearAllChunksProcessed: () => void;
getAllChunksProcessedForPlaylist: (src: string) => boolean;
getAllChunksProcessedOverall: () => boolean;
setHasFinishedManifest: () => void;
hasFinishedManifest: () => boolean;
setM3uStreamRun: (playlistUrl: string, run: M3uRun | null) => void;
setTracksDone: (playlistUrl: string) => boolean;
getTrackDone: (playlistUrl: string) => boolean;
clearTracksDone: () => void;
getM3uStreamRun: (playlistUrl: string) => M3uRun;
abortM3UStreamRuns: () => void;
setAssociatedPlaylists: (playlists: M3uAssociatedPlaylist[]) => void;
getAssociatedPlaylists: () => M3uAssociatedPlaylist[] | null;
getSelectedPlaylists: () => string[];
sampleSorter: {
clearSamples: () => void;
addToStreamWithTrack: (src: string) => void;
addVideoStreamToConsider: (src: string, callback: MediaParserOnVideoSample) => void;
addAudioStreamToConsider: (src: string, callback: MediaParserOnAudioSample) => void;
hasAudioStreamToConsider: (src: string) => boolean;
hasVideoStreamToConsider: (src: string) => boolean;
addAudioSample: (src: string, sample: import("../webcodec-sample-types").MediaParserAudioSample) => Promise<void>;
addVideoSample: (src: string, sample: import("../webcodec-sample-types").MediaParserVideoSample) => Promise<void>;
getNextStreamToRun: (streams: string[]) => string;
};
setMp4HeaderSegment: (playlistUrl: string, structure: IsoBaseMediaStructure) => void;
getMp4HeaderSegment: (playlistUrl: string) => IsoBaseMediaStructure;
setSeekToSecondsToProcess: (playlistUrl: string, m3uSeek: M3uSeek | null) => void;
getSeekToSecondsToProcess: (playlistUrl: string) => M3uSeek | null;
setNextSeekShouldSubtractChunks: (playlistUrl: string, chunks: number) => void;
getNextSeekShouldSubtractChunks: (playlistUrl: string) => number;
};
export type M3uState = ReturnType<typeof m3uState>;
export {};
@@ -0,0 +1,144 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.m3uState = void 0;
const sample_sorter_1 = require("../containers/m3u/sample-sorter");
const log_1 = require("../log");
const m3uState = (logLevel) => {
let selectedMainPlaylist = null;
let associatedPlaylists = null;
const hasEmittedVideoTrack = {};
const hasEmittedAudioTrack = {};
const hasEmittedDoneWithTracks = {};
let hasFinishedManifest = false;
const seekToSecondsToProcess = {};
const nextSeekShouldSubtractChunks = {};
let readyToIterateOverM3u = false;
const allChunksProcessed = {};
const m3uStreamRuns = {};
const tracksDone = {};
const getMainPlaylistUrl = () => {
if (!selectedMainPlaylist) {
throw new Error('No main playlist selected');
}
const playlistUrl = selectedMainPlaylist.type === 'initial-url'
? selectedMainPlaylist.url
: selectedMainPlaylist.stream.src;
return playlistUrl;
};
const getSelectedPlaylists = () => {
return [
getMainPlaylistUrl(),
...(associatedPlaylists !== null && associatedPlaylists !== void 0 ? associatedPlaylists : []).map((p) => p.src),
];
};
const getAllChunksProcessedForPlaylist = (src) => allChunksProcessed[src];
const mp4HeaderSegments = {};
const setMp4HeaderSegment = (playlistUrl, structure) => {
mp4HeaderSegments[playlistUrl] = structure;
};
const getMp4HeaderSegment = (playlistUrl) => {
return mp4HeaderSegments[playlistUrl];
};
return {
setSelectedMainPlaylist: (stream) => {
selectedMainPlaylist = stream;
},
getSelectedMainPlaylist: () => selectedMainPlaylist,
setHasEmittedVideoTrack: (src, callback) => {
hasEmittedVideoTrack[src] = callback;
},
hasEmittedVideoTrack: (src) => {
const value = hasEmittedVideoTrack[src];
if (value === undefined) {
return false;
}
return value;
},
setHasEmittedAudioTrack: (src, callback) => {
hasEmittedAudioTrack[src] = callback;
},
hasEmittedAudioTrack: (src) => {
const value = hasEmittedAudioTrack[src];
if (value === undefined) {
return false;
}
return value;
},
setHasEmittedDoneWithTracks: (src) => {
hasEmittedDoneWithTracks[src] = true;
},
hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src] !== undefined,
setReadyToIterateOverM3u: () => {
readyToIterateOverM3u = true;
},
isReadyToIterateOverM3u: () => readyToIterateOverM3u,
setAllChunksProcessed: (src) => {
allChunksProcessed[src] = true;
},
clearAllChunksProcessed: () => {
Object.keys(allChunksProcessed).forEach((key) => {
delete allChunksProcessed[key];
});
},
getAllChunksProcessedForPlaylist,
getAllChunksProcessedOverall: () => {
if (!selectedMainPlaylist) {
return false;
}
const selectedPlaylists = getSelectedPlaylists();
return selectedPlaylists.every((url) => allChunksProcessed[url]);
},
setHasFinishedManifest: () => {
hasFinishedManifest = true;
},
hasFinishedManifest: () => hasFinishedManifest,
setM3uStreamRun: (playlistUrl, run) => {
if (!run) {
delete m3uStreamRuns[playlistUrl];
return;
}
m3uStreamRuns[playlistUrl] = run;
},
setTracksDone: (playlistUrl) => {
tracksDone[playlistUrl] = true;
const selectedPlaylists = getSelectedPlaylists();
return selectedPlaylists.every((url) => tracksDone[url]);
},
getTrackDone: (playlistUrl) => {
return tracksDone[playlistUrl];
},
clearTracksDone: () => {
Object.keys(tracksDone).forEach((key) => {
delete tracksDone[key];
});
},
getM3uStreamRun: (playlistUrl) => { var _a; return (_a = m3uStreamRuns[playlistUrl]) !== null && _a !== void 0 ? _a : null; },
abortM3UStreamRuns: () => {
const values = Object.values(m3uStreamRuns);
if (values.length === 0) {
return;
}
log_1.Log.trace(logLevel, `Aborting ${values.length} M3U stream runs`);
values.forEach((run) => {
run.abort();
});
},
setAssociatedPlaylists: (playlists) => {
associatedPlaylists = playlists;
},
getAssociatedPlaylists: () => associatedPlaylists,
getSelectedPlaylists,
sampleSorter: (0, sample_sorter_1.sampleSorter)({ logLevel, getAllChunksProcessedForPlaylist }),
setMp4HeaderSegment,
getMp4HeaderSegment,
setSeekToSecondsToProcess: (playlistUrl, m3uSeek) => {
seekToSecondsToProcess[playlistUrl] = m3uSeek;
},
getSeekToSecondsToProcess: (playlistUrl) => { var _a; return (_a = seekToSecondsToProcess[playlistUrl]) !== null && _a !== void 0 ? _a : null; },
setNextSeekShouldSubtractChunks: (playlistUrl, chunks) => {
nextSeekShouldSubtractChunks[playlistUrl] = chunks;
},
getNextSeekShouldSubtractChunks: (playlistUrl) => { var _a; return (_a = nextSeekShouldSubtractChunks[playlistUrl]) !== null && _a !== void 0 ? _a : 0; },
};
};
exports.m3uState = m3uState;
@@ -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']>>;
@@ -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;
@@ -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>;
@@ -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;
@@ -0,0 +1,8 @@
import type { ParserState } from './parser-state';
export declare const missesMatroskaTracks: (state: ParserState) => boolean;
export declare const maySkipVideoData: ({ state }: {
state: ParserState;
}) => boolean;
export declare const maySkipOverSamplesInTheMiddle: ({ state, }: {
state: ParserState;
}) => boolean;
@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.maySkipOverSamplesInTheMiddle = exports.maySkipVideoData = exports.missesMatroskaTracks = void 0;
const get_ready_tracks_1 = require("../containers/webm/get-ready-tracks");
const traversal_1 = require("../containers/webm/traversal");
const need_samples_for_fields_1 = require("./need-samples-for-fields");
const getHasCallbacks = (state) => {
const hasNoTrackHandlers = !state.callbacks.hasAudioTrackHandlers &&
!state.callbacks.hasVideoTrackHandlers;
if (hasNoTrackHandlers) {
return false;
}
const hasAllTracksAndNoCallbacks = !state.callbacks.tracks.hasAllTracks() ||
Object.values(state.callbacks.videoSampleCallbacks).length > 0 ||
Object.values(state.callbacks.audioSampleCallbacks).length > 0;
return hasAllTracksAndNoCallbacks;
};
const missesMatroskaTracks = (state) => {
const struct = state.structure.getStructureOrNull();
if (struct === null) {
return false;
}
if (struct.type !== 'matroska') {
return false;
}
const mainSegment = (0, traversal_1.getMainSegment)(struct.boxes);
if (mainSegment === null) {
return false;
}
return ((0, get_ready_tracks_1.getTracksFromMatroska)({
structureState: state.structure,
webmState: state.webm,
}).missingInfo.length > 0);
};
exports.missesMatroskaTracks = missesMatroskaTracks;
const maySkipVideoData = ({ state }) => {
const hasCallbacks = getHasCallbacks(state);
return (!hasCallbacks &&
!(0, need_samples_for_fields_1.needsToIterateOverSamples)({
emittedFields: state.emittedFields,
fields: state.fields,
}) &&
!(0, exports.missesMatroskaTracks)(state));
};
exports.maySkipVideoData = maySkipVideoData;
const maySkipOverSamplesInTheMiddle = ({ state, }) => {
const hasCallbacks = getHasCallbacks(state);
return (!hasCallbacks &&
!(0, need_samples_for_fields_1.needsToIterateOverEverySample)({
emittedFields: state.emittedFields,
fields: state.fields,
}));
};
exports.maySkipOverSamplesInTheMiddle = maySkipOverSamplesInTheMiddle;
@@ -0,0 +1,26 @@
import type { XingData } from '../containers/mp3/parse-xing';
export type Mp3Info = {
sampleRate: number;
mpegVersion: 1 | 2;
layer: number;
};
export type VariableMp3BitrateInfo = {
type: 'variable';
xingData: XingData;
};
export type Mp3BitrateInfo = {
type: 'constant';
bitrateInKbit: number;
} | VariableMp3BitrateInfo;
export declare const makeMp3State: () => {
getMp3Info: () => Mp3Info | null;
setMp3Info: (info: Mp3Info) => void;
getMp3BitrateInfo: () => Mp3BitrateInfo | null;
setMp3BitrateInfo: (info: Mp3BitrateInfo) => void;
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
export type Mp3State = ReturnType<typeof makeMp3State>;
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeMp3State = void 0;
const audio_sample_map_1 = require("./audio-sample-map");
const makeMp3State = () => {
let mp3Info = null;
let bitrateInfo = null;
const audioSamples = (0, audio_sample_map_1.audioSampleMapState)();
return {
getMp3Info: () => mp3Info,
setMp3Info: (info) => {
mp3Info = info;
},
getMp3BitrateInfo: () => bitrateInfo,
setMp3BitrateInfo: (info) => {
bitrateInfo = info;
},
audioSamples,
};
};
exports.makeMp3State = makeMp3State;
@@ -0,0 +1,10 @@
import type { AllOptions, Options, ParseMediaFields } from '../fields';
export declare const fieldsNeedSamplesMap: Record<keyof Options<ParseMediaFields>, boolean>;
export declare const needsToIterateOverSamples: ({ fields, emittedFields, }: {
fields: Options<ParseMediaFields>;
emittedFields: AllOptions<ParseMediaFields>;
}) => boolean;
export declare const needsToIterateOverEverySample: ({ fields, emittedFields, }: {
fields: Options<ParseMediaFields>;
emittedFields: AllOptions<ParseMediaFields>;
}) => boolean;
@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.needsToIterateOverEverySample = exports.needsToIterateOverSamples = exports.fieldsNeedSamplesMap = void 0;
exports.fieldsNeedSamplesMap = {
slowDurationInSeconds: true,
slowFps: true,
slowKeyframes: true,
slowNumberOfFrames: true,
audioCodec: false,
container: false,
dimensions: false,
durationInSeconds: false,
fps: false,
internalStats: false,
isHdr: false,
name: false,
rotation: false,
size: false,
slowStructure: false,
tracks: false,
unrotatedDimensions: false,
videoCodec: false,
metadata: false,
location: false,
mimeType: false,
keyframes: false,
images: false,
numberOfAudioChannels: false,
sampleRate: false,
slowAudioBitrate: true,
slowVideoBitrate: true,
m3uStreams: false,
};
const needsToIterateOverSamples = ({ fields, emittedFields, }) => {
const keys = Object.keys(fields !== null && fields !== void 0 ? fields : {});
const selectedKeys = keys.filter((k) => fields[k]);
return selectedKeys.some((k) => exports.fieldsNeedSamplesMap[k] && !emittedFields[k]);
};
exports.needsToIterateOverSamples = needsToIterateOverSamples;
// For duration, we only need the first and last sample
const fieldsNeedEverySampleMap = {
...exports.fieldsNeedSamplesMap,
slowDurationInSeconds: false,
};
const needsToIterateOverEverySample = ({ fields, emittedFields, }) => {
const keys = Object.keys(fields !== null && fields !== void 0 ? fields : {});
const selectedKeys = keys.filter((k) => fields[k]);
return selectedKeys.some((k) => fieldsNeedEverySampleMap[k] && !emittedFields[k]);
};
exports.needsToIterateOverEverySample = needsToIterateOverEverySample;
@@ -0,0 +1,527 @@
import type { AvcPPs, AvcProfileInfo } from '../containers/avc/parse-avc';
import type { SelectM3uAssociatedPlaylistsFn, SelectM3uStreamFn } from '../containers/m3u/select-stream';
import type { MediaParserController } from '../controller/media-parser-controller';
import type { PrefetchCache } from '../fetch';
import type { Options, ParseMediaFields } from '../fields';
import { type BufferIterator } from '../iterator/buffer-iterator';
import { type MediaParserLogLevel } from '../log';
import type { M3uPlaylistContext, OnDiscardedData, ParseMediaCallbacks, ParseMediaMode, ParseMediaSrc } from '../options';
import type { MediaParserReaderInterface, Reader } from '../readers/reader';
import type { MediaParserOnAudioTrack, MediaParserOnVideoTrack } from '../webcodec-sample-types';
export type InternalStats = {
skippedBytes: number;
finalCursorOffset: number;
};
export type SpsAndPps = {
sps: AvcProfileInfo;
pps: AvcPPs;
};
export declare const makeParserState: ({ hasAudioTrackHandlers, hasVideoTrackHandlers, controller, onAudioTrack, onVideoTrack, contentLength, logLevel, mode, src, readerInterface, onDiscardedData, selectM3uStreamFn, selectM3uAssociatedPlaylistsFn, m3uPlaylistContext, contentType, name, callbacks, fieldsInReturnValue, mimeType, initialReaderInstance, makeSamplesStartAtZero, prefetchCache, }: {
hasAudioTrackHandlers: boolean;
hasVideoTrackHandlers: boolean;
controller: MediaParserController;
onAudioTrack: MediaParserOnAudioTrack | null;
onVideoTrack: MediaParserOnVideoTrack | null;
contentLength: number;
logLevel: MediaParserLogLevel;
mode: ParseMediaMode;
src: ParseMediaSrc;
readerInterface: MediaParserReaderInterface;
onDiscardedData: OnDiscardedData | null;
selectM3uStreamFn: SelectM3uStreamFn;
selectM3uAssociatedPlaylistsFn: SelectM3uAssociatedPlaylistsFn;
m3uPlaylistContext: M3uPlaylistContext | null;
contentType: string | null;
name: string;
callbacks: ParseMediaCallbacks;
fieldsInReturnValue: Options<ParseMediaFields>;
mimeType: string | null;
initialReaderInstance: Reader;
makeSamplesStartAtZero: boolean;
prefetchCache: PrefetchCache;
}) => {
riff: {
getAvcProfile: () => SpsAndPps | null;
onProfile: (profile: SpsAndPps) => Promise<void>;
registerOnAvcProfileCallback: (callback: (profile: SpsAndPps) => Promise<void>) => void;
getNextTrackIndex: () => number;
queuedBFrames: {
addFrame: ({ frame, maxFramesInBuffer, trackId, timescale, }: {
frame: import("./riff/queued-frames").QueuedVideoSample;
trackId: number;
maxFramesInBuffer: number;
timescale: number;
}) => void;
flush: () => void;
getReleasedFrame: () => {
sample: import("./riff/queued-frames").QueuedVideoSample;
trackId: number;
timescale: number;
} | null;
hasReleasedFrames: () => boolean;
clear: () => void;
};
incrementNextTrackIndex: () => void;
lazyIdx1: {
triggerLoad: (position: number) => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}>;
getLoadedIdx1: () => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null>;
getIfAlreadyLoaded: () => {
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null;
setFromSeekingHints: (hints: import("../containers/riff/seeking-hints").RiffSeekingHints) => void;
waitForLoaded: () => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}> | Promise<null>;
};
sampleCounter: {
onAudioSample: (trackId: number, audioSample: import("../webcodec-sample-types").MediaParserAudioSample) => void;
onVideoSample: ({ trackId, videoSample, }: {
videoSample: import("../webcodec-sample-types").MediaParserVideoSample;
trackId: number;
}) => void;
getSampleCountForTrack: ({ trackId }: {
trackId: number;
}) => number;
setSamplesFromSeek: (samples: Record<number, number>) => void;
riffKeys: {
addKeyframe: (keyframe: import("./riff/riff-keyframes").RiffKeyframe) => void;
getKeyframes: () => import("./riff/riff-keyframes").RiffKeyframe[];
setFromSeekingHints: (keyframesFromHints: import("./riff/riff-keyframes").RiffKeyframe[]) => void;
};
setPocAtKeyframeOffset: ({ keyframeOffset, poc, }: {
keyframeOffset: number;
poc: number;
}) => void;
getPocAtKeyframeOffset: ({ keyframeOffset, }: {
keyframeOffset: number;
}) => number[];
getKeyframeAtOffset: (sample: import("./riff/queued-frames").QueuedVideoSample) => number | null;
};
};
transportStream: {
nextPesHeaderStore: {
setNextPesHeader: (pesHeader: import("../containers/transport-stream/parse-pes").PacketPes) => void;
getNextPesHeader: () => import("../containers/transport-stream/parse-pes").PacketPes;
};
observedPesHeaders: {
pesHeaders: import("../containers/transport-stream/parse-pes").PacketPes[];
addPesHeader: (pesHeader: import("../containers/transport-stream/parse-pes").PacketPes) => void;
markPtsAsKeyframe: (pts: number) => void;
getPesKeyframeHeaders: () => import("../containers/transport-stream/parse-pes").PacketPes[];
setPesKeyframesFromSeekingHints: (hints: import("../seeking-hints").TransportStreamSeekingHints) => void;
};
streamBuffers: Map<number, import("../containers/transport-stream/process-stream-buffers").TransportStreamPacketBuffer>;
startOffset: {
getOffset: (trackId: number) => number;
setOffset: ({ newOffset, trackId }: {
trackId: number;
newOffset: number;
}) => void;
};
resetBeforeSeek: () => void;
lastEmittedSample: {
setLastEmittedSample: (sample: import("../webcodec-sample-types").MediaParserAudioSample | import("../webcodec-sample-types").MediaParserVideoSample) => void;
getLastEmittedSample: () => import("../webcodec-sample-types").MediaParserVideoSample | import("../webcodec-sample-types").MediaParserAudioSample | null;
resetLastEmittedSample: () => void;
};
};
webm: {
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: import("../containers/webm/segments").OnTrackEntrySegment;
getTrackInfoByNumber: (id: number) => import("../containers/webm/segments/track-entry").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<import("./matroska/webm").SegmentSection, "index">) => void;
addCluster: (cluster: import("./matroska/webm").ClusterSection) => void;
getFirstCluster: () => import("./matroska/webm").ClusterSection | undefined;
isInsideSegment: (iterator: BufferIterator) => import("./matroska/webm").SegmentSection | null;
isInsideCluster: (offset: number) => import("./matroska/webm").ClusterSection | null;
setAvcProfileForTrackNumber: (trackNumber: number, avcProfile: AvcProfileInfo) => void;
getAvcProfileForTrackNumber: (trackNumber: number) => AvcProfileInfo | null;
};
iso: {
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("./iso-base-media/precomputed-moof").MoofBox[];
setMoofBoxes: (boxes: import("./iso-base-media/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;
};
};
mp3: {
getMp3Info: () => import("./mp3").Mp3Info | null;
setMp3Info: (info: import("./mp3").Mp3Info) => void;
getMp3BitrateInfo: () => import("./mp3").Mp3BitrateInfo | null;
setMp3BitrateInfo: (info: import("./mp3").Mp3BitrateInfo) => void;
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
aac: {
addSample: ({ offset, size }: {
offset: number;
size: number;
}) => {
offset: number;
index: number;
size: number;
};
getSamples: () => {
offset: number;
index: number;
size: number;
}[];
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
flac: {
setBlockingBitStrategy: (strategy: number) => void;
getBlockingBitStrategy: () => number | undefined;
audioSamples: {
addSample: (audioSampleOffset: import("./audio-sample-map").AudioSampleOffset) => void;
getSamples: () => import("./audio-sample-map").AudioSampleOffset[];
setFromSeekingHints: (newMap: import("./audio-sample-map").AudioSampleOffset[]) => void;
};
};
m3u: {
setSelectedMainPlaylist: (stream: import("./m3u-state").M3uStreamOrInitialUrl) => void;
getSelectedMainPlaylist: () => import("./m3u-state").M3uStreamOrInitialUrl | null;
setHasEmittedVideoTrack: (src: string, callback: import("../webcodec-sample-types").MediaParserOnVideoSample | null) => void;
hasEmittedVideoTrack: (src: string) => false | import("../webcodec-sample-types").MediaParserOnVideoSample | null;
setHasEmittedAudioTrack: (src: string, callback: import("../webcodec-sample-types").MediaParserOnAudioSample | null) => void;
hasEmittedAudioTrack: (src: string) => false | import("../webcodec-sample-types").MediaParserOnAudioSample | null;
setHasEmittedDoneWithTracks: (src: string) => void;
hasEmittedDoneWithTracks: (src: string) => boolean;
setReadyToIterateOverM3u: () => void;
isReadyToIterateOverM3u: () => boolean;
setAllChunksProcessed: (src: string) => void;
clearAllChunksProcessed: () => void;
getAllChunksProcessedForPlaylist: (src: string) => boolean;
getAllChunksProcessedOverall: () => boolean;
setHasFinishedManifest: () => void;
hasFinishedManifest: () => boolean;
setM3uStreamRun: (playlistUrl: string, run: import("./m3u-state").M3uRun | null) => void;
setTracksDone: (playlistUrl: string) => boolean;
getTrackDone: (playlistUrl: string) => boolean;
clearTracksDone: () => void;
getM3uStreamRun: (playlistUrl: string) => import("./m3u-state").M3uRun;
abortM3UStreamRuns: () => void;
setAssociatedPlaylists: (playlists: import("..").M3uAssociatedPlaylist[]) => void;
getAssociatedPlaylists: () => import("..").M3uAssociatedPlaylist[] | null;
getSelectedPlaylists: () => string[];
sampleSorter: {
clearSamples: () => void;
addToStreamWithTrack: (src: string) => void;
addVideoStreamToConsider: (src: string, callback: import("../webcodec-sample-types").MediaParserOnVideoSample) => void;
addAudioStreamToConsider: (src: string, callback: import("../webcodec-sample-types").MediaParserOnAudioSample) => void;
hasAudioStreamToConsider: (src: string) => boolean;
hasVideoStreamToConsider: (src: string) => boolean;
addAudioSample: (src: string, sample: import("../webcodec-sample-types").MediaParserAudioSample) => Promise<void>;
addVideoSample: (src: string, sample: import("../webcodec-sample-types").MediaParserVideoSample) => Promise<void>;
getNextStreamToRun: (streams: string[]) => string;
};
setMp4HeaderSegment: (playlistUrl: string, structure: import("../parse-result").IsoBaseMediaStructure) => void;
getMp4HeaderSegment: (playlistUrl: string) => import("../parse-result").IsoBaseMediaStructure;
setSeekToSecondsToProcess: (playlistUrl: string, m3uSeek: {
targetTime: number;
} | null) => void;
getSeekToSecondsToProcess: (playlistUrl: string) => {
targetTime: number;
} | null;
setNextSeekShouldSubtractChunks: (playlistUrl: string, chunks: number) => void;
getNextSeekShouldSubtractChunks: (playlistUrl: string) => number;
};
timings: {
timeIterating: number;
timeReadingData: number;
timeSeeking: number;
timeCheckingIfDone: number;
timeFreeingData: number;
timeInParseLoop: number;
};
callbacks: {
registerVideoSampleCallback: (id: number, callback: import("../webcodec-sample-types").MediaParserOnVideoSample | null) => Promise<void>;
onAudioSample: ({ audioSample, trackId, }: {
trackId: number;
audioSample: import("../webcodec-sample-types").MediaParserAudioSample;
}) => Promise<void>;
onVideoSample: ({ trackId, videoSample, }: {
trackId: number;
videoSample: import("../webcodec-sample-types").MediaParserVideoSample;
}) => Promise<void>;
canSkipTracksState: {
doFieldsNeedTracks: () => boolean;
canSkipTracks: () => boolean;
};
registerAudioSampleCallback: (id: number, callback: import("../webcodec-sample-types").MediaParserOnAudioSample | null) => Promise<void>;
tracks: {
hasAllTracks: () => boolean;
getIsDone: () => boolean;
setIsDone: (logLevel: MediaParserLogLevel) => void;
addTrack: (track: import("..").MediaParserTrack) => void;
getTracks: () => import("..").MediaParserTrack[];
ensureHasTracksAtEnd: (fields: Options<ParseMediaFields>) => void;
};
audioSampleCallbacks: Record<number, import("../webcodec-sample-types").MediaParserOnAudioSample>;
videoSampleCallbacks: Record<number, import("../webcodec-sample-types").MediaParserOnVideoSample>;
hasAudioTrackHandlers: boolean;
hasVideoTrackHandlers: boolean;
callTracksDoneCallback: () => Promise<void>;
};
getInternalStats: () => InternalStats;
getSkipBytes: () => number;
increaseSkippedBytes: (bytes: number) => void;
keyframes: {
addKeyframe: (keyframe: import("../options").MediaParserKeyframe) => void;
getKeyframes: () => import("../options").MediaParserKeyframe[];
setFromSeekingHints: (keyframesFromHints: import("../options").MediaParserKeyframe[]) => void;
};
structure: {
getStructureOrNull: () => import("../parse-result").MediaParserStructureUnstable | null;
getStructure: () => import("../parse-result").MediaParserStructureUnstable;
setStructure: (value: import("../parse-result").MediaParserStructureUnstable) => void;
getFlacStructure: () => import("../containers/flac/types").FlacStructure;
getIsoStructure: () => import("../parse-result").IsoBaseMediaStructure;
getMp3Structure: () => import("../parse-result").Mp3Structure;
getM3uStructure: () => import("../containers/m3u/types").M3uStructure;
getRiffStructure: () => import("../containers/riff/riff-box").RiffStructure;
getTsStructure: () => import("../parse-result").TransportStreamStructure;
getWavStructure: () => import("../containers/wav/types").WavStructure;
getMatroskaStructure: () => import("../parse-result").MatroskaStructure;
};
onAudioTrack: MediaParserOnAudioTrack | null;
onVideoTrack: MediaParserOnVideoTrack | null;
emittedFields: import("../fields").AllOptions<ParseMediaFields>;
fields: Partial<import("../fields").AllOptions<ParseMediaFields>>;
samplesObserved: {
addVideoSample: (videoSample: import("../webcodec-sample-types").MediaParserVideoSample) => void;
addAudioSample: (audioSample: import("../webcodec-sample-types").MediaParserAudioSample) => void;
getSlowDurationInSeconds: () => number;
getFps: () => number;
getSlowNumberOfFrames: () => number;
getAudioBitrate: () => number | null;
getVideoBitrate: () => number | null;
getLastSampleObserved: () => boolean;
setLastSampleObserved: () => void;
getAmountOfSamplesObserved: () => number;
};
contentLength: number;
images: {
images: import("./images").MediaParserEmbeddedImage[];
addImage: (image: import("./images").MediaParserEmbeddedImage) => void;
};
mediaSection: {
addMediaSection: (section: import("./video-section").MediaSection) => void;
getMediaSections: () => import("./video-section").MediaSection[];
isCurrentByteInMediaSection: (iterator: BufferIterator) => "no-section-defined" | "in-section" | "outside-section";
isByteInMediaSection: ({ position, mediaSections, }: {
position: number;
mediaSections: import("./video-section").MediaSection[];
}) => "no-section-defined" | "in-section" | "outside-section";
getCurrentMediaSection: ({ offset, mediaSections, }: {
offset: number;
mediaSections: import("./video-section").MediaSection[];
}) => import("./video-section").MediaSection | null;
getMediaSectionAssertOnlyOne: () => import("./video-section").MediaSection;
mediaSections: import("./video-section").MediaSection[];
};
logLevel: "trace" | "verbose" | "info" | "warn" | "error";
iterator: {
startReadingBits: () => void;
stopReadingBits: () => void;
skipTo: (offset: number) => void;
addData: (newData: Uint8Array) => void;
counter: {
getOffset: () => number;
discardBytes: (bytes: number) => void;
increment: (bytes: number) => void;
getDiscardedBytes: () => number;
setDiscardedOffset: (bytes: number) => void;
getDiscardedOffset: () => number;
decrement: (bytes: number) => void;
};
peekB: (length: number) => void;
peekD: (length: number) => void;
getBits: (bits: number) => number;
bytesRemaining: () => number;
leb128: () => number;
removeBytesRead: (force: boolean, mode: ParseMediaMode) => {
bytesRemoved: number;
removedData: Uint8Array<ArrayBuffer> | null;
};
discard: (length: number) => void;
getEightByteNumber: (littleEndian?: boolean) => number;
getFourByteNumber: () => number;
getSlice: (amount: number) => Uint8Array<ArrayBuffer>;
getAtom: () => string;
detectFileType: () => import("../file-types/detect-file-type").FileType;
getPaddedFourByteNumber: () => number;
getMatroskaSegmentId: () => string | null;
getVint: () => number | null;
getUint8: () => number;
getEBML: () => number;
getInt8: () => number;
getUint16: () => number;
getUint16Le: () => number;
getUint24: () => number;
getInt24: () => number;
getInt16: () => number;
getUint32: () => number;
getUint64: (littleEndian?: boolean) => bigint;
getInt64: (littleEndian?: boolean) => bigint;
getFixedPointUnsigned1616Number: () => number;
getFixedPointSigned1616Number: () => number;
getFixedPointSigned230Number: () => number;
getPascalString: () => number[];
getUint(length: number): number;
getByteString(length: number, trimTrailingZeroes: boolean): string;
planBytes: (size: number) => {
discardRest: () => Uint8Array<ArrayBuffer>;
};
getFloat64: () => number;
readUntilNullTerminator: () => string;
getFloat32: () => number;
getUint32Le: () => number;
getInt32Le: () => number;
getInt32: () => number;
destroy: () => void;
startBox: (size: number) => {
discardRest: () => void;
expectNoMoreBytes: () => void;
};
readExpGolomb: () => number;
startCheckpoint: () => {
returnToCheckpoint: () => void;
};
getFlacCodecNumber: () => number;
readUntilLineEnd: () => string | null;
getSyncSafeInt32: () => number;
replaceData: (newData: Uint8Array, seekTo: number) => void;
};
controller: MediaParserController;
mode: ParseMediaMode;
src: ParseMediaSrc;
readerInterface: MediaParserReaderInterface;
discardReadBytes: (force: boolean) => Promise<void>;
selectM3uStreamFn: SelectM3uStreamFn;
selectM3uAssociatedPlaylistsFn: SelectM3uAssociatedPlaylistsFn;
m3uPlaylistContext: M3uPlaylistContext | null;
contentType: string | null;
name: string;
returnValue: {
dimensions: import("..").MediaParserDimensions | null;
durationInSeconds: number | null;
slowDurationInSeconds: number;
slowNumberOfFrames: number;
slowFps: number;
slowStructure: import("../parse-result").MediaParserStructureUnstable;
fps: number | null;
videoCodec: import("..").MediaParserVideoCodec | null;
audioCodec: import("..").MediaParserAudioCodec | null;
tracks: import("..").MediaParserTrack[];
rotation: number | null;
unrotatedDimensions: import("..").MediaParserDimensions | null;
internalStats: InternalStats;
size: number | null;
name: string;
container: import("../options").MediaParserContainer;
isHdr: boolean;
metadata: import("..").MediaParserMetadataEntry[];
location: import("..").MediaParserLocation | null;
mimeType: string | null;
keyframes: import("../options").MediaParserKeyframe[] | null;
slowKeyframes: import("../options").MediaParserKeyframe[];
images: import("./images").MediaParserEmbeddedImage[];
sampleRate: number | null;
numberOfAudioChannels: number | null;
slowVideoBitrate: number | null;
slowAudioBitrate: number | null;
m3uStreams: import("..").M3uStream[] | null;
};
callbackFunctions: Partial<import("../options").ParseMediaCallbacksMandatory>;
fieldsInReturnValue: Partial<import("../fields").AllOptions<ParseMediaFields>>;
mimeType: string | null;
errored: Error | null;
currentReader: {
getCurrent: () => Reader;
setCurrent: (newReader: Reader) => void;
};
seekInfiniteLoop: {
registerSeek: (byte: number) => void;
reset: () => void;
};
makeSamplesStartAtZero: boolean;
prefetchCache: PrefetchCache;
avc: {
getPrevPicOrderCntLsb(): number;
getPrevPicOrderCntMsb(): number;
setPrevPicOrderCntLsb(value: number): void;
setPrevPicOrderCntMsb(value: number): void;
setSps(value: import("../containers/avc/parse-avc").SpsInfo): void;
getSps(): import("../containers/avc/parse-avc").SpsInfo | null;
getMaxFramesInBuffer(): number | null;
clear(): void;
};
};
export type ParserState = ReturnType<typeof makeParserState>;
@@ -0,0 +1,148 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeParserState = void 0;
const get_fields_from_callbacks_1 = require("../get-fields-from-callbacks");
const buffer_iterator_1 = require("../iterator/buffer-iterator");
const log_1 = require("../log");
const aac_state_1 = require("./aac-state");
const avc_state_1 = require("./avc/avc-state");
const current_reader_1 = require("./current-reader");
const emitted_fields_1 = require("./emitted-fields");
const flac_state_1 = require("./flac-state");
const images_1 = require("./images");
const iso_state_1 = require("./iso-base-media/iso-state");
const keyframes_1 = require("./keyframes");
const m3u_state_1 = require("./m3u-state");
const webm_1 = require("./matroska/webm");
const mp3_1 = require("./mp3");
const riff_1 = require("./riff");
const sample_callbacks_1 = require("./sample-callbacks");
const slow_duration_fps_1 = require("./samples-observed/slow-duration-fps");
const seek_infinite_loop_1 = require("./seek-infinite-loop");
const structure_1 = require("./structure");
const timings_1 = require("./timings");
const transport_stream_1 = require("./transport-stream/transport-stream");
const video_section_1 = require("./video-section");
const makeParserState = ({ hasAudioTrackHandlers, hasVideoTrackHandlers, controller, onAudioTrack, onVideoTrack, contentLength, logLevel, mode, src, readerInterface, onDiscardedData, selectM3uStreamFn, selectM3uAssociatedPlaylistsFn, m3uPlaylistContext, contentType, name, callbacks, fieldsInReturnValue, mimeType, initialReaderInstance, makeSamplesStartAtZero, prefetchCache, }) => {
let skippedBytes = 0;
const returnValue = {};
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: new Uint8Array([]),
maxBytes: contentLength,
logLevel,
});
const increaseSkippedBytes = (bytes) => {
skippedBytes += bytes;
};
const structure = (0, structure_1.structureState)();
const keyframes = (0, keyframes_1.keyframesState)();
const emittedFields = (0, emitted_fields_1.emittedState)();
const samplesObserved = (0, slow_duration_fps_1.samplesObservedState)();
const mp3 = (0, mp3_1.makeMp3State)();
const images = (0, images_1.imagesState)();
const timings = (0, timings_1.timingsState)();
const seekInfiniteLoop = (0, seek_infinite_loop_1.seekInfiniteLoopDetectionState)();
const currentReaderState = (0, current_reader_1.currentReader)(initialReaderInstance);
const avc = (0, avc_state_1.avcState)();
const errored = null;
const discardReadBytes = async (force) => {
const { bytesRemoved, removedData } = iterator.removeBytesRead(force, mode);
if (bytesRemoved) {
log_1.Log.verbose(logLevel, `Freed ${bytesRemoved} bytes`);
}
if (removedData && onDiscardedData) {
await onDiscardedData(removedData);
}
};
const fields = (0, get_fields_from_callbacks_1.getFieldsFromCallback)({
fields: fieldsInReturnValue,
callbacks,
});
const mediaSection = (0, video_section_1.mediaSectionState)();
return {
riff: (0, riff_1.riffSpecificState)({
controller,
logLevel,
readerInterface,
src,
prefetchCache,
contentLength,
}),
transportStream: (0, transport_stream_1.transportStreamState)(),
webm: (0, webm_1.webmState)({
controller,
logLevel,
readerInterface,
src,
prefetchCache,
}),
iso: (0, iso_state_1.isoBaseMediaState)({
contentLength,
controller,
readerInterface,
src,
logLevel,
prefetchCache,
}),
mp3,
aac: (0, aac_state_1.aacState)(),
flac: (0, flac_state_1.flacState)(),
m3u: (0, m3u_state_1.m3uState)(logLevel),
timings,
callbacks: (0, sample_callbacks_1.callbacksState)({
controller,
hasAudioTrackHandlers,
hasVideoTrackHandlers,
fields,
keyframes,
emittedFields,
samplesObserved,
structure,
src,
seekSignal: controller._internals.seekSignal,
logLevel,
}),
getInternalStats: () => {
var _a;
return ({
skippedBytes,
finalCursorOffset: (_a = iterator.counter.getOffset()) !== null && _a !== void 0 ? _a : 0,
});
},
getSkipBytes: () => skippedBytes,
increaseSkippedBytes,
keyframes,
structure,
onAudioTrack,
onVideoTrack,
emittedFields,
fields,
samplesObserved,
contentLength,
images,
mediaSection,
logLevel,
iterator,
controller,
mode,
src,
readerInterface,
discardReadBytes,
selectM3uStreamFn,
selectM3uAssociatedPlaylistsFn,
m3uPlaylistContext,
contentType,
name,
returnValue,
callbackFunctions: callbacks,
fieldsInReturnValue,
mimeType,
errored: errored,
currentReader: currentReaderState,
seekInfiniteLoop,
makeSamplesStartAtZero,
prefetchCache,
avc,
};
};
exports.makeParserState = makeParserState;
@@ -0,0 +1,82 @@
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 { SpsAndPps } from './parser-state';
type AvcProfileInfoCallback = (profile: SpsAndPps) => Promise<void>;
export declare const riffSpecificState: ({ controller, logLevel, readerInterface, src, prefetchCache, contentLength, }: {
controller: MediaParserController;
logLevel: MediaParserLogLevel;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
prefetchCache: PrefetchCache;
contentLength: number;
}) => {
getAvcProfile: () => SpsAndPps | null;
onProfile: (profile: SpsAndPps) => Promise<void>;
registerOnAvcProfileCallback: (callback: AvcProfileInfoCallback) => void;
getNextTrackIndex: () => number;
queuedBFrames: {
addFrame: ({ frame, maxFramesInBuffer, trackId, timescale, }: {
frame: import("./riff/queued-frames").QueuedVideoSample;
trackId: number;
maxFramesInBuffer: number;
timescale: number;
}) => void;
flush: () => void;
getReleasedFrame: () => {
sample: import("./riff/queued-frames").QueuedVideoSample;
trackId: number;
timescale: number;
} | null;
hasReleasedFrames: () => boolean;
clear: () => void;
};
incrementNextTrackIndex: () => void;
lazyIdx1: {
triggerLoad: (position: number) => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}>;
getLoadedIdx1: () => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null>;
getIfAlreadyLoaded: () => {
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null;
setFromSeekingHints: (hints: import("../containers/riff/seeking-hints").RiffSeekingHints) => void;
waitForLoaded: () => Promise<{
entries: import("../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}> | Promise<null>;
};
sampleCounter: {
onAudioSample: (trackId: number, audioSample: import("..").MediaParserAudioSample) => void;
onVideoSample: ({ trackId, videoSample, }: {
videoSample: import("..").MediaParserVideoSample;
trackId: number;
}) => void;
getSampleCountForTrack: ({ trackId }: {
trackId: number;
}) => number;
setSamplesFromSeek: (samples: Record<number, number>) => void;
riffKeys: {
addKeyframe: (keyframe: import("./riff/riff-keyframes").RiffKeyframe) => void;
getKeyframes: () => import("./riff/riff-keyframes").RiffKeyframe[];
setFromSeekingHints: (keyframesFromHints: import("./riff/riff-keyframes").RiffKeyframe[]) => void;
};
setPocAtKeyframeOffset: ({ keyframeOffset, poc, }: {
keyframeOffset: number;
poc: number;
}) => void;
getPocAtKeyframeOffset: ({ keyframeOffset, }: {
keyframeOffset: number;
}) => number[];
getKeyframeAtOffset: (sample: import("./riff/queued-frames").QueuedVideoSample) => number | null;
};
};
export type RiffState = ReturnType<typeof riffSpecificState>;
export {};
@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.riffSpecificState = void 0;
const lazy_idx1_fetch_1 = require("./riff/lazy-idx1-fetch");
const queued_frames_1 = require("./riff/queued-frames");
const sample_counter_1 = require("./riff/sample-counter");
const riffSpecificState = ({ controller, logLevel, readerInterface, src, prefetchCache, contentLength, }) => {
let avcProfile = null;
let nextTrackIndex = 0;
const profileCallbacks = [];
const registerOnAvcProfileCallback = (callback) => {
profileCallbacks.push(callback);
};
const onProfile = async (profile) => {
avcProfile = profile;
for (const callback of profileCallbacks) {
await callback(profile);
}
profileCallbacks.length = 0;
};
const lazyIdx1 = (0, lazy_idx1_fetch_1.lazyIdx1Fetch)({
controller,
logLevel,
readerInterface,
src,
prefetchCache,
contentLength,
});
const sampleCounter = (0, sample_counter_1.riffSampleCounter)();
const queuedBFrames = (0, queued_frames_1.queuedBFramesState)();
return {
getAvcProfile: () => {
return avcProfile;
},
onProfile,
registerOnAvcProfileCallback,
getNextTrackIndex: () => {
return nextTrackIndex;
},
queuedBFrames,
incrementNextTrackIndex: () => {
nextTrackIndex++;
},
lazyIdx1,
sampleCounter,
};
};
exports.riffSpecificState = riffSpecificState;
@@ -0,0 +1,33 @@
import type { RiffSeekingHints } from '../../containers/riff/seeking-hints';
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 lazyIdx1Fetch: ({ controller, logLevel, readerInterface, src, prefetchCache, contentLength, }: {
controller: MediaParserController;
logLevel: MediaParserLogLevel;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
prefetchCache: PrefetchCache;
contentLength: number;
}) => {
triggerLoad: (position: number) => Promise<{
entries: import("../../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}>;
getLoadedIdx1: () => Promise<{
entries: import("../../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null>;
getIfAlreadyLoaded: () => {
entries: import("../../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
} | null;
setFromSeekingHints: (hints: RiffSeekingHints) => void;
waitForLoaded: () => Promise<{
entries: import("../../containers/riff/riff-box").Idx1Entry[];
videoTrackIndex: number | null;
}> | Promise<null>;
};
export type LazyIdx1Fetch = ReturnType<typeof lazyIdx1Fetch>;
@@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazyIdx1Fetch = void 0;
const fetch_idx1_1 = require("../../containers/riff/seek/fetch-idx1");
const lazyIdx1Fetch = ({ controller, logLevel, readerInterface, src, prefetchCache, contentLength, }) => {
let prom = null;
let result = null;
const triggerLoad = (position) => {
if (result) {
return Promise.resolve(result);
}
if (prom) {
return prom;
}
prom = (0, fetch_idx1_1.fetchIdx1)({
controller,
logLevel,
position,
readerInterface,
src,
prefetchCache,
contentLength,
}).then((entries) => {
prom = null;
result = entries;
return entries;
});
return prom;
};
const getLoadedIdx1 = async () => {
if (!prom) {
return null;
}
const entries = await prom;
return entries;
};
const getIfAlreadyLoaded = () => {
if (result) {
return result;
}
return null;
};
const setFromSeekingHints = (hints) => {
if (hints.idx1Entries) {
result = hints.idx1Entries;
}
};
const waitForLoaded = () => {
if (result) {
return Promise.resolve(result);
}
if (prom) {
return prom;
}
return Promise.resolve(null);
};
return {
triggerLoad,
getLoadedIdx1,
getIfAlreadyLoaded,
setFromSeekingHints,
waitForLoaded,
};
};
exports.lazyIdx1Fetch = lazyIdx1Fetch;
@@ -0,0 +1,20 @@
import type { MediaParserVideoSample } from '../../webcodec-sample-types';
export type QueuedVideoSample = Omit<MediaParserVideoSample, 'decodingTimestamp' | 'timestamp'>;
type QueueItem = {
sample: QueuedVideoSample;
trackId: number;
timescale: number;
};
export declare const queuedBFramesState: () => {
addFrame: ({ frame, maxFramesInBuffer, trackId, timescale, }: {
frame: QueuedVideoSample;
trackId: number;
maxFramesInBuffer: number;
timescale: number;
}) => void;
flush: () => void;
getReleasedFrame: () => QueueItem | null;
hasReleasedFrames: () => boolean;
clear: () => void;
};
export {};
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.queuedBFramesState = void 0;
const queuedBFramesState = () => {
const queuedFrames = [];
const releasedFrames = [];
const flush = () => {
releasedFrames.push(...queuedFrames);
queuedFrames.length = 0;
};
return {
addFrame: ({ frame, maxFramesInBuffer, trackId, timescale, }) => {
if (frame.type === 'key') {
flush();
releasedFrames.push({ sample: frame, trackId, timescale });
return;
}
queuedFrames.push({ sample: frame, trackId, timescale });
if (queuedFrames.length > maxFramesInBuffer) {
releasedFrames.push(queuedFrames.shift());
}
},
flush,
getReleasedFrame: () => {
if (releasedFrames.length === 0) {
return null;
}
return releasedFrames.shift();
},
hasReleasedFrames: () => {
return releasedFrames.length > 0;
},
clear: () => {
releasedFrames.length = 0;
queuedFrames.length = 0;
},
};
};
exports.queuedBFramesState = queuedBFramesState;
@@ -0,0 +1,10 @@
import type { MediaParserKeyframe } from '../../options';
export type RiffKeyframe = MediaParserKeyframe & {
sampleCounts: Record<number, number>;
};
export declare const riffKeyframesState: () => {
addKeyframe: (keyframe: RiffKeyframe) => void;
getKeyframes: () => RiffKeyframe[];
setFromSeekingHints: (keyframesFromHints: RiffKeyframe[]) => void;
};
export type RiffKeyframesState = ReturnType<typeof riffKeyframesState>;
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.riffKeyframesState = void 0;
const riffKeyframesState = () => {
const keyframes = [];
const addKeyframe = (keyframe) => {
if (keyframes.find((k) => k.positionInBytes === keyframe.positionInBytes)) {
return;
}
keyframes.push(keyframe);
keyframes.sort((a, b) => a.positionInBytes - b.positionInBytes);
};
const getKeyframes = () => {
return keyframes;
};
const setFromSeekingHints = (keyframesFromHints) => {
for (const keyframe of keyframesFromHints) {
addKeyframe(keyframe);
}
};
return {
addKeyframe,
getKeyframes,
setFromSeekingHints,
};
};
exports.riffKeyframesState = riffKeyframesState;
@@ -0,0 +1,26 @@
import type { MediaParserAudioSample, MediaParserVideoSample } from '../../webcodec-sample-types';
import type { QueuedVideoSample } from './queued-frames';
export declare const riffSampleCounter: () => {
onAudioSample: (trackId: number, audioSample: MediaParserAudioSample) => void;
onVideoSample: ({ trackId, videoSample, }: {
videoSample: MediaParserVideoSample;
trackId: number;
}) => void;
getSampleCountForTrack: ({ trackId }: {
trackId: number;
}) => number;
setSamplesFromSeek: (samples: Record<number, number>) => void;
riffKeys: {
addKeyframe: (keyframe: import("./riff-keyframes").RiffKeyframe) => void;
getKeyframes: () => import("./riff-keyframes").RiffKeyframe[];
setFromSeekingHints: (keyframesFromHints: import("./riff-keyframes").RiffKeyframe[]) => void;
};
setPocAtKeyframeOffset: ({ keyframeOffset, poc, }: {
keyframeOffset: number;
poc: number;
}) => void;
getPocAtKeyframeOffset: ({ keyframeOffset, }: {
keyframeOffset: number;
}) => number[];
getKeyframeAtOffset: (sample: QueuedVideoSample) => number | null;
};
@@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.riffSampleCounter = void 0;
const webcodecs_timescale_1 = require("../../webcodecs-timescale");
const riff_keyframes_1 = require("./riff-keyframes");
const riffSampleCounter = () => {
const samplesForTrack = {};
// keyframe offset -> poc[]
const pocsAtKeyframeOffset = {};
const riffKeys = (0, riff_keyframes_1.riffKeyframesState)();
const onAudioSample = (trackId, audioSample) => {
if (typeof samplesForTrack[trackId] === 'undefined') {
samplesForTrack[trackId] = 0;
}
if (audioSample.data.length > 0) {
samplesForTrack[trackId]++;
}
samplesForTrack[trackId]++;
};
const onVideoSample = ({ trackId, videoSample, }) => {
if (typeof samplesForTrack[trackId] === 'undefined') {
samplesForTrack[trackId] = 0;
}
if (videoSample.type === 'key') {
riffKeys.addKeyframe({
trackId,
decodingTimeInSeconds: videoSample.decodingTimestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE,
positionInBytes: videoSample.offset,
presentationTimeInSeconds: videoSample.timestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE,
sizeInBytes: videoSample.data.length,
sampleCounts: { ...samplesForTrack },
});
}
if (videoSample.data.length > 0) {
samplesForTrack[trackId]++;
}
};
const getSampleCountForTrack = ({ trackId }) => {
var _a;
return (_a = samplesForTrack[trackId]) !== null && _a !== void 0 ? _a : 0;
};
const setSamplesFromSeek = (samples) => {
for (const trackId in samples) {
samplesForTrack[trackId] = samples[trackId];
}
};
const setPocAtKeyframeOffset = ({ keyframeOffset, poc, }) => {
if (typeof pocsAtKeyframeOffset[keyframeOffset] === 'undefined') {
pocsAtKeyframeOffset[keyframeOffset] = [];
}
if (pocsAtKeyframeOffset[keyframeOffset].includes(poc)) {
return;
}
pocsAtKeyframeOffset[keyframeOffset].push(poc);
pocsAtKeyframeOffset[keyframeOffset].sort((a, b) => a - b);
};
const getPocAtKeyframeOffset = ({ keyframeOffset, }) => {
return pocsAtKeyframeOffset[keyframeOffset];
};
const getKeyframeAtOffset = (sample) => {
var _a, _b;
if (sample.type === 'key') {
return sample.offset;
}
return ((_b = (_a = riffKeys
.getKeyframes()
.findLast((k) => k.positionInBytes <= sample.offset)) === null || _a === void 0 ? void 0 : _a.positionInBytes) !== null && _b !== void 0 ? _b : null);
};
return {
onAudioSample,
onVideoSample,
getSampleCountForTrack,
setSamplesFromSeek,
riffKeys,
setPocAtKeyframeOffset,
getPocAtKeyframeOffset,
getKeyframeAtOffset,
};
};
exports.riffSampleCounter = riffSampleCounter;
@@ -0,0 +1,51 @@
import type { MediaParserController } from '../controller/media-parser-controller';
import type { SeekSignal } from '../controller/seek-signal';
import type { AllOptions, Options, ParseMediaFields } from '../fields';
import type { MediaParserLogLevel } from '../log';
import type { ParseMediaSrc } from '../options';
import type { MediaParserAudioSample, MediaParserOnAudioSample, MediaParserOnVideoSample, MediaParserVideoSample } from '../webcodec-sample-types';
import { type KeyframesState } from './keyframes';
import type { SamplesObservedState } from './samples-observed/slow-duration-fps';
import type { StructureState } from './structure';
export declare const callbacksState: ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, samplesObserved, structure, src, seekSignal, logLevel, }: {
controller: MediaParserController;
hasAudioTrackHandlers: boolean;
hasVideoTrackHandlers: boolean;
fields: Options<ParseMediaFields>;
keyframes: KeyframesState;
emittedFields: AllOptions<ParseMediaFields>;
samplesObserved: SamplesObservedState;
structure: StructureState;
src: ParseMediaSrc;
seekSignal: SeekSignal;
logLevel: MediaParserLogLevel;
}) => {
registerVideoSampleCallback: (id: number, callback: MediaParserOnVideoSample | null) => Promise<void>;
onAudioSample: ({ audioSample, trackId, }: {
trackId: number;
audioSample: MediaParserAudioSample;
}) => Promise<void>;
onVideoSample: ({ trackId, videoSample, }: {
trackId: number;
videoSample: MediaParserVideoSample;
}) => Promise<void>;
canSkipTracksState: {
doFieldsNeedTracks: () => boolean;
canSkipTracks: () => boolean;
};
registerAudioSampleCallback: (id: number, callback: MediaParserOnAudioSample | null) => Promise<void>;
tracks: {
hasAllTracks: () => boolean;
getIsDone: () => boolean;
setIsDone: (logLevel: MediaParserLogLevel) => void;
addTrack: (track: import("..").MediaParserTrack) => void;
getTracks: () => import("..").MediaParserTrack[];
ensureHasTracksAtEnd: (fields: Options<ParseMediaFields>) => void;
};
audioSampleCallbacks: Record<number, MediaParserOnAudioSample>;
videoSampleCallbacks: Record<number, MediaParserOnVideoSample>;
hasAudioTrackHandlers: boolean;
hasVideoTrackHandlers: boolean;
callTracksDoneCallback: () => Promise<void>;
};
export type CallbacksState = ReturnType<typeof callbacksState>;
@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.callbacksState = void 0;
const log_1 = require("../log");
const webcodecs_timescale_1 = require("../webcodecs-timescale");
const can_skip_tracks_1 = require("./can-skip-tracks");
const has_tracks_section_1 = require("./has-tracks-section");
const need_samples_for_fields_1 = require("./need-samples-for-fields");
const callbacksState = ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, samplesObserved, structure, src, seekSignal, logLevel, }) => {
const videoSampleCallbacks = {};
const audioSampleCallbacks = {};
const onTrackDoneCallback = {};
const queuedAudioSamples = {};
const queuedVideoSamples = {};
const canSkipTracksState = (0, can_skip_tracks_1.makeCanSkipTracksState)({
hasAudioTrackHandlers,
fields,
hasVideoTrackHandlers,
structure,
});
const tracksState = (0, has_tracks_section_1.makeTracksSectionState)(canSkipTracksState, src);
return {
registerVideoSampleCallback: async (id, callback) => {
var _a;
if (callback === null) {
delete videoSampleCallbacks[id];
return;
}
videoSampleCallbacks[id] = callback;
for (const queued of (_a = queuedVideoSamples[id]) !== null && _a !== void 0 ? _a : []) {
await callback(queued);
}
queuedVideoSamples[id] = [];
},
onAudioSample: async ({ audioSample, trackId, }) => {
if (controller._internals.signal.aborted) {
throw new Error('Aborted');
}
const callback = audioSampleCallbacks[trackId];
if (audioSample.data.length > 0) {
// If we emit samples with data length 0, Chrome will fail
if (callback) {
if (seekSignal.getSeek() !== null) {
log_1.Log.trace(logLevel, 'Not emitting sample because seek is processing');
}
else {
const trackDoneCallback = await callback(audioSample);
onTrackDoneCallback[trackId] = trackDoneCallback !== null && trackDoneCallback !== void 0 ? trackDoneCallback : null;
}
}
}
if ((0, need_samples_for_fields_1.needsToIterateOverSamples)({ emittedFields, fields })) {
samplesObserved.addAudioSample(audioSample);
}
},
onVideoSample: async ({ trackId, videoSample, }) => {
if (controller._internals.signal.aborted) {
throw new Error('Aborted');
}
if (videoSample.data.length > 0) {
const callback = videoSampleCallbacks[trackId];
// If we emit samples with data 0, Chrome will fail
if (callback) {
if (seekSignal.getSeek() !== null) {
log_1.Log.trace(logLevel, 'Not emitting sample because seek is processing');
}
else {
const trackDoneCallback = await callback(videoSample);
onTrackDoneCallback[trackId] = trackDoneCallback !== null && trackDoneCallback !== void 0 ? trackDoneCallback : null;
}
}
}
if (videoSample.type === 'key') {
keyframes.addKeyframe({
trackId,
decodingTimeInSeconds: videoSample.decodingTimestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE,
positionInBytes: videoSample.offset,
presentationTimeInSeconds: videoSample.timestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE,
sizeInBytes: videoSample.data.length,
});
}
if ((0, need_samples_for_fields_1.needsToIterateOverSamples)({
fields,
emittedFields,
})) {
samplesObserved.addVideoSample(videoSample);
}
},
canSkipTracksState,
registerAudioSampleCallback: async (id, callback) => {
var _a;
if (callback === null) {
delete audioSampleCallbacks[id];
return;
}
audioSampleCallbacks[id] = callback;
for (const queued of (_a = queuedAudioSamples[id]) !== null && _a !== void 0 ? _a : []) {
await callback(queued);
}
queuedAudioSamples[id] = [];
},
tracks: tracksState,
audioSampleCallbacks,
videoSampleCallbacks,
hasAudioTrackHandlers,
hasVideoTrackHandlers,
callTracksDoneCallback: async () => {
for (const callback of Object.values(onTrackDoneCallback)) {
if (callback) {
await callback();
}
}
},
};
};
exports.callbacksState = callbacksState;
@@ -0,0 +1,14 @@
import type { MediaParserAudioSample, MediaParserVideoSample } from '../../webcodec-sample-types';
export declare const samplesObservedState: () => {
addVideoSample: (videoSample: MediaParserVideoSample) => void;
addAudioSample: (audioSample: MediaParserAudioSample) => void;
getSlowDurationInSeconds: () => number;
getFps: () => number;
getSlowNumberOfFrames: () => number;
getAudioBitrate: () => number | null;
getVideoBitrate: () => number | null;
getLastSampleObserved: () => boolean;
setLastSampleObserved: () => void;
getAmountOfSamplesObserved: () => number;
};
export type SamplesObservedState = ReturnType<typeof samplesObservedState>;
@@ -0,0 +1,96 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.samplesObservedState = void 0;
const webcodecs_timescale_1 = require("../../webcodecs-timescale");
const samplesObservedState = () => {
let smallestVideoSample;
let largestVideoSample;
let smallestAudioSample;
let largestAudioSample;
let lastSampleObserved = false;
const videoSamples = new Map();
const audioSamples = new Map();
const getSlowVideoDurationInSeconds = () => {
return (largestVideoSample !== null && largestVideoSample !== void 0 ? largestVideoSample : 0) - (smallestVideoSample !== null && smallestVideoSample !== void 0 ? smallestVideoSample : 0);
};
const getSlowAudioDurationInSeconds = () => {
return (largestAudioSample !== null && largestAudioSample !== void 0 ? largestAudioSample : 0) - (smallestAudioSample !== null && smallestAudioSample !== void 0 ? smallestAudioSample : 0);
};
const getSlowDurationInSeconds = () => {
const smallestSample = Math.min(smallestAudioSample !== null && smallestAudioSample !== void 0 ? smallestAudioSample : Infinity, smallestVideoSample !== null && smallestVideoSample !== void 0 ? smallestVideoSample : Infinity);
const largestSample = Math.max(largestAudioSample !== null && largestAudioSample !== void 0 ? largestAudioSample : 0, largestVideoSample !== null && largestVideoSample !== void 0 ? largestVideoSample : 0);
if (smallestSample === Infinity || largestSample === Infinity) {
return 0;
}
return largestSample - smallestSample;
};
const addVideoSample = (videoSample) => {
var _a;
videoSamples.set(videoSample.timestamp, videoSample.data.byteLength);
const presentationTimeInSeconds = videoSample.timestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE;
const duration = ((_a = videoSample.duration) !== null && _a !== void 0 ? _a : 0) / webcodecs_timescale_1.WEBCODECS_TIMESCALE;
if (largestVideoSample === undefined ||
presentationTimeInSeconds > largestVideoSample) {
largestVideoSample = presentationTimeInSeconds + duration;
}
if (smallestVideoSample === undefined ||
presentationTimeInSeconds < smallestVideoSample) {
smallestVideoSample = presentationTimeInSeconds;
}
};
const addAudioSample = (audioSample) => {
var _a;
audioSamples.set(audioSample.timestamp, audioSample.data.byteLength);
const presentationTimeInSeconds = audioSample.timestamp / webcodecs_timescale_1.WEBCODECS_TIMESCALE;
const duration = ((_a = audioSample.duration) !== null && _a !== void 0 ? _a : 0) / webcodecs_timescale_1.WEBCODECS_TIMESCALE;
if (largestAudioSample === undefined ||
presentationTimeInSeconds > largestAudioSample) {
largestAudioSample = presentationTimeInSeconds + duration;
}
if (smallestAudioSample === undefined ||
presentationTimeInSeconds < smallestAudioSample) {
smallestAudioSample = presentationTimeInSeconds;
}
};
const getFps = () => {
const videoDuration = (largestVideoSample !== null && largestVideoSample !== void 0 ? largestVideoSample : 0) - (smallestVideoSample !== null && smallestVideoSample !== void 0 ? smallestVideoSample : 0);
if (videoDuration === 0) {
return 0;
}
return (videoSamples.size - 1) / videoDuration;
};
const getSlowNumberOfFrames = () => videoSamples.size;
const getAudioBitrate = () => {
const audioDuration = getSlowAudioDurationInSeconds();
if (audioDuration === 0 || audioSamples.size === 0) {
return null;
}
const audioSizesInBytes = Array.from(audioSamples.values()).reduce((acc, size) => acc + size, 0);
return (audioSizesInBytes * 8) / audioDuration;
};
const getVideoBitrate = () => {
const videoDuration = getSlowVideoDurationInSeconds();
if (videoDuration === 0 || videoSamples.size === 0) {
return null;
}
const videoSizesInBytes = Array.from(videoSamples.values()).reduce((acc, size) => acc + size, 0);
return (videoSizesInBytes * 8) / videoDuration;
};
const getLastSampleObserved = () => lastSampleObserved;
const setLastSampleObserved = () => {
lastSampleObserved = true;
};
return {
addVideoSample,
addAudioSample,
getSlowDurationInSeconds,
getFps,
getSlowNumberOfFrames,
getAudioBitrate,
getVideoBitrate,
getLastSampleObserved,
setLastSampleObserved,
getAmountOfSamplesObserved: () => videoSamples.size + audioSamples.size,
};
};
exports.samplesObservedState = samplesObservedState;
@@ -0,0 +1,5 @@
export declare const seekInfiniteLoopDetectionState: () => {
registerSeek: (byte: number) => void;
reset: () => void;
};
export type SeekInfiniteLoop = ReturnType<typeof seekInfiniteLoopDetectionState>;
@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.seekInfiniteLoopDetectionState = void 0;
const seekInfiniteLoopDetectionState = () => {
let lastSeek = null;
let firstSeekTime = null;
return {
registerSeek: (byte) => {
const now = Date.now();
if (!lastSeek || lastSeek.byte !== byte) {
lastSeek = { byte, numberOfTimes: 1 };
firstSeekTime = now;
return;
}
lastSeek.numberOfTimes++;
if (lastSeek.numberOfTimes >= 10 &&
firstSeekTime &&
now - firstSeekTime <= 2000) {
throw new Error(`Seeking infinite loop detected: Seeked to byte 0x${byte.toString(16)} ${lastSeek.numberOfTimes} times in a row in the last 2 seconds. Check your usage of .seek().`);
}
if (now - firstSeekTime > 2000) {
lastSeek = { byte, numberOfTimes: 1 };
firstSeekTime = now;
}
},
reset: () => {
lastSeek = null;
firstSeekTime = null;
},
};
};
exports.seekInfiniteLoopDetectionState = seekInfiniteLoopDetectionState;
@@ -0,0 +1,15 @@
import type { MediaParserStructureUnstable } from '../parse-result';
export declare const structureState: () => {
getStructureOrNull: () => MediaParserStructureUnstable | null;
getStructure: () => MediaParserStructureUnstable;
setStructure: (value: MediaParserStructureUnstable) => void;
getFlacStructure: () => import("../containers/flac/types").FlacStructure;
getIsoStructure: () => import("../parse-result").IsoBaseMediaStructure;
getMp3Structure: () => import("../parse-result").Mp3Structure;
getM3uStructure: () => import("../containers/m3u/types").M3uStructure;
getRiffStructure: () => import("../containers/riff/riff-box").RiffStructure;
getTsStructure: () => import("../parse-result").TransportStreamStructure;
getWavStructure: () => import("../containers/wav/types").WavStructure;
getMatroskaStructure: () => import("../parse-result").MatroskaStructure;
};
export type StructureState = ReturnType<typeof structureState>;
@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.structureState = void 0;
const structureState = () => {
let structure = null;
const getStructure = () => {
if (structure === null) {
throw new Error('Expected structure');
}
return structure;
};
return {
getStructureOrNull: () => {
return structure;
},
getStructure,
setStructure: (value) => {
structure = value;
},
getFlacStructure: () => {
const struc = getStructure();
if (struc.type !== 'flac') {
throw new Error('Invalid structure type');
}
return struc;
},
getIsoStructure: () => {
const struc = getStructure();
if (struc.type !== 'iso-base-media') {
throw new Error('Invalid structure type');
}
return struc;
},
getMp3Structure: () => {
const struc = getStructure();
if (struc.type !== 'mp3') {
throw new Error('Invalid structure type');
}
return struc;
},
getM3uStructure: () => {
const struc = getStructure();
if (struc.type !== 'm3u') {
throw new Error('Invalid structure type');
}
return struc;
},
getRiffStructure: () => {
const struc = getStructure();
if (struc.type !== 'riff') {
throw new Error('Invalid structure type');
}
return struc;
},
getTsStructure: () => {
const struc = getStructure();
if (struc.type !== 'transport-stream') {
throw new Error('Invalid structure type');
}
return struc;
},
getWavStructure: () => {
const struc = getStructure();
if (struc.type !== 'wav') {
throw new Error('Invalid structure type');
}
return struc;
},
getMatroskaStructure: () => {
const struc = getStructure();
if (struc.type !== 'matroska') {
throw new Error('Invalid structure type');
}
return struc;
},
};
};
exports.structureState = structureState;
@@ -0,0 +1,9 @@
export declare const timingsState: () => {
timeIterating: number;
timeReadingData: number;
timeSeeking: number;
timeCheckingIfDone: number;
timeFreeingData: number;
timeInParseLoop: number;
};
export type TimingsState = ReturnType<typeof timingsState>;
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.timingsState = void 0;
const timingsState = () => {
return {
timeIterating: 0,
timeReadingData: 0,
timeSeeking: 0,
timeCheckingIfDone: 0,
timeFreeingData: 0,
timeInParseLoop: 0,
};
};
exports.timingsState = timingsState;
@@ -0,0 +1,6 @@
import type { MediaParserAudioSample, MediaParserVideoSample } from '../../webcodec-sample-types';
export declare const lastEmittedSampleState: () => {
setLastEmittedSample: (sample: MediaParserAudioSample | MediaParserVideoSample) => void;
getLastEmittedSample: () => MediaParserVideoSample | MediaParserAudioSample | null;
resetLastEmittedSample: () => void;
};
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lastEmittedSampleState = void 0;
const lastEmittedSampleState = () => {
let lastEmittedSample = null;
return {
setLastEmittedSample: (sample) => {
lastEmittedSample = sample;
},
getLastEmittedSample: () => lastEmittedSample,
resetLastEmittedSample: () => {
lastEmittedSample = null;
},
};
};
exports.lastEmittedSampleState = lastEmittedSampleState;
@@ -0,0 +1,6 @@
import type { PacketPes } from '../../containers/transport-stream/parse-pes';
export declare const makeNextPesHeaderStore: () => {
setNextPesHeader: (pesHeader: PacketPes) => void;
getNextPesHeader: () => PacketPes;
};
export type NextPesHeaderStore = ReturnType<typeof makeNextPesHeaderStore>;
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeNextPesHeaderStore = void 0;
const makeNextPesHeaderStore = () => {
let nextPesHeader = null;
return {
setNextPesHeader: (pesHeader) => {
nextPesHeader = pesHeader;
},
getNextPesHeader: () => {
if (!nextPesHeader) {
throw new Error('No next PES header found');
}
return nextPesHeader;
},
};
};
exports.makeNextPesHeaderStore = makeNextPesHeaderStore;
@@ -0,0 +1,15 @@
import type { PacketPes } from '../../containers/transport-stream/parse-pes';
import type { TransportStreamSeekingHints } from '../../seeking-hints';
export declare const makeObservedPesHeader: () => {
pesHeaders: PacketPes[];
addPesHeader: (pesHeader: PacketPes) => void;
markPtsAsKeyframe: (pts: number) => void;
getPesKeyframeHeaders: () => PacketPes[];
setPesKeyframesFromSeekingHints: (hints: TransportStreamSeekingHints) => void;
};
export type ObservedPesHeaderState = ReturnType<typeof makeObservedPesHeader>;
export declare const getLastKeyFrameBeforeTimeInSeconds: ({ observedPesHeaders, timeInSeconds, ptsStartOffset, }: {
observedPesHeaders: ObservedPesHeaderState["pesHeaders"];
timeInSeconds: number;
ptsStartOffset: number;
}) => PacketPes | undefined;
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLastKeyFrameBeforeTimeInSeconds = exports.makeObservedPesHeader = void 0;
const handle_avc_packet_1 = require("../../containers/transport-stream/handle-avc-packet");
const makeObservedPesHeader = () => {
const pesHeaders = [];
const confirmedAsKeyframe = [];
const addPesHeader = (pesHeader) => {
if (pesHeaders.find((p) => p.offset === pesHeader.offset)) {
return;
}
pesHeaders.push(pesHeader);
};
const markPtsAsKeyframe = (pts) => {
confirmedAsKeyframe.push(pts);
};
const getPesKeyframeHeaders = () => {
return pesHeaders.filter((p) => confirmedAsKeyframe.includes(p.pts));
};
const setPesKeyframesFromSeekingHints = (hints) => {
for (const pesHeader of hints.observedPesHeaders) {
addPesHeader(pesHeader);
markPtsAsKeyframe(pesHeader.pts);
}
};
const state = {
pesHeaders,
addPesHeader,
markPtsAsKeyframe,
getPesKeyframeHeaders,
setPesKeyframesFromSeekingHints,
};
return state;
};
exports.makeObservedPesHeader = makeObservedPesHeader;
const getLastKeyFrameBeforeTimeInSeconds = ({ observedPesHeaders, timeInSeconds, ptsStartOffset, }) => {
return observedPesHeaders.findLast((k) => (k.pts - ptsStartOffset) / handle_avc_packet_1.MPEG_TIMESCALE <= timeInSeconds);
};
exports.getLastKeyFrameBeforeTimeInSeconds = getLastKeyFrameBeforeTimeInSeconds;
@@ -0,0 +1,8 @@
export declare const ptsStartOffsetStore: () => {
getOffset: (trackId: number) => number;
setOffset: ({ newOffset, trackId }: {
trackId: number;
newOffset: number;
}) => void;
};
export type PtsStartOffsetState = ReturnType<typeof ptsStartOffsetStore>;
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ptsStartOffsetStore = void 0;
const ptsStartOffsetStore = () => {
const offsets = {};
return {
getOffset: (trackId) => offsets[trackId] || 0,
setOffset: ({ newOffset, trackId }) => {
offsets[trackId] = newOffset;
},
};
};
exports.ptsStartOffsetStore = ptsStartOffsetStore;
@@ -0,0 +1,29 @@
import type { TransportStreamPacketBuffer } from '../../containers/transport-stream/process-stream-buffers';
export declare const transportStreamState: () => {
nextPesHeaderStore: {
setNextPesHeader: (pesHeader: import("../../containers/transport-stream/parse-pes").PacketPes) => void;
getNextPesHeader: () => import("../../containers/transport-stream/parse-pes").PacketPes;
};
observedPesHeaders: {
pesHeaders: import("../../containers/transport-stream/parse-pes").PacketPes[];
addPesHeader: (pesHeader: import("../../containers/transport-stream/parse-pes").PacketPes) => void;
markPtsAsKeyframe: (pts: number) => void;
getPesKeyframeHeaders: () => import("../../containers/transport-stream/parse-pes").PacketPes[];
setPesKeyframesFromSeekingHints: (hints: import("../../seeking-hints").TransportStreamSeekingHints) => void;
};
streamBuffers: Map<number, TransportStreamPacketBuffer>;
startOffset: {
getOffset: (trackId: number) => number;
setOffset: ({ newOffset, trackId }: {
trackId: number;
newOffset: number;
}) => void;
};
resetBeforeSeek: () => void;
lastEmittedSample: {
setLastEmittedSample: (sample: import("../..").MediaParserAudioSample | import("../..").MediaParserVideoSample) => void;
getLastEmittedSample: () => import("../..").MediaParserVideoSample | import("../..").MediaParserAudioSample | null;
resetLastEmittedSample: () => void;
};
};
export type TransportStreamState = ReturnType<typeof transportStreamState>;
@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transportStreamState = void 0;
const last_emitted_sample_1 = require("./last-emitted-sample");
const next_pes_header_store_1 = require("./next-pes-header-store");
const observed_pes_header_1 = require("./observed-pes-header");
const pts_start_offset_1 = require("./pts-start-offset");
const transportStreamState = () => {
const streamBuffers = new Map();
const startOffset = (0, pts_start_offset_1.ptsStartOffsetStore)();
const lastEmittedSample = (0, last_emitted_sample_1.lastEmittedSampleState)();
const state = {
nextPesHeaderStore: (0, next_pes_header_store_1.makeNextPesHeaderStore)(),
observedPesHeaders: (0, observed_pes_header_1.makeObservedPesHeader)(),
streamBuffers,
startOffset,
resetBeforeSeek: () => {
state.streamBuffers.clear();
state.nextPesHeaderStore = (0, next_pes_header_store_1.makeNextPesHeaderStore)();
// start offset is useful, we can keep it
},
lastEmittedSample,
};
return state;
};
exports.transportStreamState = transportStreamState;
@@ -0,0 +1,34 @@
/**
* Keeps track of in which section of the file the video is playing
* Usually this section is in a different format and it is the only section
* that can be read partially
*/
import type { BufferIterator } from '../iterator/buffer-iterator';
export type MediaSection = {
start: number;
size: number;
};
export declare const isByteInMediaSection: ({ position, mediaSections, }: {
position: number;
mediaSections: MediaSection[];
}) => "no-section-defined" | "in-section" | "outside-section";
export declare const getCurrentMediaSection: ({ offset, mediaSections, }: {
offset: number;
mediaSections: MediaSection[];
}) => MediaSection | null;
export declare const mediaSectionState: () => {
addMediaSection: (section: MediaSection) => void;
getMediaSections: () => MediaSection[];
isCurrentByteInMediaSection: (iterator: BufferIterator) => "no-section-defined" | "in-section" | "outside-section";
isByteInMediaSection: ({ position, mediaSections, }: {
position: number;
mediaSections: MediaSection[];
}) => "no-section-defined" | "in-section" | "outside-section";
getCurrentMediaSection: ({ offset, mediaSections, }: {
offset: number;
mediaSections: MediaSection[];
}) => MediaSection | null;
getMediaSectionAssertOnlyOne: () => MediaSection;
mediaSections: MediaSection[];
};
export type MediaSectionState = ReturnType<typeof mediaSectionState>;
@@ -0,0 +1,78 @@
"use strict";
/**
* Keeps track of in which section of the file the video is playing
* Usually this section is in a different format and it is the only section
* that can be read partially
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.mediaSectionState = exports.getCurrentMediaSection = exports.isByteInMediaSection = void 0;
const isByteInMediaSection = ({ position, mediaSections, }) => {
if (mediaSections.length === 0) {
return 'no-section-defined';
}
for (const section of mediaSections) {
if (position >= section.start && position < section.start + section.size) {
return 'in-section';
}
}
return 'outside-section';
};
exports.isByteInMediaSection = isByteInMediaSection;
const getCurrentMediaSection = ({ offset, mediaSections, }) => {
for (const section of mediaSections) {
if (offset >= section.start && offset < section.start + section.size) {
return section;
}
}
return null;
};
exports.getCurrentMediaSection = getCurrentMediaSection;
const mediaSectionState = () => {
const mediaSections = [];
const addMediaSection = (section) => {
// Check if section overlaps with any existing sections
const overlaps = mediaSections.some((existingSection) => section.start < existingSection.start + existingSection.size &&
section.start + section.size > existingSection.start);
if (overlaps) {
return;
}
// Remove any existing sections that are encompassed by the new section
// Needed by Matroska because we need to define a 1 byte media section
// when seeking into a Cluster we have not seen yet
for (let i = mediaSections.length - 1; i >= 0; i--) {
const existingSection = mediaSections[i];
if (section.start <= existingSection.start &&
section.start + section.size >=
existingSection.start + existingSection.size) {
mediaSections.splice(i, 1);
}
}
mediaSections.push(section);
};
const getMediaSections = () => {
return mediaSections;
};
const isCurrentByteInMediaSection = (iterator) => {
const offset = iterator.counter.getOffset();
return (0, exports.isByteInMediaSection)({
position: offset,
mediaSections,
});
};
const getMediaSectionAssertOnlyOne = () => {
if (mediaSections.length !== 1) {
throw new Error('Expected only one video section');
}
return mediaSections[0];
};
return {
addMediaSection,
getMediaSections,
isCurrentByteInMediaSection,
isByteInMediaSection: exports.isByteInMediaSection,
getCurrentMediaSection: exports.getCurrentMediaSection,
getMediaSectionAssertOnlyOne,
mediaSections,
};
};
exports.mediaSectionState = mediaSectionState;