init commit

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

View File

@@ -0,0 +1,6 @@
import type { MoofBox } from '../../state/iso-base-media/precomputed-moof';
import type { TfraBox } from './mfra/tfra';
export declare const areSamplesComplete: ({ moofBoxes, tfraBoxes, }: {
moofBoxes: MoofBox[];
tfraBoxes: TfraBox[];
}) => boolean;

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.areSamplesComplete = void 0;
const areSamplesComplete = ({ moofBoxes, tfraBoxes, }) => {
if (moofBoxes.length === 0) {
return true;
}
return (tfraBoxes.length > 0 &&
tfraBoxes.every((t) => t.entries.length === moofBoxes.length));
};
exports.areSamplesComplete = areSamplesComplete;

View File

@@ -0,0 +1,40 @@
import type { BaseBox } from './base-type';
import type { ElstBox } from './elst';
import type { EsdsBox } from './esds/esds';
import type { FtypBox } from './ftyp';
import type { MdhdBox } from './mdhd';
import type { HdlrBox } from './meta/hdlr';
import type { IlstBox } from './meta/ilst';
import type { TfraBox } from './mfra/tfra';
import type { MoovBox } from './moov/moov';
import type { MvhdBox } from './moov/mvhd';
import type { TrexBox } from './moov/trex';
import type { Av1CBox } from './stsd/av1c';
import type { AvccBox } from './stsd/avcc';
import type { ColorParameterBox } from './stsd/colr';
import type { CttsBox } from './stsd/ctts';
import type { HvccBox } from './stsd/hvcc';
import type { KeysBox } from './stsd/keys';
import type { MebxBox } from './stsd/mebx';
import type { PaspBox } from './stsd/pasp';
import type { StcoBox } from './stsd/stco';
import type { StscBox } from './stsd/stsc';
import type { StsdBox } from './stsd/stsd';
import type { StssBox } from './stsd/stss';
import type { StszBox } from './stsd/stsz';
import type { SttsBox } from './stsd/stts';
import type { VpccBox } from './stsd/vpcc';
import type { TfdtBox } from './tfdt';
import type { TfhdBox } from './tfhd';
import type { TkhdBox } from './tkhd';
import type { TrakBox } from './trak/trak';
import type { TrunBox } from './trun';
import type { VoidBox } from './void-box';
export interface RegularBox extends BaseBox {
boxType: string;
boxSize: number;
children: IsoBaseMediaBox[];
offset: number;
type: 'regular-box';
}
export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | ElstBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox | MdhdBox | IlstBox | EsdsBox | StszBox | StcoBox | StscBox | AvccBox | HvccBox | VpccBox | VoidBox | StssBox | PaspBox | CttsBox | Av1CBox | TrunBox | HdlrBox | ColorParameterBox | TfdtBox | TfhdBox | TfraBox | TrexBox;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,4 @@
export interface BaseBox {
boxSize: number;
offset: number;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,15 @@
import type { MoofBox } from '../../state/iso-base-media/precomputed-moof';
import type { TrexBox } from './moov/trex';
import type { TkhdBox } from './tkhd';
export declare const collectSamplePositionsFromMoofBoxes: ({ moofBoxes, tkhdBox, isComplete, trexBoxes, }: {
moofBoxes: MoofBox[];
tkhdBox: TkhdBox;
isComplete: boolean;
trexBoxes: TrexBox[];
}) => {
samplePositions: {
isLastFragment: boolean;
samples: import("../../get-sample-positions").SamplePosition[];
}[];
isComplete: boolean;
};

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectSamplePositionsFromMoofBoxes = void 0;
const samples_from_moof_1 = require("../../samples-from-moof");
const collectSamplePositionsFromMoofBoxes = ({ moofBoxes, tkhdBox, isComplete, trexBoxes, }) => {
const samplePositions = moofBoxes.map((m, index) => {
const isLastFragment = index === moofBoxes.length - 1 && isComplete;
return {
isLastFragment,
samples: (0, samples_from_moof_1.getSamplesFromMoof)({
moofBox: m,
trackId: tkhdBox.trackId,
trexBoxes,
}),
};
});
return { samplePositions, isComplete };
};
exports.collectSamplePositionsFromMoofBoxes = collectSamplePositionsFromMoofBoxes;

View File

@@ -0,0 +1,2 @@
import type { TrakBox } from './trak/trak';
export declare const collectSamplePositionsFromTrak: (trakBox: TrakBox) => import("../../get-sample-positions").SamplePosition[];

View File

@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectSamplePositionsFromTrak = void 0;
const get_fps_1 = require("../../get-fps");
const get_sample_positions_1 = require("../../get-sample-positions");
const get_sample_positions_from_mp4_1 = require("../../get-sample-positions-from-mp4");
const should_group_audio_samples_1 = require("./should-group-audio-samples");
const traversal_1 = require("./traversal");
const collectSamplePositionsFromTrak = (trakBox) => {
const shouldGroupSamples = (0, should_group_audio_samples_1.shouldGroupAudioSamples)(trakBox);
const timescaleAndDuration = (0, get_fps_1.getTimescaleAndDuration)(trakBox);
if (shouldGroupSamples) {
return (0, get_sample_positions_from_mp4_1.getGroupedSamplesPositionsFromMp4)({
trakBox,
bigEndian: shouldGroupSamples.bigEndian,
});
}
const stszBox = (0, traversal_1.getStszBox)(trakBox);
const stcoBox = (0, traversal_1.getStcoBox)(trakBox);
const stscBox = (0, traversal_1.getStscBox)(trakBox);
const stssBox = (0, traversal_1.getStssBox)(trakBox);
const sttsBox = (0, traversal_1.getSttsBox)(trakBox);
const cttsBox = (0, traversal_1.getCttsBox)(trakBox);
if (!stszBox) {
throw new Error('Expected stsz box in trak box');
}
if (!stcoBox) {
throw new Error('Expected stco box in trak box');
}
if (!stscBox) {
throw new Error('Expected stsc box in trak box');
}
if (!sttsBox) {
throw new Error('Expected stts box in trak box');
}
if (!timescaleAndDuration) {
throw new Error('Expected timescale and duration in trak box');
}
const samplePositions = (0, get_sample_positions_1.getSamplePositions)({
stcoBox,
stscBox,
stszBox,
stssBox,
sttsBox,
cttsBox,
});
return samplePositions;
};
exports.collectSamplePositionsFromTrak = collectSamplePositionsFromTrak;

View File

@@ -0,0 +1,2 @@
import type { MediaParserAdvancedColor } from '../../get-tracks';
export declare const mediaParserAdvancedColorToWebCodecsColor: (color: MediaParserAdvancedColor) => VideoColorSpaceInit;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mediaParserAdvancedColorToWebCodecsColor = void 0;
const mediaParserAdvancedColorToWebCodecsColor = (color) => {
return {
transfer: color.transfer,
matrix: color.matrix,
primaries: color.primaries,
fullRange: color.fullRange,
};
};
exports.mediaParserAdvancedColorToWebCodecsColor = mediaParserAdvancedColorToWebCodecsColor;

View File

@@ -0,0 +1,19 @@
import type { BufferIterator } from '../../iterator/buffer-iterator';
import type { BaseBox } from './base-type';
export interface ElstBox extends BaseBox {
type: 'elst-box';
version: number;
flags: number;
entries: ElstEntry[];
}
export type ElstEntry = {
editDuration: number;
mediaTime: number;
mediaRateInteger: number;
mediaRateFraction: number;
};
export declare const parseElst: ({ iterator, size, offset, }: {
iterator: BufferIterator;
size: number;
offset: number;
}) => ElstBox;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseElst = void 0;
const parseElst = ({ iterator, size, offset, }) => {
const { discardRest } = iterator.startBox(size - 8);
const version = iterator.getUint8();
const flags = iterator.getUint24();
const entryCount = iterator.getUint32();
const entries = [];
for (let i = 0; i < entryCount; i++) {
const editDuration = Number(version === 1 ? iterator.getUint64() : iterator.getUint32());
const mediaTime = Number(version === 1 ? iterator.getUint64() : iterator.getInt32());
const mediaRateInteger = iterator.getUint16();
const mediaRateFraction = iterator.getUint16();
entries.push({
editDuration,
mediaTime,
mediaRateInteger,
mediaRateFraction,
});
}
discardRest();
const result = {
type: 'elst-box',
version,
flags,
entries,
boxSize: size,
offset,
};
return result;
};
exports.parseElst = parseElst;

View File

@@ -0,0 +1,14 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
type UnknownDecoderSpecificConfig = {
type: 'unknown-decoder-specific-config';
};
type AudioSpecificConfig = {
type: 'mp4a-specific-config';
audioObjectType: number;
samplingFrequencyIndex: number;
channelConfiguration: number;
asBytes: Uint8Array;
};
export type DecoderSpecificConfig = UnknownDecoderSpecificConfig | AudioSpecificConfig;
export declare const parseDecoderSpecificConfig: (iterator: BufferIterator) => DecoderSpecificConfig;
export {};

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseDecoderSpecificConfig = void 0;
const parseDecoderSpecificConfig = (iterator) => {
const layerTag = iterator.getUint8();
const layerSize = iterator.getPaddedFourByteNumber();
const start = iterator.counter.getOffset();
if (layerTag !== 5) {
iterator.discard(layerSize);
return {
type: 'unknown-decoder-specific-config',
};
}
// https://csclub.uwaterloo.ca/~pbarfuss/ISO14496-3-2009.pdf
// 1.6.2.1 AudioSpecificConfig
const bytes = iterator.getSlice(layerSize);
iterator.counter.decrement(layerSize);
iterator.startReadingBits();
const audioObjectType = iterator.getBits(5);
const samplingFrequencyIndex = iterator.getBits(4);
if (samplingFrequencyIndex === 0xf) {
iterator.getBits(24);
}
const channelConfiguration = iterator.getBits(4);
iterator.stopReadingBits();
const read = iterator.counter.getOffset() - start;
if (read < layerSize) {
iterator.discard(layerSize - read);
}
return {
type: 'mp4a-specific-config',
audioObjectType,
samplingFrequencyIndex,
channelConfiguration,
asBytes: bytes,
};
};
exports.parseDecoderSpecificConfig = parseDecoderSpecificConfig;

View File

@@ -0,0 +1,29 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { DecoderSpecificConfig } from './decoder-specific-config';
type AudioObjectType = 'aac' | 'mp3' | 'unknown';
type DecoderConfigDescriptor = {
type: 'decoder-config-descriptor';
objectTypeIndication: AudioObjectType;
asNumber: number;
streamType: number;
upStream: number;
bufferSizeDB: number;
maxBitrate: number;
avgBitrate: number;
decoderSpecificConfigs: DecoderSpecificConfig[];
};
type SlConfigDescriptor = {
type: 'sl-config-descriptor';
};
type UnknownDescriptor = {
type: 'unknown-descriptor';
};
export type Descriptor = DecoderConfigDescriptor | SlConfigDescriptor | UnknownDescriptor;
type DescriptorAndNext = {
descriptor: Descriptor | null;
};
export declare const processDescriptor: ({ iterator, }: {
iterator: BufferIterator;
}) => DescriptorAndNext;
export declare const parseDescriptors: (iterator: BufferIterator, maxBytes: number) => Descriptor[];
export {};

View File

@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseDescriptors = exports.processDescriptor = void 0;
const decoder_specific_config_1 = require("./decoder-specific-config");
const mapToObjectAudioIndicator = (num) => {
// https://chromium.googlesource.com/chromium/src/media/+/master/formats/mp4/es_descriptor.h
// http://netmedia.zju.edu.cn/multimedia2013/mpeg-4/ISO%20IEC%2014496-1%20MPEG-4%20System%20Standard.pdf
// Page 42, table 8
if (num === 0x40) {
return 'aac';
}
if (num === 0x6b) {
return 'mp3';
}
return 'unknown';
};
const processDescriptor = ({ iterator, }) => {
const tag = iterator.getUint8();
if (tag === 4) {
const size = iterator.getPaddedFourByteNumber();
const initialOffset = iterator.counter.getOffset();
const objectTypeIndication = iterator.getUint8();
iterator.startReadingBits();
const streamType = iterator.getBits(6);
const upStream = iterator.getBits(1);
// reserved
iterator.getBits(1);
const bufferSizeDB = iterator.getBits(24);
iterator.stopReadingBits();
const maxBitrate = iterator.getUint32();
const avgBitrate = iterator.getUint32();
const decoderSpecificConfigs = [];
while (size - (iterator.counter.getOffset() - initialOffset) > 0) {
const decoderSpecificConfig = (0, decoder_specific_config_1.parseDecoderSpecificConfig)(iterator);
decoderSpecificConfigs.push(decoderSpecificConfig);
}
return {
descriptor: {
type: 'decoder-config-descriptor',
objectTypeIndication: mapToObjectAudioIndicator(objectTypeIndication),
asNumber: objectTypeIndication,
bufferSizeDB,
streamType,
upStream,
avgBitrate,
maxBitrate,
decoderSpecificConfigs,
},
};
}
if (tag === 6) {
const size = iterator.getPaddedFourByteNumber();
iterator.discard(size);
return {
descriptor: {
type: 'sl-config-descriptor',
},
};
}
return {
descriptor: null,
};
};
exports.processDescriptor = processDescriptor;
const parseDescriptors = (iterator, maxBytes) => {
const descriptors = [];
const initialOffset = iterator.counter.getOffset();
while (iterator.bytesRemaining() > 0 &&
iterator.counter.getOffset() - initialOffset < maxBytes) {
const { descriptor } = (0, exports.processDescriptor)({
iterator,
});
if (descriptor) {
descriptors.push(descriptor);
}
else {
break;
}
}
return descriptors;
};
exports.parseDescriptors = parseDescriptors;

View File

@@ -0,0 +1,15 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { Descriptor } from './esds-descriptors';
export interface EsdsBox {
type: 'esds-box';
version: number;
tag: number;
sizeOfInstance: number;
esId: number;
descriptors: Descriptor[];
}
export declare const parseEsds: ({ data, size, fileOffset, }: {
data: BufferIterator;
size: number;
fileOffset: number;
}) => EsdsBox;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseEsds = void 0;
const esds_descriptors_1 = require("./esds-descriptors");
const parseEsds = ({ data, size, fileOffset, }) => {
const version = data.getUint8();
// Flags, we discard them
data.discard(3);
const tag = data.getUint8();
const sizeOfInstance = data.getPaddedFourByteNumber();
const esId = data.getUint16();
// disard 1 byte, currently unknown
data.discard(1);
const remaining = size - (data.counter.getOffset() - fileOffset);
const descriptors = (0, esds_descriptors_1.parseDescriptors)(data, remaining);
const remainingNow = size - (data.counter.getOffset() - fileOffset);
data.discard(remainingNow);
return {
type: 'esds-box',
version,
tag,
sizeOfInstance,
esId,
descriptors,
};
};
exports.parseEsds = parseEsds;

View File

@@ -0,0 +1,11 @@
import type { SamplePosition } from '../../get-sample-positions';
import type { MediaParserLogLevel } from '../../log';
import type { MediaSection } from '../../state/video-section';
export declare const findKeyframeBeforeTime: ({ samplePositions, time, timescale, mediaSections, logLevel, startInSeconds, }: {
samplePositions: SamplePosition[];
time: number;
timescale: number;
mediaSections: MediaSection[];
logLevel: MediaParserLogLevel;
startInSeconds: number;
}) => SamplePosition | null;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findKeyframeBeforeTime = void 0;
const log_1 = require("../../log");
const findKeyframeBeforeTime = ({ samplePositions, time, timescale, mediaSections, logLevel, startInSeconds, }) => {
let videoByte = 0;
let videoSample = null;
for (const sample of samplePositions) {
const ctsInSeconds = sample.timestamp / timescale + startInSeconds;
const dtsInSeconds = sample.decodingTimestamp / timescale + startInSeconds;
if (!sample.isKeyframe) {
continue;
}
if (!(ctsInSeconds <= time || dtsInSeconds <= time)) {
continue;
}
if (videoByte <= sample.offset) {
videoByte = sample.offset;
videoSample = sample;
}
}
if (!videoSample) {
throw new Error('No sample found');
}
const mediaSection = mediaSections.find((section) => videoSample.offset >= section.start &&
videoSample.offset < section.start + section.size);
if (!mediaSection) {
log_1.Log.trace(logLevel, 'Found a sample, but the offset has not yet been marked as a video section yet. Not yet able to seek, but probably once we have started reading the next box.', videoSample);
return null;
}
return videoSample;
};
exports.findKeyframeBeforeTime = findKeyframeBeforeTime;

View File

@@ -0,0 +1,14 @@
import type { SamplePosition } from '../../get-sample-positions';
import type { MediaParserAudioTrack, MediaParserOtherTrack, MediaParserVideoTrack } from '../../get-tracks';
import type { IsoBaseMediaStructure } from '../../parse-result';
import type { StructureState } from '../../state/structure';
export declare const findAnyTrackWithSamplePositions: (allTracks: (MediaParserVideoTrack | MediaParserAudioTrack | MediaParserOtherTrack)[], struc: IsoBaseMediaStructure) => {
track: MediaParserVideoTrack | MediaParserAudioTrack;
samplePositions: SamplePosition[];
} | null;
type TrackWithSamplePositions = {
track: MediaParserVideoTrack | MediaParserAudioTrack;
samplePositions: SamplePosition[];
};
export declare const findTrackToSeek: (allTracks: (MediaParserVideoTrack | MediaParserAudioTrack | MediaParserOtherTrack)[], structure: StructureState) => TrackWithSamplePositions | null;
export {};

View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findTrackToSeek = exports.findAnyTrackWithSamplePositions = void 0;
const are_samples_complete_1 = require("./are-samples-complete");
const get_sample_positions_from_track_1 = require("./get-sample-positions-from-track");
const traversal_1 = require("./traversal");
const findAnyTrackWithSamplePositions = (allTracks, struc) => {
const moov = (0, traversal_1.getMoovFromFromIsoStructure)(struc);
if (!moov) {
return null;
}
for (const track of allTracks) {
if (track.type === 'video' || track.type === 'audio') {
const trakBox = (0, traversal_1.getTrakBoxByTrackId)(moov, track.trackId);
if (!trakBox) {
continue;
}
const { samplePositions } = (0, get_sample_positions_from_track_1.getSamplePositionsFromTrack)({
trakBox,
moofBoxes: (0, traversal_1.getMoofBoxes)(struc.boxes),
moofComplete: (0, are_samples_complete_1.areSamplesComplete)({
moofBoxes: (0, traversal_1.getMoofBoxes)(struc.boxes),
tfraBoxes: (0, traversal_1.getTfraBoxes)(struc.boxes),
}),
trexBoxes: (0, traversal_1.getTrexBoxes)(moov),
});
if (samplePositions.length === 0) {
continue;
}
return { track, samplePositions };
}
}
return null;
};
exports.findAnyTrackWithSamplePositions = findAnyTrackWithSamplePositions;
const findTrackToSeek = (allTracks, structure) => {
const firstVideoTrack = allTracks.find((t) => t.type === 'video');
const struc = structure.getIsoStructure();
if (!firstVideoTrack) {
return (0, exports.findAnyTrackWithSamplePositions)(allTracks, struc);
}
const moov = (0, traversal_1.getMoovFromFromIsoStructure)(struc);
if (!moov) {
return null;
}
const trakBox = (0, traversal_1.getTrakBoxByTrackId)(moov, firstVideoTrack.trackId);
if (!trakBox) {
return null;
}
const { samplePositions } = (0, get_sample_positions_from_track_1.getSamplePositionsFromTrack)({
trakBox,
moofBoxes: (0, traversal_1.getMoofBoxes)(struc.boxes),
moofComplete: (0, are_samples_complete_1.areSamplesComplete)({
moofBoxes: (0, traversal_1.getMoofBoxes)(struc.boxes),
tfraBoxes: (0, traversal_1.getTfraBoxes)(struc.boxes),
}),
trexBoxes: (0, traversal_1.getTrexBoxes)(moov),
});
if (samplePositions.length === 0) {
return (0, exports.findAnyTrackWithSamplePositions)(allTracks, struc);
}
return { track: firstVideoTrack, samplePositions };
};
exports.findTrackToSeek = findTrackToSeek;

View File

@@ -0,0 +1,13 @@
import type { BufferIterator } from '../../iterator/buffer-iterator';
import type { BaseBox } from './base-type';
export interface FtypBox extends BaseBox {
type: 'ftyp-box';
majorBrand: string;
minorVersion: number;
compatibleBrands: string[];
}
export declare const parseFtyp: ({ iterator, size, offset, }: {
iterator: BufferIterator;
size: number;
offset: number;
}) => FtypBox;

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseFtyp = void 0;
const parseFtyp = ({ iterator, size, offset, }) => {
const majorBrand = iterator.getByteString(4, false);
const minorVersion = iterator.getFourByteNumber();
const types = (size - iterator.counter.getOffset()) / 4;
const compatibleBrands = [];
for (let i = 0; i < types; i++) {
compatibleBrands.push(iterator.getByteString(4, false).trim());
}
const offsetAtEnd = iterator.counter.getOffset();
return {
type: 'ftyp-box',
majorBrand,
minorVersion,
compatibleBrands,
offset,
boxSize: offsetAtEnd - offset,
};
};
exports.parseFtyp = parseFtyp;

View File

@@ -0,0 +1,14 @@
import type { MediaParserCodecData } from '../../codec-data';
import type { MediaParserAudioCodec } from '../../get-tracks';
type AudioDecoderConfig = {
numberOfChannels: number;
sampleRate: number;
codecPrivate: MediaParserCodecData | null;
};
export declare const getActualDecoderParameters: ({ audioCodec, codecPrivate, numberOfChannels, sampleRate, }: {
audioCodec: MediaParserAudioCodec;
codecPrivate: MediaParserCodecData | null;
numberOfChannels: number;
sampleRate: number;
}) => AudioDecoderConfig;
export {};

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getActualDecoderParameters = void 0;
const aac_codecprivate_1 = require("../../aac-codecprivate");
// Example video: 'https://pub-646d808d9cb240cea53bedc76dd3cd0c.r2.dev/riverside.mp4';
// This video has `numberOfChannels = 2`, but the actual number of channels is `1` according to Codec Private.
// Therefore, prioritizing Codec Private over `numberOfChannels`.
const getActualDecoderParameters = ({ audioCodec, codecPrivate, numberOfChannels, sampleRate, }) => {
if (audioCodec !== 'aac') {
return {
numberOfChannels,
sampleRate,
codecPrivate,
};
}
if (codecPrivate === null) {
return { numberOfChannels, sampleRate, codecPrivate };
}
if (codecPrivate.type !== 'aac-config') {
throw new Error('Expected AAC codec private data');
}
const parsed = (0, aac_codecprivate_1.parseAacCodecPrivate)(codecPrivate.data);
const actual = (0, aac_codecprivate_1.createAacCodecPrivate)({
...parsed,
codecPrivate: codecPrivate.data,
});
return {
numberOfChannels: parsed.channelConfiguration,
sampleRate: parsed.sampleRate,
codecPrivate: { type: 'aac-config', data: actual },
};
};
exports.getActualDecoderParameters = getActualDecoderParameters;

View File

@@ -0,0 +1,11 @@
import type { BufferIterator } from '../../iterator/buffer-iterator';
import type { MediaParserLogLevel } from '../../log';
import type { IsoBaseMediaBox } from './base-media-box';
import type { OnlyIfMoovAtomExpected } from './process-box';
export declare const getIsoBaseMediaChildren: ({ size, iterator, logLevel, onlyIfMoovAtomExpected, contentLength, }: {
size: number;
iterator: BufferIterator;
logLevel: MediaParserLogLevel;
onlyIfMoovAtomExpected: OnlyIfMoovAtomExpected | null;
contentLength: number;
}) => Promise<IsoBaseMediaBox[]>;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIsoBaseMediaChildren = void 0;
const process_box_1 = require("./process-box");
const getIsoBaseMediaChildren = async ({ size, iterator, logLevel, onlyIfMoovAtomExpected, contentLength, }) => {
const boxes = [];
const initial = iterator.counter.getOffset();
while (iterator.counter.getOffset() < size + initial) {
const parsed = await (0, process_box_1.processBox)({
iterator,
logLevel,
onlyIfMoovAtomExpected,
onlyIfMdatAtomExpected: null,
contentLength,
});
if (parsed.type !== 'box') {
throw new Error('Expected box');
}
boxes.push(parsed.box);
}
if (iterator.counter.getOffset() > size + initial) {
throw new Error(`read too many bytes - size: ${size}, read: ${iterator.counter.getOffset() - initial}. initial offset: ${initial}`);
}
return boxes;
};
exports.getIsoBaseMediaChildren = getIsoBaseMediaChildren;

View File

@@ -0,0 +1,3 @@
import type { MediaParserKeyframe } from '../../options';
import type { ParserState } from '../../state/parser-state';
export declare const getKeyframesFromIsoBaseMedia: (state: ParserState) => MediaParserKeyframe[];

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getKeyframesFromIsoBaseMedia = void 0;
const get_tracks_1 = require("../../get-tracks");
const are_samples_complete_1 = require("./are-samples-complete");
const get_sample_positions_from_track_1 = require("./get-sample-positions-from-track");
const traversal_1 = require("./traversal");
const getKeyframesFromIsoBaseMedia = (state) => {
const tracks = (0, get_tracks_1.getTracksFromIsoBaseMedia)({
isoState: state.iso,
m3uPlaylistContext: state.m3uPlaylistContext,
structure: state.structure,
mayUsePrecomputed: true,
});
const videoTracks = tracks.filter((t) => t.type === 'video');
const structure = state.structure.getIsoStructure();
const moofBoxes = (0, traversal_1.getMoofBoxes)(structure.boxes);
const tfraBoxes = (0, traversal_1.getTfraBoxes)(structure.boxes);
const moov = (0, traversal_1.getMoovFromFromIsoStructure)(structure);
if (!moov) {
return [];
}
const allSamples = videoTracks.map((t) => {
const { originalTimescale: ts } = t;
const trakBox = (0, traversal_1.getTrakBoxByTrackId)(moov, t.trackId);
if (!trakBox) {
return [];
}
const { samplePositions, isComplete } = (0, get_sample_positions_from_track_1.getSamplePositionsFromTrack)({
trakBox,
moofBoxes,
moofComplete: (0, are_samples_complete_1.areSamplesComplete)({
moofBoxes,
tfraBoxes,
}),
trexBoxes: (0, traversal_1.getTrexBoxes)(moov),
});
if (!isComplete) {
return [];
}
const keyframes = samplePositions
.filter((k) => {
return k.isKeyframe;
})
.map((k) => {
return {
trackId: t.trackId,
presentationTimeInSeconds: k.timestamp / ts,
decodingTimeInSeconds: k.decodingTimestamp / ts,
positionInBytes: k.offset,
sizeInBytes: k.size,
};
});
return keyframes;
});
return allSamples.flat();
};
exports.getKeyframesFromIsoBaseMedia = getKeyframesFromIsoBaseMedia;

View File

@@ -0,0 +1,15 @@
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 { IsoBaseMediaBox } from './base-media-box';
export type MfraSeekingBoxOptions = {
contentLength: number;
controller: MediaParserController;
readerInterface: MediaParserReaderInterface;
src: ParseMediaSrc;
logLevel: MediaParserLogLevel;
prefetchCache: PrefetchCache;
};
export declare const getMfraSeekingBox: ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }: MfraSeekingBoxOptions) => Promise<IsoBaseMediaBox[] | null>;

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMfraSeekingBox = void 0;
const get_children_1 = require("./get-children");
const get_mfra_atom_1 = require("./mfra/get-mfra-atom");
const get_mfro_atom_1 = require("./mfra/get-mfro-atom");
const getMfraSeekingBox = async ({ contentLength, controller, readerInterface, src, logLevel, prefetchCache, }) => {
const parentSize = await (0, get_mfro_atom_1.getMfroAtom)({
contentLength,
controller,
readerInterface,
src,
logLevel,
prefetchCache,
});
if (!parentSize) {
return null;
}
const mfraAtom = await (0, get_mfra_atom_1.getMfraAtom)({
contentLength,
controller,
readerInterface,
src,
parentSize,
logLevel,
prefetchCache,
});
mfraAtom.discard(8);
return (0, get_children_1.getIsoBaseMediaChildren)({
iterator: mfraAtom,
logLevel,
size: parentSize - 8,
onlyIfMoovAtomExpected: null,
contentLength,
});
};
exports.getMfraSeekingBox = getMfraSeekingBox;

View File

@@ -0,0 +1,6 @@
import type { ParserState } from '../../state/parser-state';
import type { MoovBox } from './moov/moov';
export declare const getMoovAtom: ({ endOfMdat, state, }: {
state: ParserState;
endOfMdat: number;
}) => Promise<MoovBox>;

View File

@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMoovAtom = void 0;
const buffer_iterator_1 = require("../../iterator/buffer-iterator");
const log_1 = require("../../log");
const register_track_1 = require("../../register-track");
const can_skip_tracks_1 = require("../../state/can-skip-tracks");
const has_tracks_section_1 = require("../../state/has-tracks-section");
const structure_1 = require("../../state/structure");
const process_box_1 = require("./process-box");
const traversal_1 = require("./traversal");
const getMoovAtom = async ({ endOfMdat, state, }) => {
var _a;
const headerSegment = (_a = state.m3uPlaylistContext) === null || _a === void 0 ? void 0 : _a.mp4HeaderSegment;
if (headerSegment) {
const segment = (0, traversal_1.getMoovFromFromIsoStructure)(headerSegment);
if (!segment) {
throw new Error('No moov box found in header segment');
}
return segment;
}
const start = Date.now();
log_1.Log.verbose(state.logLevel, 'Starting second fetch to get moov atom');
const { reader } = await state.readerInterface.read({
src: state.src,
range: endOfMdat,
controller: state.controller,
logLevel: state.logLevel,
prefetchCache: state.prefetchCache,
});
const onAudioTrack = state.onAudioTrack
? async ({ track, container }) => {
await (0, register_track_1.registerAudioTrack)({
track,
container,
logLevel: state.logLevel,
onAudioTrack: state.onAudioTrack,
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
tracks: state.callbacks.tracks,
});
return null;
}
: null;
const onVideoTrack = state.onVideoTrack
? async ({ track, container }) => {
await (0, register_track_1.registerVideoTrack)({
track,
container,
logLevel: state.logLevel,
onVideoTrack: state.onVideoTrack,
registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
tracks: state.callbacks.tracks,
});
return null;
}
: null;
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: new Uint8Array([]),
maxBytes: state.contentLength - endOfMdat,
logLevel: 'error',
});
while (true) {
const result = await reader.reader.read();
if (result.value) {
iterator.addData(result.value);
}
if (result.done) {
break;
}
}
const boxes = [];
const canSkipTracksState = (0, can_skip_tracks_1.makeCanSkipTracksState)({
hasAudioTrackHandlers: false,
fields: { slowStructure: true },
hasVideoTrackHandlers: false,
structure: (0, structure_1.structureState)(),
});
const tracksState = (0, has_tracks_section_1.makeTracksSectionState)(canSkipTracksState, state.src);
while (true) {
const box = await (0, process_box_1.processBox)({
iterator,
logLevel: state.logLevel,
onlyIfMoovAtomExpected: {
tracks: tracksState,
isoState: null,
movieTimeScaleState: state.iso.movieTimeScale,
onAudioTrack,
onVideoTrack,
registerVideoSampleCallback: () => Promise.resolve(),
registerAudioSampleCallback: () => Promise.resolve(),
},
onlyIfMdatAtomExpected: null,
contentLength: state.contentLength - endOfMdat,
});
if (box.type === 'box') {
boxes.push(box.box);
}
if (iterator.counter.getOffset() + endOfMdat > state.contentLength) {
throw new Error('Read past end of file');
}
if (iterator.counter.getOffset() + endOfMdat === state.contentLength) {
break;
}
}
const moov = boxes.find((b) => b.type === 'moov-box');
if (!moov) {
throw new Error('No moov box found');
}
log_1.Log.verbose(state.logLevel, `Finished fetching moov atom in ${Date.now() - start}ms`);
return moov;
};
exports.getMoovAtom = getMoovAtom;

View File

@@ -0,0 +1,5 @@
import type { SamplePosition } from '../../get-sample-positions';
export declare const getSamplePositionBounds: (samplePositions: SamplePosition[], timescale: number) => {
min: number;
max: number;
};

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSamplePositionBounds = void 0;
const getSamplePositionBounds = (samplePositions, timescale) => {
var _a;
let min = Infinity;
let max = -Infinity;
for (const samplePosition of samplePositions) {
const timestampMin = Math.min(samplePosition.timestamp, samplePosition.decodingTimestamp);
const timestampMax = Math.max(samplePosition.timestamp, samplePosition.decodingTimestamp) +
((_a = samplePosition.duration) !== null && _a !== void 0 ? _a : 0);
if (timestampMin < min) {
min = timestampMin;
}
if (timestampMax > max) {
max = timestampMax;
}
}
return { min: min / timescale, max: max / timescale };
};
exports.getSamplePositionBounds = getSamplePositionBounds;

View File

@@ -0,0 +1,13 @@
import type { SamplePosition } from '../../get-sample-positions';
import type { MoofBox } from '../../state/iso-base-media/precomputed-moof';
import type { TrexBox } from './moov/trex';
import type { TrakBox } from './trak/trak';
export declare const getSamplePositionsFromTrack: ({ trakBox, moofBoxes, moofComplete, trexBoxes, }: {
trakBox: TrakBox;
moofBoxes: MoofBox[];
moofComplete: boolean;
trexBoxes: TrexBox[];
}) => {
samplePositions: SamplePosition[];
isComplete: boolean;
};

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSamplePositionsFromTrack = void 0;
const collect_sample_positions_from_moof_boxes_1 = require("./collect-sample-positions-from-moof-boxes");
const collect_sample_positions_from_trak_1 = require("./collect-sample-positions-from-trak");
const traversal_1 = require("./traversal");
const getSamplePositionsFromTrack = ({ trakBox, moofBoxes, moofComplete, trexBoxes, }) => {
const tkhdBox = (0, traversal_1.getTkhdBox)(trakBox);
if (!tkhdBox) {
throw new Error('Expected tkhd box in trak box');
}
if (moofBoxes.length > 0) {
const { samplePositions } = (0, collect_sample_positions_from_moof_boxes_1.collectSamplePositionsFromMoofBoxes)({
moofBoxes,
tkhdBox,
isComplete: moofComplete,
trexBoxes,
});
return {
samplePositions: samplePositions.map((s) => s.samples).flat(1),
isComplete: moofComplete,
};
}
return {
samplePositions: (0, collect_sample_positions_from_trak_1.collectSamplePositionsFromTrak)(trakBox),
isComplete: true,
};
};
exports.getSamplePositionsFromTrack = getSamplePositionsFromTrack;

View File

@@ -0,0 +1,18 @@
import type { MediaParserTrack } from '../../get-tracks';
import type { MediaParserLogLevel } from '../../log';
import type { IsoBaseMediaStructure } from '../../parse-result';
import type { IsoBaseMediaSeekingHints } from '../../seeking-hints';
import type { IsoBaseMediaState } from '../../state/iso-base-media/iso-state';
import type { StructureState } from '../../state/structure';
import type { SeekResolution } from '../../work-on-seek-request';
export declare const getSeekingByteFromFragmentedMp4: ({ info, time, logLevel, currentPosition, isoState, tracks, isLastChunkInPlaylist, structure, mp4HeaderSegment, }: {
info: IsoBaseMediaSeekingHints;
time: number;
logLevel: MediaParserLogLevel;
currentPosition: number;
isoState: IsoBaseMediaState;
structure: StructureState;
tracks: MediaParserTrack[];
isLastChunkInPlaylist: boolean;
mp4HeaderSegment: IsoBaseMediaStructure | null;
}) => Promise<SeekResolution>;

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSeekingByteFromFragmentedMp4 = void 0;
const log_1 = require("../../log");
const video_section_1 = require("../../state/video-section");
const are_samples_complete_1 = require("./are-samples-complete");
const collect_sample_positions_from_moof_boxes_1 = require("./collect-sample-positions-from-moof-boxes");
const find_keyframe_before_time_1 = require("./find-keyframe-before-time");
const get_sample_position_bounds_1 = require("./get-sample-position-bounds");
const find_best_segment_from_tfra_1 = require("./mfra/find-best-segment-from-tfra");
const traversal_1 = require("./traversal");
const getSeekingByteFromFragmentedMp4 = async ({ info, time, logLevel, currentPosition, isoState, tracks, isLastChunkInPlaylist, structure, mp4HeaderSegment, }) => {
const firstVideoTrack = tracks.find((t) => t.type === 'video');
// If there is both video and audio, seek based on video, but if not then audio is also okay
const firstTrack = firstVideoTrack !== null && firstVideoTrack !== void 0 ? firstVideoTrack : tracks.find((t) => t.type === 'audio');
if (!firstTrack) {
throw new Error('no video and no audio tracks');
}
const moov = (0, traversal_1.getMoovBoxFromState)({
structureState: structure,
isoState,
mp4HeaderSegment,
mayUsePrecomputed: true,
});
if (!moov) {
throw new Error('No moov atom found');
}
const trakBox = (0, traversal_1.getTrakBoxByTrackId)(moov, firstTrack.trackId);
if (!trakBox) {
throw new Error('No trak box found');
}
const tkhdBox = (0, traversal_1.getTkhdBox)(trakBox);
if (!tkhdBox) {
throw new Error('Expected tkhd box in trak box');
}
const isComplete = (0, are_samples_complete_1.areSamplesComplete)({
moofBoxes: info.moofBoxes,
tfraBoxes: info.tfraBoxes,
});
const { samplePositions: samplePositionsArray } = (0, collect_sample_positions_from_moof_boxes_1.collectSamplePositionsFromMoofBoxes)({
moofBoxes: info.moofBoxes,
tkhdBox,
isComplete,
trexBoxes: (0, traversal_1.getTrexBoxes)(moov),
});
log_1.Log.trace(logLevel, 'Fragmented MP4 - Checking if we have seeking info for this time range');
for (const positions of samplePositionsArray) {
const { min, max } = (0, get_sample_position_bounds_1.getSamplePositionBounds)(positions.samples, firstTrack.originalTimescale);
if (min <= time &&
(positions.isLastFragment || isLastChunkInPlaylist || time <= max)) {
log_1.Log.trace(logLevel, `Fragmented MP4 - Found that we have seeking info for this time range: ${min} <= ${time} <= ${max}`);
const kf = (0, find_keyframe_before_time_1.findKeyframeBeforeTime)({
samplePositions: positions.samples,
time,
timescale: firstTrack.originalTimescale,
logLevel,
mediaSections: info.mediaSections,
startInSeconds: firstTrack.startInSeconds,
});
if (kf) {
return {
type: 'do-seek',
byte: kf.offset,
timeInSeconds: Math.min(kf.decodingTimestamp, kf.timestamp) /
firstTrack.originalTimescale,
};
}
}
}
const atom = await (info.mfraAlreadyLoaded
? Promise.resolve(info.mfraAlreadyLoaded)
: isoState.mfra.triggerLoad());
if (atom) {
const moofOffset = (0, find_best_segment_from_tfra_1.findBestSegmentFromTfra)({
mfra: atom,
time,
firstTrack,
timescale: firstTrack.originalTimescale,
});
if (moofOffset !== null &&
!(moofOffset.start <= currentPosition && currentPosition < moofOffset.end)) {
log_1.Log.verbose(logLevel, `Fragmented MP4 - Found based on mfra information that we should seek to: ${moofOffset.start} ${moofOffset.end}`);
return {
type: 'intermediary-seek',
byte: moofOffset.start,
};
}
}
log_1.Log.trace(logLevel, 'Fragmented MP4 - No seeking info found for this time range.');
if ((0, video_section_1.isByteInMediaSection)({
position: currentPosition,
mediaSections: info.mediaSections,
}) !== 'in-section') {
return {
type: 'valid-but-must-wait',
};
}
log_1.Log.trace(logLevel, 'Fragmented MP4 - Inside the wrong video section, skipping to the end of the section');
const mediaSection = (0, video_section_1.getCurrentMediaSection)({
offset: currentPosition,
mediaSections: info.mediaSections,
});
if (!mediaSection) {
throw new Error('No video section defined');
}
return {
type: 'intermediary-seek',
byte: mediaSection.start + mediaSection.size,
};
};
exports.getSeekingByteFromFragmentedMp4 = getSeekingByteFromFragmentedMp4;

View File

@@ -0,0 +1,15 @@
import type { MediaParserLogLevel } from '../../log';
import type { M3uPlaylistContext } from '../../options';
import type { IsoBaseMediaSeekingHints } from '../../seeking-hints';
import type { IsoBaseMediaState } from '../../state/iso-base-media/iso-state';
import type { StructureState } from '../../state/structure';
import type { SeekResolution } from '../../work-on-seek-request';
export declare const getSeekingByteFromIsoBaseMedia: ({ info, time, logLevel, currentPosition, isoState, m3uPlaylistContext, structure, }: {
info: IsoBaseMediaSeekingHints;
time: number;
logLevel: MediaParserLogLevel;
currentPosition: number;
isoState: IsoBaseMediaState;
m3uPlaylistContext: M3uPlaylistContext | null;
structure: StructureState;
}) => Promise<SeekResolution>;

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSeekingByteFromIsoBaseMedia = void 0;
const get_tracks_1 = require("../../get-tracks");
const log_1 = require("../../log");
const find_keyframe_before_time_1 = require("./find-keyframe-before-time");
const find_track_to_seek_1 = require("./find-track-to-seek");
const get_seeking_byte_from_fragmented_mp4_1 = require("./get-seeking-byte-from-fragmented-mp4");
const traversal_1 = require("./traversal");
const getSeekingByteFromIsoBaseMedia = ({ info, time, logLevel, currentPosition, isoState, m3uPlaylistContext, structure, }) => {
var _a, _b, _c;
const tracks = (0, get_tracks_1.getTracksFromIsoBaseMedia)({
isoState,
m3uPlaylistContext,
structure,
mayUsePrecomputed: false,
});
const hasMoov = Boolean((0, traversal_1.getMoovBoxFromState)({
structureState: structure,
isoState,
mayUsePrecomputed: false,
mp4HeaderSegment: (_a = m3uPlaylistContext === null || m3uPlaylistContext === void 0 ? void 0 : m3uPlaylistContext.mp4HeaderSegment) !== null && _a !== void 0 ? _a : null,
}));
if (!hasMoov) {
log_1.Log.trace(logLevel, 'No moov box found, must wait');
return Promise.resolve({
type: 'valid-but-must-wait',
});
}
if (info.moofBoxes.length > 0) {
return (0, get_seeking_byte_from_fragmented_mp4_1.getSeekingByteFromFragmentedMp4)({
info,
time,
logLevel,
currentPosition,
isoState,
tracks,
isLastChunkInPlaylist: (_b = m3uPlaylistContext === null || m3uPlaylistContext === void 0 ? void 0 : m3uPlaylistContext.isLastChunkInPlaylist) !== null && _b !== void 0 ? _b : false,
structure,
mp4HeaderSegment: (_c = m3uPlaylistContext === null || m3uPlaylistContext === void 0 ? void 0 : m3uPlaylistContext.mp4HeaderSegment) !== null && _c !== void 0 ? _c : null,
});
}
const trackWithSamplePositions = (0, find_track_to_seek_1.findTrackToSeek)(tracks, structure);
if (!trackWithSamplePositions) {
return Promise.resolve({
type: 'valid-but-must-wait',
});
}
const { track, samplePositions } = trackWithSamplePositions;
const keyframe = (0, find_keyframe_before_time_1.findKeyframeBeforeTime)({
samplePositions,
time,
timescale: track.originalTimescale,
logLevel,
mediaSections: info.mediaSections,
startInSeconds: track.startInSeconds,
});
if (keyframe) {
return Promise.resolve({
type: 'do-seek',
byte: keyframe.offset,
timeInSeconds: Math.min(keyframe.decodingTimestamp, keyframe.timestamp) /
track.originalTimescale,
});
}
return Promise.resolve({
type: 'invalid',
});
};
exports.getSeekingByteFromIsoBaseMedia = getSeekingByteFromIsoBaseMedia;

View File

@@ -0,0 +1,2 @@
import type { TrakBox } from './trak/trak';
export declare const getVideoCodecFromIsoTrak: (trakBox: TrakBox) => "h264" | "vp9" | "av1" | "h265" | "prores";

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getVideoCodecFromIsoTrak = void 0;
const traversal_1 = require("./traversal");
const getVideoCodecFromIsoTrak = (trakBox) => {
const stsdBox = (0, traversal_1.getStsdBox)(trakBox);
if (stsdBox && stsdBox.type === 'stsd-box') {
const videoSample = stsdBox.samples.find((s) => s.type === 'video');
if (videoSample && videoSample.type === 'video') {
if (videoSample.format === 'hvc1' || videoSample.format === 'hev1') {
return 'h265';
}
if (videoSample.format === 'avc1') {
return 'h264';
}
if (videoSample.format === 'av01') {
return 'av1';
}
if (videoSample.format === 'vp09') {
return 'vp9';
}
// ap4h: ProRes 4444
if (videoSample.format === 'ap4h') {
return 'prores';
}
// ap4x: ap4x: ProRes 4444 XQ
if (videoSample.format === 'ap4x') {
return 'prores';
}
// apch: ProRes 422 High Quality
if (videoSample.format === 'apch') {
return 'prores';
}
// apcn: ProRes 422 Standard Definition
if (videoSample.format === 'apcn') {
return 'prores';
}
// apcs: ProRes 422 LT
if (videoSample.format === 'apcs') {
return 'prores';
}
// apco: ProRes 422 Proxy
if (videoSample.format === 'apco') {
return 'prores';
}
// aprh: ProRes RAW High Quality
if (videoSample.format === 'aprh') {
return 'prores';
}
// aprn: ProRes RAW Standard Definition
if (videoSample.format === 'aprn') {
return 'prores';
}
}
}
throw new Error('Could not find video codec');
};
exports.getVideoCodecFromIsoTrak = getVideoCodecFromIsoTrak;

View File

@@ -0,0 +1,3 @@
import type { MediaParserAudioTrack, MediaParserOtherTrack, MediaParserVideoTrack } from '../../get-tracks';
import type { TrakBox } from './trak/trak';
export declare const makeBaseMediaTrack: (trakBox: TrakBox, startTimeInSeconds: number) => MediaParserVideoTrack | MediaParserAudioTrack | MediaParserOtherTrack | null;

View File

@@ -0,0 +1,128 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeBaseMediaTrack = void 0;
const get_audio_codec_1 = require("../../get-audio-codec");
const get_fps_1 = require("../../get-fps");
const get_sample_aspect_ratio_1 = require("../../get-sample-aspect-ratio");
const get_video_codec_1 = require("../../get-video-codec");
const normalize_video_rotation_1 = require("../../normalize-video-rotation");
const webcodecs_timescale_1 = require("../../webcodecs-timescale");
const color_to_webcodecs_colors_1 = require("./color-to-webcodecs-colors");
const get_actual_number_of_channels_1 = require("./get-actual-number-of-channels");
const get_video_codec_from_iso_track_1 = require("./get-video-codec-from-iso-track");
const get_editlist_1 = require("./mdat/get-editlist");
const traversal_1 = require("./traversal");
const makeBaseMediaTrack = (trakBox, startTimeInSeconds) => {
var _a, _b, _c, _d, _e;
const tkhdBox = (0, traversal_1.getTkhdBox)(trakBox);
const videoDescriptors = (0, traversal_1.getVideoDescriptors)(trakBox);
const timescaleAndDuration = (0, get_fps_1.getTimescaleAndDuration)(trakBox);
if (!tkhdBox) {
throw new Error('Expected tkhd box in trak box');
}
if (!timescaleAndDuration) {
throw new Error('Expected timescale and duration in trak box');
}
if ((0, get_fps_1.trakBoxContainsAudio)(trakBox)) {
const numberOfChannels = (0, get_audio_codec_1.getNumberOfChannelsFromTrak)(trakBox);
if (numberOfChannels === null) {
throw new Error('Could not find number of channels');
}
const sampleRate = (0, get_audio_codec_1.getSampleRate)(trakBox);
if (sampleRate === null) {
throw new Error('Could not find sample rate');
}
const { codecString, description } = (0, get_audio_codec_1.getAudioCodecStringFromTrak)(trakBox);
const codecPrivate = (_b = (_a = (0, get_audio_codec_1.getCodecPrivateFromTrak)(trakBox)) !== null && _a !== void 0 ? _a : description) !== null && _b !== void 0 ? _b : null;
const codecEnum = (0, get_audio_codec_1.getAudioCodecFromTrack)(trakBox);
const actual = (0, get_actual_number_of_channels_1.getActualDecoderParameters)({
audioCodec: codecEnum,
codecPrivate: codecPrivate !== null && codecPrivate !== void 0 ? codecPrivate : null,
numberOfChannels,
sampleRate,
});
return {
type: 'audio',
trackId: tkhdBox.trackId,
originalTimescale: timescaleAndDuration.timescale,
codec: codecString,
numberOfChannels: actual.numberOfChannels,
sampleRate: actual.sampleRate,
description: (_d = (_c = actual.codecPrivate) === null || _c === void 0 ? void 0 : _c.data) !== null && _d !== void 0 ? _d : undefined,
codecData: actual.codecPrivate,
codecEnum,
startInSeconds: startTimeInSeconds,
timescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
trackMediaTimeOffsetInTrackTimescale: (0, get_editlist_1.findTrackMediaTimeOffsetInTrackTimescale)({
trakBox,
}),
};
}
if (!(0, get_fps_1.trakBoxContainsVideo)(trakBox)) {
return {
type: 'other',
trackId: tkhdBox.trackId,
originalTimescale: timescaleAndDuration.timescale,
trakBox,
startInSeconds: startTimeInSeconds,
timescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
trackMediaTimeOffsetInTrackTimescale: (0, get_editlist_1.findTrackMediaTimeOffsetInTrackTimescale)({
trakBox,
}),
};
}
const videoSample = (0, get_sample_aspect_ratio_1.getStsdVideoConfig)(trakBox);
if (!videoSample) {
throw new Error('No video sample');
}
const sampleAspectRatio = (0, get_sample_aspect_ratio_1.getSampleAspectRatio)(trakBox);
const aspectRatioApplied = (0, get_sample_aspect_ratio_1.applyAspectRatios)({
dimensions: videoSample,
sampleAspectRatio,
displayAspectRatio: (0, get_sample_aspect_ratio_1.getDisplayAspectRatio)({
sampleAspectRatio,
nativeDimensions: videoSample,
}),
});
const { displayAspectHeight, displayAspectWidth, height, rotation, width } = (0, get_sample_aspect_ratio_1.applyTkhdBox)(aspectRatioApplied, tkhdBox);
const codec = (0, get_video_codec_1.getVideoCodecString)(trakBox);
if (!codec) {
throw new Error('Could not find video codec');
}
const privateData = (0, get_video_codec_1.getVideoPrivateData)(trakBox);
const advancedColor = (_e = (0, get_video_codec_1.getIsoBmColrConfig)(trakBox)) !== null && _e !== void 0 ? _e : {
fullRange: null,
matrix: null,
primaries: null,
transfer: null,
};
const track = {
m3uStreamFormat: null,
type: 'video',
trackId: tkhdBox.trackId,
description: videoDescriptors !== null && videoDescriptors !== void 0 ? videoDescriptors : undefined,
originalTimescale: timescaleAndDuration.timescale,
codec,
sampleAspectRatio: (0, get_sample_aspect_ratio_1.getSampleAspectRatio)(trakBox),
width,
height,
codedWidth: videoSample.width,
codedHeight: videoSample.height,
// Repeating those keys because they get picked up by VideoDecoder
displayAspectWidth,
displayAspectHeight,
rotation: (0, normalize_video_rotation_1.normalizeVideoRotation)(0 - rotation),
codecData: privateData,
colorSpace: (0, color_to_webcodecs_colors_1.mediaParserAdvancedColorToWebCodecsColor)(advancedColor),
advancedColor,
codecEnum: (0, get_video_codec_from_iso_track_1.getVideoCodecFromIsoTrak)(trakBox),
fps: (0, get_fps_1.getFpsFromMp4TrakBox)(trakBox),
startInSeconds: startTimeInSeconds,
timescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
trackMediaTimeOffsetInTrackTimescale: (0, get_editlist_1.findTrackMediaTimeOffsetInTrackTimescale)({
trakBox,
}),
};
return track;
};
exports.makeBaseMediaTrack = makeBaseMediaTrack;

View File

@@ -0,0 +1,8 @@
import type { TrakBox } from '../trak/trak';
export declare const findTrackStartTimeInSeconds: ({ movieTimeScale, trakBox, }: {
movieTimeScale: number;
trakBox: TrakBox;
}) => number;
export declare const findTrackMediaTimeOffsetInTrackTimescale: ({ trakBox, }: {
trakBox: TrakBox;
}) => number;

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findTrackMediaTimeOffsetInTrackTimescale = exports.findTrackStartTimeInSeconds = void 0;
const traversal_1 = require("../traversal");
const findTrackStartTimeInSeconds = ({ movieTimeScale, trakBox, }) => {
const elstBox = (0, traversal_1.getElstBox)(trakBox);
if (!elstBox) {
return 0;
}
const { entries } = elstBox;
let dwellTime = 0;
for (const entry of entries) {
const { editDuration, mediaTime } = entry;
if (mediaTime !== -1) {
continue;
}
dwellTime += editDuration;
}
return dwellTime / movieTimeScale;
};
exports.findTrackStartTimeInSeconds = findTrackStartTimeInSeconds;
const findTrackMediaTimeOffsetInTrackTimescale = ({ trakBox, }) => {
const elstBox = (0, traversal_1.getElstBox)(trakBox);
if (!elstBox) {
return 0;
}
const { entries } = elstBox;
let dwellTime = 0;
for (const entry of entries) {
const { mediaTime } = entry;
if (mediaTime === -1) {
continue;
}
dwellTime += mediaTime;
}
return dwellTime;
};
exports.findTrackMediaTimeOffsetInTrackTimescale = findTrackMediaTimeOffsetInTrackTimescale;

View File

@@ -0,0 +1,3 @@
import type { FetchMoreData, Skip } from '../../../skip';
import type { ParserState } from '../../../state/parser-state';
export declare const parseMdatSection: (state: ParserState) => Promise<Skip | FetchMoreData | null>;

View File

@@ -0,0 +1,175 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMdatSection = void 0;
const convert_audio_or_video_sample_1 = require("../../../convert-audio-or-video-sample");
const get_tracks_1 = require("../../../get-tracks");
const log_1 = require("../../../log");
const skip_1 = require("../../../skip");
const cached_sample_positions_1 = require("../../../state/iso-base-media/cached-sample-positions");
const last_moof_box_1 = require("../../../state/iso-base-media/last-moof-box");
const may_skip_video_data_1 = require("../../../state/may-skip-video-data");
const video_section_1 = require("../../../state/video-section");
const webcodecs_timescale_1 = require("../../../webcodecs-timescale");
const get_moov_atom_1 = require("../get-moov-atom");
const postprocess_bytes_1 = require("./postprocess-bytes");
const parseMdatSection = async (state) => {
const mediaSection = (0, video_section_1.getCurrentMediaSection)({
offset: state.iterator.counter.getOffset(),
mediaSections: state.mediaSection.getMediaSections(),
});
if (!mediaSection) {
throw new Error('No video section defined');
}
const endOfMdat = mediaSection.size + mediaSection.start;
// don't need mdat at all, can skip
if ((0, may_skip_video_data_1.maySkipVideoData)({ state })) {
const mfra = state.iso.mfra.getIfAlreadyLoaded();
if (mfra) {
const lastMoof = (0, last_moof_box_1.getLastMoofBox)(mfra);
if (lastMoof && lastMoof > endOfMdat) {
log_1.Log.verbose(state.logLevel, 'Skipping to last moof', lastMoof);
return (0, skip_1.makeSkip)(lastMoof);
}
}
return (0, skip_1.makeSkip)(endOfMdat);
}
// if we only need the first and last sample, we may skip over the samples in the middle
// this logic skips the samples in the middle for a fragmented mp4
if ((0, may_skip_video_data_1.maySkipOverSamplesInTheMiddle)({ state })) {
const mfra = state.iso.mfra.getIfAlreadyLoaded();
if (mfra) {
const lastMoof = (0, last_moof_box_1.getLastMoofBox)(mfra);
// we require that all moof boxes of both tracks have been processed, for correct duration calculation
const firstMax = (0, last_moof_box_1.getMaxFirstMoofOffset)(mfra);
const mediaSectionsBiggerThanMoof = state.mediaSection
.getMediaSections()
.filter((m) => m.start > firstMax).length;
if (mediaSectionsBiggerThanMoof > 1 && lastMoof && lastMoof > endOfMdat) {
log_1.Log.verbose(state.logLevel, 'Skipping to last moof because only first and last samples are needed');
return (0, skip_1.makeSkip)(lastMoof);
}
}
}
const alreadyHasMoov = (0, get_tracks_1.getHasTracks)(state, true);
if (!alreadyHasMoov) {
const moov = await (0, get_moov_atom_1.getMoovAtom)({
endOfMdat,
state,
});
const tracksFromMoov = (0, get_tracks_1.getTracksFromMoovBox)(moov);
state.iso.moov.setMoovBox({
moovBox: moov,
precomputed: false,
});
const existingTracks = state.callbacks.tracks.getTracks();
for (const trackFromMoov of tracksFromMoov) {
if (existingTracks.find((t) => t.trackId === trackFromMoov.trackId)) {
continue;
}
if (trackFromMoov.type === 'other') {
continue;
}
state.callbacks.tracks.addTrack(trackFromMoov);
}
state.callbacks.tracks.setIsDone(state.logLevel);
state.structure.getIsoStructure().boxes.push(moov);
return (0, exports.parseMdatSection)(state);
}
const tracks = state.callbacks.tracks.getTracks();
if (!state.iso.flatSamples.getSamples(mediaSection.start)) {
const samplePosition = (0, cached_sample_positions_1.calculateSamplePositions)({
state,
mediaSectionStart: mediaSection.start,
trackIds: tracks.map((t) => t.trackId),
});
state.iso.flatSamples.setSamples(mediaSection.start, samplePosition);
}
const samplePositions = state.iso.flatSamples.getSamples(mediaSection.start);
const sampleIndices = state.iso.flatSamples.getCurrentSampleIndices(mediaSection.start);
const nextSampleArray = (0, cached_sample_positions_1.getSampleWithLowestDts)(samplePositions, sampleIndices);
if (nextSampleArray.length === 0) {
log_1.Log.verbose(state.logLevel, 'Iterated over all samples.', endOfMdat);
return (0, skip_1.makeSkip)(endOfMdat);
}
const exactMatch = nextSampleArray.find((s) => s.samplePosition.offset === state.iterator.counter.getOffset());
const nextSample = exactMatch !== null && exactMatch !== void 0 ? exactMatch : nextSampleArray.sort((a, b) => a.samplePosition.offset - b.samplePosition.offset)[0];
if (nextSample.samplePosition.offset !== state.iterator.counter.getOffset()) {
return (0, skip_1.makeSkip)(nextSample.samplePosition.offset);
}
// Corrupt file: Sample is beyond the end of the file. Don't process it.
if (nextSample.samplePosition.offset + nextSample.samplePosition.size >
state.contentLength) {
log_1.Log.verbose(state.logLevel, "Sample is beyond the end of the file. Don't process it.", nextSample.samplePosition.offset + nextSample.samplePosition.size, endOfMdat);
return (0, skip_1.makeSkip)(endOfMdat);
}
const { iterator } = state;
// Need to fetch more data
if (iterator.bytesRemaining() < nextSample.samplePosition.size) {
return (0, skip_1.makeFetchMoreData)(nextSample.samplePosition.size - iterator.bytesRemaining());
}
const { timestamp: rawCts, decodingTimestamp: rawDts, duration, isKeyframe, offset, bigEndian, chunkSize, } = nextSample.samplePosition;
const track = tracks.find((t) => t.trackId === nextSample.trackId);
const { originalTimescale, startInSeconds, trackMediaTimeOffsetInTrackTimescale, timescale: trackTimescale, } = track;
const cts = rawCts +
startInSeconds * originalTimescale -
(trackMediaTimeOffsetInTrackTimescale / trackTimescale) *
webcodecs_timescale_1.WEBCODECS_TIMESCALE;
const dts = rawDts +
startInSeconds * originalTimescale -
(trackMediaTimeOffsetInTrackTimescale / trackTimescale) *
webcodecs_timescale_1.WEBCODECS_TIMESCALE;
const bytes = (0, postprocess_bytes_1.postprocessBytes)({
bytes: iterator.getSlice(nextSample.samplePosition.size),
bigEndian,
chunkSize,
});
if (track.type === 'audio') {
const audioSample = (0, convert_audio_or_video_sample_1.convertAudioOrVideoSampleToWebCodecsTimestamps)({
sample: {
data: bytes,
timestamp: cts,
duration,
decodingTimestamp: dts,
type: isKeyframe ? 'key' : 'delta',
offset,
},
timescale: originalTimescale,
});
await state.callbacks.onAudioSample({
audioSample,
trackId: track.trackId,
});
}
if (track.type === 'video') {
// https://pub-646d808d9cb240cea53bedc76dd3cd0c.r2.dev/sei_checkpoint.mp4
// Position in file 0x0001aba615
// https://github.com/remotion-dev/remotion/issues/4680
// In Chrome, we may not treat recovery points as keyframes
// otherwise "a keyframe is required after flushing"
const nalUnitType = bytes[4] & 0b00011111;
let isRecoveryPoint = false;
// SEI (Supplemental enhancement information)
if (nalUnitType === 6) {
const seiType = bytes[5];
isRecoveryPoint = seiType === 6;
}
const videoSample = (0, convert_audio_or_video_sample_1.convertAudioOrVideoSampleToWebCodecsTimestamps)({
sample: {
data: bytes,
timestamp: cts,
duration,
decodingTimestamp: dts,
type: isKeyframe && !isRecoveryPoint ? 'key' : 'delta',
offset,
},
timescale: originalTimescale,
});
await state.callbacks.onVideoSample({
videoSample,
trackId: track.trackId,
});
}
state.iso.flatSamples.setCurrentSampleIndex(mediaSection.start, nextSample.trackId, nextSample.index + 1);
return null;
};
exports.parseMdatSection = parseMdatSection;

View File

@@ -0,0 +1,5 @@
export declare const postprocessBytes: ({ bytes, bigEndian, chunkSize, }: {
bytes: Uint8Array;
bigEndian: boolean;
chunkSize: number | null;
}) => Uint8Array<ArrayBufferLike>;

View File

@@ -0,0 +1,25 @@
"use strict";
// Addressing the issue of audio that is stored in big-endian format!
// If a "twos" atom is present, that is the case.
// The samples are stored internally in small chunks, like 4 bytes
// but WebCodecs does not accept such small chunks.
Object.defineProperty(exports, "__esModule", { value: true });
exports.postprocessBytes = void 0;
// When entering this function, they are already concatenated, but in litte endian order.
// This function reverses the bytes in each chunk, so that they are in big-endian order.
const postprocessBytes = ({ bytes, bigEndian, chunkSize, }) => {
if (!bigEndian) {
return bytes;
}
if (chunkSize === null) {
return bytes;
}
const newBuffer = new Uint8Array(bytes);
for (let i = 0; i < newBuffer.length; i += chunkSize) {
const slice = newBuffer.slice(i, i + chunkSize);
slice.reverse();
newBuffer.set(slice, i);
}
return newBuffer;
};
exports.postprocessBytes = postprocessBytes;

View File

@@ -0,0 +1,16 @@
import type { BufferIterator } from '../../iterator/buffer-iterator';
export interface MdhdBox {
type: 'mdhd-box';
version: number;
timescale: number;
duration: number;
language: number;
quality: number;
creationTime: number | null;
modificationTime: number | null;
}
export declare const parseMdhd: ({ data, size, fileOffset, }: {
data: BufferIterator;
size: number;
fileOffset: number;
}) => MdhdBox;

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMdhd = void 0;
const parseMdhd = ({ data, size, fileOffset, }) => {
const version = data.getUint8();
// flags, we discard them
data.discard(3);
// creation time
const creationTime = version === 1 ? Number(data.getUint64()) : data.getUint32();
// modification time
const modificationTime = version === 1 ? Number(data.getUint64()) : data.getUint32();
const timescale = data.getUint32();
const duration = version === 1 ? data.getUint64() : data.getUint32();
const language = data.getUint16();
// quality
const quality = data.getUint16();
const remaining = size - (data.counter.getOffset() - fileOffset);
if (remaining !== 0) {
throw new Error(`Expected remaining bytes to be 0, got ${remaining}`);
}
return {
type: 'mdhd-box',
duration: Number(duration),
timescale,
version,
language,
quality,
creationTime,
modificationTime,
};
};
exports.parseMdhd = parseMdhd;

View File

@@ -0,0 +1,12 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
export interface HdlrBox extends BaseBox {
type: 'hdlr-box';
hdlrType: string;
componentName: string;
}
export declare const parseHdlr: ({ iterator, size, offset, }: {
iterator: BufferIterator;
size: number;
offset: number;
}) => Promise<HdlrBox>;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseHdlr = void 0;
const parseHdlr = ({ iterator, size, offset, }) => {
const box = iterator.startBox(size - 8);
const version = iterator.getUint8();
if (version !== 0) {
throw new Error(`Unsupported hdlr version: ${version}`);
}
// version
iterator.discard(3);
// predefined
iterator.discard(4);
// type
const hdlrType = iterator.getByteString(4, false);
// component manufactor
iterator.discard(4);
// component flags
iterator.discard(4);
// component flags mask
iterator.discard(4);
// component name
const componentName = iterator.readUntilNullTerminator();
box.discardRest();
return Promise.resolve({
type: 'hdlr-box',
boxSize: size,
offset,
hdlrType,
componentName,
});
};
exports.parseHdlr = parseHdlr;

View File

@@ -0,0 +1,28 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
type IlstEntry = {
index: string;
wellKnownType: number;
type: string;
value: Value;
};
export type Value = {
type: 'text';
value: string;
} | {
type: 'number';
value: number;
} | {
type: 'unknown';
value: unknown;
};
export interface IlstBox extends BaseBox {
type: 'ilst-box';
entries: IlstEntry[];
}
export declare const parseIlstBox: ({ iterator, size, offset, }: {
iterator: BufferIterator;
size: number;
offset: number;
}) => IlstBox;
export {};

View File

@@ -0,0 +1,98 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseIlstBox = void 0;
// https://developer.apple.com/documentation/quicktime-file-format/well-known_types
const parseFromWellKnownType = (wellKnownType, iterator, size) => {
if (wellKnownType === 1) {
const value = iterator.getByteString(size, false);
return { type: 'text', value };
}
if (wellKnownType === 21) {
if (size === 1) {
return { type: 'number', value: iterator.getInt8() };
}
if (size === 2) {
return { type: 'number', value: iterator.getInt16() };
}
if (size === 3) {
return { type: 'number', value: iterator.getInt24() };
}
if (size === 4) {
return { type: 'number', value: iterator.getInt32() };
}
if (size === 8) {
return { type: 'number', value: Number(iterator.getInt64()) };
}
throw new Error(`Weird size for number ${size}`);
}
if (wellKnownType === 22) {
if (size === 1) {
return { type: 'number', value: iterator.getUint8() };
}
if (size === 2) {
return { type: 'number', value: iterator.getUint16() };
}
if (size === 3) {
return { type: 'number', value: iterator.getUint24() };
}
if (size === 4) {
return { type: 'number', value: iterator.getUint32() };
}
throw new Error(`Weird size for number ${size}`);
}
if (wellKnownType === 23) {
if (size === 4) {
return { type: 'number', value: iterator.getFloat32() };
}
if (size === 8) {
return { type: 'number', value: iterator.getFloat64() };
}
throw new Error(`Weird size for number ${size}`);
}
iterator.discard(size);
return { type: 'unknown', value: null };
};
const parseIlstBox = ({ iterator, size, offset, }) => {
const box = iterator.startBox(size - 8);
const entries = [];
while (iterator.counter.getOffset() < size + offset) {
// metadata size
const metadataSize = iterator.getUint32();
const index = iterator.getAtom();
// this can be of a different type
if (!index.startsWith('<27>') && !index.startsWith('\u0000')) {
// "skip" as a number
if (index === 'skip') {
iterator.discard(metadataSize - 8);
continue;
}
// "----" atom in m4a files
// iTunes adds it for .m4a files
// not very interesting data, so we don't parse them
if (index === '----') {
iterator.discard(metadataSize - 8);
continue;
}
iterator.discard(metadataSize - 8);
continue;
}
const innerSize = iterator.getUint32();
const type = iterator.getAtom();
const typeIndicator = iterator.getUint8();
if (typeIndicator !== 0) {
throw new Error('Expected type indicator to be 0');
}
const wellKnownType = iterator.getUint24();
iterator.discard(4);
const value = parseFromWellKnownType(wellKnownType, iterator, innerSize - 16);
entries.push({ index, type, wellKnownType, value });
}
box.discardRest();
return {
type: 'ilst-box',
boxSize: size,
offset,
entries,
};
};
exports.parseIlstBox = parseIlstBox;

View File

@@ -0,0 +1,11 @@
import type { MediaParserAudioTrack, MediaParserVideoTrack } from '../../../get-tracks';
import type { IsoBaseMediaBox } from '../base-media-box';
export declare const findBestSegmentFromTfra: ({ mfra, time, firstTrack, timescale, }: {
mfra: IsoBaseMediaBox[];
time: number;
firstTrack: MediaParserVideoTrack | MediaParserAudioTrack;
timescale: number;
}) => {
start: number;
end: number;
} | null;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findBestSegmentFromTfra = void 0;
const findBestSegmentFromTfra = ({ mfra, time, firstTrack, timescale, }) => {
const tfra = mfra.find((b) => b.type === 'tfra-box' && b.trackId === firstTrack.trackId);
if (!tfra) {
return null;
}
let bestSegment = null;
for (const segment of tfra.entries) {
if (segment.time / timescale <= time) {
bestSegment = segment;
}
}
if (!bestSegment) {
return null;
}
const currentSegmentIndex = tfra.entries.indexOf(bestSegment);
const offsetOfNext = currentSegmentIndex === tfra.entries.length - 1
? Infinity
: tfra.entries[currentSegmentIndex + 1].moofOffset;
return {
start: bestSegment.moofOffset,
end: offsetOfNext,
};
};
exports.findBestSegmentFromTfra = findBestSegmentFromTfra;

View File

@@ -0,0 +1,85 @@
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 getMfraAtom: ({ src, contentLength, readerInterface, controller, parentSize, logLevel, prefetchCache, }: {
src: ParseMediaSrc;
contentLength: number;
readerInterface: MediaParserReaderInterface;
controller: MediaParserController;
parentSize: number;
logLevel: MediaParserLogLevel;
prefetchCache: PrefetchCache;
}) => Promise<{
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: import("../../../options").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;
}>;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMfraAtom = void 0;
const buffer_iterator_1 = require("../../../iterator/buffer-iterator");
const getMfraAtom = async ({ src, contentLength, readerInterface, controller, parentSize, logLevel, prefetchCache, }) => {
const result = await readerInterface.read({
controller,
range: [contentLength - parentSize, contentLength - 1],
src,
logLevel,
prefetchCache,
});
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: new Uint8Array(),
maxBytes: parentSize,
logLevel: 'error',
});
while (true) {
const res = await result.reader.reader.read();
if (res.value) {
iterator.addData(res.value);
}
if (res.done) {
break;
}
}
return iterator;
};
exports.getMfraAtom = getMfraAtom;

View File

@@ -0,0 +1,13 @@
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 getMfroAtom: ({ src, contentLength, readerInterface, controller, logLevel, prefetchCache, }: {
src: ParseMediaSrc;
contentLength: number;
readerInterface: MediaParserReaderInterface;
controller: MediaParserController;
logLevel: MediaParserLogLevel;
prefetchCache: PrefetchCache;
}) => Promise<number | null>;

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMfroAtom = void 0;
const buffer_iterator_1 = require("../../../iterator/buffer-iterator");
const getMfroAtom = async ({ src, contentLength, readerInterface, controller, logLevel, prefetchCache, }) => {
const result = await readerInterface.read({
controller,
range: [contentLength - 16, contentLength - 1],
src,
logLevel,
prefetchCache,
});
const { value } = await result.reader.reader.read();
if (!value) {
return null;
}
result.reader.abort();
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: value,
maxBytes: value.length,
logLevel: 'error',
});
const size = iterator.getUint32();
if (size !== 16) {
iterator.destroy();
return null;
}
const atom = iterator.getByteString(4, false);
if (atom !== 'mfro') {
iterator.destroy();
return null;
}
const version = iterator.getUint8();
if (version !== 0) {
iterator.destroy();
return null;
}
// flags
iterator.discard(3);
const parentSize = iterator.getUint32();
iterator.destroy();
return parentSize;
};
exports.getMfroAtom = getMfroAtom;

View File

@@ -0,0 +1,19 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
export type TfraEntry = {
time: number;
moofOffset: number;
trafNumber: number;
trunNumber: number;
sampleNumber: number;
};
export interface TfraBox extends BaseBox {
type: 'tfra-box';
trackId: number;
entries: TfraEntry[];
}
export declare const parseTfraBox: ({ iterator, size, offset, }: {
iterator: BufferIterator;
size: number;
offset: number;
}) => TfraBox;

View File

@@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseTfraBox = void 0;
const readTrafNumber = (iterator, lengthSizeOfTrafNum) => {
const uintTypeTrafNum = (lengthSizeOfTrafNum + 1) * 8;
if (uintTypeTrafNum === 8) {
return iterator.getUint8();
}
if (uintTypeTrafNum === 16) {
return iterator.getUint16();
}
if (uintTypeTrafNum === 32) {
return iterator.getUint32();
}
if (uintTypeTrafNum === 64) {
return Number(iterator.getUint64());
}
throw new Error('Invalid traf number size');
};
const readTrunNumber = (iterator, lengthSizeOfTrunNum) => {
const uintTypeTrunNum = (lengthSizeOfTrunNum + 1) * 8;
if (uintTypeTrunNum === 8) {
return iterator.getUint8();
}
if (uintTypeTrunNum === 16) {
return iterator.getUint16();
}
if (uintTypeTrunNum === 32) {
return iterator.getUint32();
}
if (uintTypeTrunNum === 64) {
return Number(iterator.getUint64());
}
throw new Error('Invalid trun number size');
};
const readSampleNumber = (iterator, lengthSizeOfSampleNum) => {
const uintTypeSampleNum = (lengthSizeOfSampleNum + 1) * 8;
if (uintTypeSampleNum === 8) {
return iterator.getUint8();
}
if (uintTypeSampleNum === 16) {
return iterator.getUint16();
}
if (uintTypeSampleNum === 32) {
return iterator.getUint32();
}
if (uintTypeSampleNum === 64) {
return Number(iterator.getUint64());
}
throw new Error('Invalid sample number size');
};
const readTime = (iterator, version) => {
if (version === 1) {
return Number(iterator.getUint64());
}
return iterator.getUint32();
};
const readMoofOffset = (iterator, version) => {
if (version === 1) {
return Number(iterator.getUint64());
}
return iterator.getUint32();
};
const parseTfraBox = ({ iterator, size, offset, }) => {
const box = iterator.startBox(size - 8);
const version = iterator.getUint8();
// flags, we discard them
iterator.discard(3);
const trackId = iterator.getUint32();
iterator.getUint24();
const tmpByte = iterator.getUint8();
const lengthSizeOfTrafNum = (tmpByte >> 4) & 0x3;
const lengthSizeOfTrunNum = (tmpByte >> 2) & 0x3;
const lengthSizeOfSampleNum = tmpByte & 0x3;
const numberOfEntries = iterator.getUint32();
const entries = [];
for (let i = 0; i < numberOfEntries; i++) {
const time = readTime(iterator, version);
const moofOffset = readMoofOffset(iterator, version);
const trafNumber = readTrafNumber(iterator, lengthSizeOfTrafNum);
const trunNumber = readTrunNumber(iterator, lengthSizeOfTrunNum);
const sampleNumber = readSampleNumber(iterator, lengthSizeOfSampleNum);
entries.push({
time,
moofOffset,
trafNumber,
trunNumber,
sampleNumber,
});
}
box.expectNoMoreBytes();
return {
offset,
boxSize: size,
type: 'tfra-box',
entries,
trackId,
};
};
exports.parseTfraBox = parseTfraBox;

View File

@@ -0,0 +1,17 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { MediaParserLogLevel } from '../../../log';
import type { AnySegment } from '../../../parse-result';
import type { BaseBox } from '../base-type';
import type { OnlyIfMoovAtomExpected } from '../process-box';
export interface MoovBox extends BaseBox {
type: 'moov-box';
children: AnySegment[];
}
export declare const parseMoov: ({ offset, size, onlyIfMoovAtomExpected, iterator, logLevel, contentLength, }: {
offset: number;
size: number;
onlyIfMoovAtomExpected: OnlyIfMoovAtomExpected;
iterator: BufferIterator;
logLevel: MediaParserLogLevel;
contentLength: number;
}) => Promise<MoovBox>;

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMoov = void 0;
const get_children_1 = require("../get-children");
const parseMoov = async ({ offset, size, onlyIfMoovAtomExpected, iterator, logLevel, contentLength, }) => {
const children = await (0, get_children_1.getIsoBaseMediaChildren)({
onlyIfMoovAtomExpected,
size: size - 8,
iterator,
logLevel,
contentLength,
});
return {
offset,
boxSize: size,
type: 'moov-box',
children,
};
};
exports.parseMoov = parseMoov;

View File

@@ -0,0 +1,30 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
export type ThreeDMatrix = [
number,
number,
number,
number,
number,
number,
number,
number,
number
];
export interface MvhdBox extends BaseBox {
durationInUnits: number;
durationInSeconds: number;
creationTime: number | null;
modificationTime: number | null;
timeScale: number;
rate: number;
volume: number;
matrix: ThreeDMatrix;
nextTrackId: number;
type: 'mvhd-box';
}
export declare const parseMvhd: ({ iterator, offset, size, }: {
iterator: BufferIterator;
offset: number;
size: number;
}) => MvhdBox;

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMvhd = void 0;
const buffer_iterator_1 = require("../../../iterator/buffer-iterator");
const to_date_1 = require("../to-date");
const parseMvhd = ({ iterator, offset, size, }) => {
const version = iterator.getUint8();
// Flags, we discard them
iterator.discard(3);
const creationTime = version === 1 ? iterator.getUint64() : iterator.getUint32();
const modificationTime = version === 1 ? iterator.getUint64() : iterator.getUint32();
const timeScale = iterator.getUint32();
const durationInUnits = version === 1 ? iterator.getUint64() : iterator.getUint32();
const durationInSeconds = Number(durationInUnits) / timeScale;
const rateArray = iterator.getSlice(4);
const rateView = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: rateArray,
maxBytes: rateArray.length,
logLevel: 'error',
});
const rate = rateView.getInt8() * 10 +
rateView.getInt8() +
rateView.getInt8() * 0.1 +
rateView.getInt8() * 0.01;
const volumeArray = iterator.getSlice(2);
const volumeView = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: volumeArray,
maxBytes: volumeArray.length,
logLevel: 'error',
});
const volume = volumeView.getInt8() + volumeView.getInt8() * 0.1;
// reserved 16bit
iterator.discard(2);
// reserved 32bit x2
iterator.discard(4);
iterator.discard(4);
// matrix
const matrix = [
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned230Number(),
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned230Number(),
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned1616Number(),
iterator.getFixedPointSigned230Number(),
];
// pre-defined
iterator.discard(4 * 6);
// next track id
const nextTrackId = iterator.getUint32();
volumeView.destroy();
const bytesRemaining = size - (iterator.counter.getOffset() - offset);
if (bytesRemaining !== 0) {
throw new Error('expected 0 bytes ' + bytesRemaining);
}
return {
creationTime: (0, to_date_1.toUnixTimestamp)(Number(creationTime)),
modificationTime: (0, to_date_1.toUnixTimestamp)(Number(modificationTime)),
timeScale,
durationInUnits: Number(durationInUnits),
durationInSeconds,
rate,
volume,
matrix: matrix,
nextTrackId,
type: 'mvhd-box',
boxSize: size,
offset,
};
};
exports.parseMvhd = parseMvhd;

View File

@@ -0,0 +1,16 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
export interface TrexBox extends BaseBox {
type: 'trex-box';
version: number;
trackId: number;
defaultSampleDescriptionIndex: number;
defaultSampleDuration: number;
defaultSampleSize: number;
defaultSampleFlags: number;
}
export declare const parseTrex: ({ iterator, offset, size, }: {
iterator: BufferIterator;
offset: number;
size: number;
}) => TrexBox;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseTrex = void 0;
const parseTrex = ({ iterator, offset, size, }) => {
const box = iterator.startBox(size - 8);
const version = iterator.getUint8();
// Flags, we discard them
iterator.discard(3);
const trackId = iterator.getUint32();
const defaultSampleDescriptionIndex = iterator.getUint32();
const defaultSampleDuration = iterator.getUint32();
const defaultSampleSize = iterator.getUint32();
const defaultSampleFlags = iterator.getUint32();
box.expectNoMoreBytes();
return {
type: 'trex-box',
boxSize: size,
offset,
trackId,
version,
defaultSampleDescriptionIndex,
defaultSampleDuration,
defaultSampleSize,
defaultSampleFlags,
};
};
exports.parseTrex = parseTrex;

View File

@@ -0,0 +1,3 @@
import type { ParseResult } from '../../parse-result';
import type { ParserState } from '../../state/parser-state';
export declare const parseIsoBaseMedia: (state: ParserState) => Promise<ParseResult>;

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseIsoBaseMedia = void 0;
const mdat_1 = require("./mdat/mdat");
const process_box_1 = require("./process-box");
const parseIsoBaseMedia = async (state) => {
const mediaSectionState = state.mediaSection.isCurrentByteInMediaSection(state.iterator);
if (mediaSectionState === 'in-section') {
const skipTo = await (0, mdat_1.parseMdatSection)(state);
return skipTo;
}
const result = await (0, process_box_1.processBox)({
iterator: state.iterator,
logLevel: state.logLevel,
onlyIfMoovAtomExpected: {
tracks: state.callbacks.tracks,
isoState: state.iso,
movieTimeScaleState: state.iso.movieTimeScale,
onAudioTrack: state.onAudioTrack,
onVideoTrack: state.onVideoTrack,
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
},
onlyIfMdatAtomExpected: {
mediaSectionState: state.mediaSection,
},
contentLength: state.contentLength,
});
if (result.type === 'fetch-more-data') {
return result.bytesNeeded;
}
if (result.type === 'box') {
state.structure.getIsoStructure().boxes.push(result.box);
}
return null;
};
exports.parseIsoBaseMedia = parseIsoBaseMedia;

View File

@@ -0,0 +1,36 @@
type Point = {
x: number;
y: number;
z: number;
};
export type IccProfile = {
size: number;
preferredCMMType: string;
profileVersion: string;
profileDeviceClass: string;
colorSpace: string;
pcs: string;
dateTime: Uint8Array;
signature: string;
primaryPlatform: string;
profileFlags: number;
deviceManufacturer: string;
deviceModel: string;
deviceAttributes: bigint;
renderingIntent: number;
pcsIlluminant: [number, number, number];
profileCreator: string;
profileId: string;
entries: Entry[];
rXYZ: Point | null;
gXYZ: Point | null;
bXYZ: Point | null;
whitePoint: Point | null;
};
type Entry = {
tag: string;
size: number;
offset: number;
};
export declare const parseIccProfile: (data: Uint8Array) => IccProfile;
export {};

View File

@@ -0,0 +1,123 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseIccProfile = void 0;
const buffer_iterator_1 = require("../../iterator/buffer-iterator");
const parseIccProfile = (data) => {
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: data,
maxBytes: data.length,
logLevel: 'error',
});
const size = iterator.getUint32();
if (size !== data.length) {
throw new Error('Invalid ICC profile size');
}
const preferredCMMType = iterator.getByteString(4, false);
const profileVersion = iterator.getByteString(4, false);
const profileDeviceClass = iterator.getByteString(4, false);
const colorSpace = iterator.getByteString(4, false);
const pcs = iterator.getByteString(4, false);
const dateTime = iterator.getSlice(12);
const signature = iterator.getByteString(4, false);
if (signature !== 'acsp') {
throw new Error('Invalid ICC profile signature');
}
const primaryPlatform = iterator.getByteString(4, false);
const profileFlags = iterator.getUint32();
const deviceManufacturer = iterator.getByteString(4, false);
const deviceModel = iterator.getByteString(4, false);
const deviceAttributes = iterator.getUint64();
const renderingIntent = iterator.getUint32();
const pcsIlluminant1 = iterator.getUint32();
const pcsIlluminant2 = iterator.getUint32();
const pcsIlluminant3 = iterator.getUint32();
const profileCreator = iterator.getByteString(4, false);
const profileId = iterator.getByteString(16, false);
// reserved
iterator.discard(28);
const tagCount = iterator.getUint32();
const entries = [];
for (let i = 0; i < tagCount; i++) {
const entry = {
tag: iterator.getByteString(4, false),
offset: iterator.getUint32(),
size: iterator.getUint32(),
};
entries.push(entry);
}
let lastOffset = -1;
let rXYZ = null;
let gXYZ = null;
let bXYZ = null;
let whitePoint = null;
for (const entry of entries) {
const found = data.slice(entry.offset, entry.offset + entry.size);
if (entry.tag === 'rXYZ' ||
entry.tag === 'gXYZ' ||
entry.tag === 'bXYZ' ||
entry.tag === 'wtpt') {
const it = (0, buffer_iterator_1.getArrayBufferIterator)({
initialData: found,
maxBytes: found.length,
logLevel: 'error',
});
it.discard(4);
const x = it.getInt32() / 65536;
const y = it.getInt32() / 65536;
const z = it.getInt32() / 65536;
it.destroy();
const point = { x, y, z };
if (entry.tag === 'rXYZ') {
rXYZ = point;
}
else if (entry.tag === 'gXYZ') {
gXYZ = point;
}
else if (entry.tag === 'bXYZ') {
bXYZ = point;
}
else if (entry.tag === 'wtpt') {
whitePoint = point;
}
}
if (lastOffset !== -1) {
const bytesToAdvance = entry.offset - lastOffset;
const bytesToGoBackwards = entry.size - bytesToAdvance;
if (bytesToGoBackwards > 0) {
iterator.counter.decrement(bytesToGoBackwards);
}
}
lastOffset = entry.offset;
}
const profile = {
size,
preferredCMMType,
profileVersion,
profileDeviceClass,
colorSpace,
pcs,
dateTime,
signature,
primaryPlatform,
profileFlags,
deviceManufacturer,
deviceModel,
deviceAttributes,
renderingIntent,
pcsIlluminant: [
pcsIlluminant1 / 65536,
pcsIlluminant2 / 65536,
pcsIlluminant3 / 65536,
],
profileCreator,
profileId,
entries,
bXYZ,
gXYZ,
rXYZ,
whitePoint,
};
iterator.destroy();
return profile;
};
exports.parseIccProfile = parseIccProfile;

View File

@@ -0,0 +1,39 @@
import type { BufferIterator } from '../../iterator/buffer-iterator';
import type { MediaParserLogLevel } from '../../log';
import type { FetchMoreData } from '../../skip';
import type { TracksState } from '../../state/has-tracks-section';
import type { IsoBaseMediaState } from '../../state/iso-base-media/iso-state';
import type { MovieTimeScaleState } from '../../state/iso-base-media/timescale-state';
import type { CallbacksState } from '../../state/sample-callbacks';
import type { MediaSectionState } from '../../state/video-section';
import type { MediaParserOnAudioTrack, MediaParserOnVideoTrack } from '../../webcodec-sample-types';
import type { IsoBaseMediaBox } from './base-media-box';
export type OnlyIfMoovAtomExpected = {
tracks: TracksState;
isoState: IsoBaseMediaState | null;
movieTimeScaleState: MovieTimeScaleState;
onVideoTrack: MediaParserOnVideoTrack | null;
onAudioTrack: MediaParserOnAudioTrack | null;
registerVideoSampleCallback: CallbacksState['registerVideoSampleCallback'];
registerAudioSampleCallback: CallbacksState['registerAudioSampleCallback'];
};
export type OnlyIfMdatAtomExpected = {
mediaSectionState: MediaSectionState;
};
type ProcessBoxResult = {
type: 'box';
box: IsoBaseMediaBox;
} | {
type: 'nothing';
} | {
type: 'fetch-more-data';
bytesNeeded: FetchMoreData;
};
export declare const processBox: ({ iterator, logLevel, onlyIfMoovAtomExpected, onlyIfMdatAtomExpected, contentLength, }: {
iterator: BufferIterator;
logLevel: MediaParserLogLevel;
onlyIfMoovAtomExpected: OnlyIfMoovAtomExpected | null;
onlyIfMdatAtomExpected: OnlyIfMdatAtomExpected | null;
contentLength: number;
}) => Promise<ProcessBoxResult>;
export {};

View File

@@ -0,0 +1,454 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processBox = void 0;
const log_1 = require("../../log");
const register_track_1 = require("../../register-track");
const skip_1 = require("../../skip");
const elst_1 = require("./elst");
const esds_1 = require("./esds/esds");
const ftyp_1 = require("./ftyp");
const get_children_1 = require("./get-children");
const make_track_1 = require("./make-track");
const get_editlist_1 = require("./mdat/get-editlist");
const mdhd_1 = require("./mdhd");
const hdlr_1 = require("./meta/hdlr");
const ilst_1 = require("./meta/ilst");
const tfra_1 = require("./mfra/tfra");
const moov_1 = require("./moov/moov");
const mvhd_1 = require("./moov/mvhd");
const trex_1 = require("./moov/trex");
const av1c_1 = require("./stsd/av1c");
const avcc_1 = require("./stsd/avcc");
const colr_1 = require("./stsd/colr");
const ctts_1 = require("./stsd/ctts");
const hvcc_1 = require("./stsd/hvcc");
const keys_1 = require("./stsd/keys");
const mebx_1 = require("./stsd/mebx");
const pasp_1 = require("./stsd/pasp");
const stco_1 = require("./stsd/stco");
const stsc_1 = require("./stsd/stsc");
const stsd_1 = require("./stsd/stsd");
const stss_1 = require("./stsd/stss");
const stsz_1 = require("./stsd/stsz");
const stts_1 = require("./stsd/stts");
const vpcc_1 = require("./stsd/vpcc");
const tfdt_1 = require("./tfdt");
const tfhd_1 = require("./tfhd");
const tkhd_1 = require("./tkhd");
const trak_1 = require("./trak/trak");
const trun_1 = require("./trun");
const processBox = async ({ iterator, logLevel, onlyIfMoovAtomExpected, onlyIfMdatAtomExpected, contentLength, }) => {
var _a, _b;
const fileOffset = iterator.counter.getOffset();
const { returnToCheckpoint } = iterator.startCheckpoint();
const bytesRemaining = iterator.bytesRemaining();
const startOff = iterator.counter.getOffset();
const boxSizeRaw = iterator.getFourByteNumber();
if (boxSizeRaw === 0) {
return {
type: 'box',
box: {
type: 'void-box',
boxSize: 0,
},
};
}
// If `boxSize === 1`, the 8 bytes after the box type are the size of the box.
if ((boxSizeRaw === 1 && iterator.bytesRemaining() < 12) ||
iterator.bytesRemaining() < 4) {
iterator.counter.decrement(iterator.counter.getOffset() - fileOffset);
throw new Error(`Expected box size of ${bytesRemaining}, got ${boxSizeRaw}. Incomplete boxes are not allowed.`);
}
const maxSize = contentLength - startOff;
const boxType = iterator.getByteString(4, false);
const boxSizeUnlimited = boxSizeRaw === 1 ? iterator.getEightByteNumber() : boxSizeRaw;
const boxSize = Math.min(boxSizeUnlimited, maxSize);
const headerLength = iterator.counter.getOffset() - startOff;
if (boxType === 'mdat') {
if (!onlyIfMdatAtomExpected) {
return { type: 'nothing' };
}
const { mediaSectionState } = onlyIfMdatAtomExpected;
mediaSectionState.addMediaSection({
size: boxSize - headerLength,
start: iterator.counter.getOffset(),
});
return { type: 'nothing' };
}
if (bytesRemaining < boxSize) {
returnToCheckpoint();
return {
type: 'fetch-more-data',
bytesNeeded: (0, skip_1.makeFetchMoreData)(boxSize - bytesRemaining),
};
}
if (boxType === 'ftyp') {
return {
type: 'box',
box: (0, ftyp_1.parseFtyp)({ iterator, size: boxSize, offset: fileOffset }),
};
}
if (boxType === 'elst') {
return {
type: 'box',
box: (0, elst_1.parseElst)({
iterator,
size: boxSize,
offset: fileOffset,
}),
};
}
if (boxType === 'colr') {
return {
type: 'box',
box: (0, colr_1.parseColorParameterBox)({
iterator,
size: boxSize,
}),
};
}
if (boxType === 'mvhd') {
const mvhdBox = (0, mvhd_1.parseMvhd)({
iterator,
offset: fileOffset,
size: boxSize,
});
if (!onlyIfMoovAtomExpected) {
throw new Error('State is required');
}
onlyIfMoovAtomExpected.movieTimeScaleState.setTrackTimescale(mvhdBox.timeScale);
return {
type: 'box',
box: mvhdBox,
};
}
if (boxType === 'tkhd') {
return {
type: 'box',
box: (0, tkhd_1.parseTkhd)({ iterator, offset: fileOffset, size: boxSize }),
};
}
if (boxType === 'trun') {
return {
type: 'box',
box: (0, trun_1.parseTrun)({ iterator, offset: fileOffset, size: boxSize }),
};
}
if (boxType === 'tfdt') {
return {
type: 'box',
box: (0, tfdt_1.parseTfdt)({ iterator, size: boxSize, offset: fileOffset }),
};
}
if (boxType === 'stsd') {
return {
type: 'box',
box: await (0, stsd_1.parseStsd)({
offset: fileOffset,
size: boxSize,
iterator,
logLevel,
contentLength,
}),
};
}
if (boxType === 'stsz') {
return {
type: 'box',
box: (0, stsz_1.parseStsz)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'stco' || boxType === 'co64') {
return {
type: 'box',
box: (0, stco_1.parseStco)({
iterator,
offset: fileOffset,
size: boxSize,
mode64Bit: boxType === 'co64',
}),
};
}
if (boxType === 'pasp') {
return {
type: 'box',
box: (0, pasp_1.parsePasp)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'stss') {
return {
type: 'box',
box: (0, stss_1.parseStss)({
iterator,
offset: fileOffset,
boxSize,
}),
};
}
if (boxType === 'ctts') {
return {
type: 'box',
box: (0, ctts_1.parseCtts)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'stsc') {
return {
type: 'box',
box: (0, stsc_1.parseStsc)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'mebx') {
return {
type: 'box',
box: await (0, mebx_1.parseMebx)({
offset: fileOffset,
size: boxSize,
iterator,
logLevel,
contentLength,
}),
};
}
if (boxType === 'hdlr') {
return {
type: 'box',
box: await (0, hdlr_1.parseHdlr)({ iterator, size: boxSize, offset: fileOffset }),
};
}
if (boxType === 'keys') {
return {
type: 'box',
box: await (0, keys_1.parseKeys)({ iterator, size: boxSize, offset: fileOffset }),
};
}
if (boxType === 'ilst') {
return {
type: 'box',
box: await (0, ilst_1.parseIlstBox)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'tfra') {
return {
type: 'box',
box: await (0, tfra_1.parseTfraBox)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'moov') {
if (!onlyIfMoovAtomExpected) {
throw new Error('State is required');
}
const { tracks, isoState } = onlyIfMoovAtomExpected;
if (tracks.hasAllTracks()) {
iterator.discard(boxSize - 8);
return { type: 'nothing' };
}
if (isoState &&
isoState.moov.getMoovBoxAndPrecomputed() &&
!((_a = isoState.moov.getMoovBoxAndPrecomputed()) === null || _a === void 0 ? void 0 : _a.precomputed)) {
log_1.Log.verbose(logLevel, 'Moov box already parsed, skipping');
iterator.discard(boxSize - 8);
return { type: 'nothing' };
}
const box = await (0, moov_1.parseMoov)({
offset: fileOffset,
size: boxSize,
onlyIfMoovAtomExpected,
iterator,
logLevel,
contentLength,
});
tracks.setIsDone(logLevel);
return { type: 'box', box };
}
if (boxType === 'trak') {
if (!onlyIfMoovAtomExpected) {
throw new Error('State is required');
}
const { tracks, onAudioTrack, onVideoTrack } = onlyIfMoovAtomExpected;
const trakBox = await (0, trak_1.parseTrak)({
size: boxSize,
offsetAtStart: fileOffset,
iterator,
logLevel,
contentLength,
});
const movieTimeScale = onlyIfMoovAtomExpected.movieTimeScaleState.getTrackTimescale();
if (movieTimeScale === null) {
throw new Error('Movie timescale is not set');
}
const editList = (0, get_editlist_1.findTrackStartTimeInSeconds)({ movieTimeScale, trakBox });
const transformedTrack = (0, make_track_1.makeBaseMediaTrack)(trakBox, editList);
if (transformedTrack && transformedTrack.type === 'video') {
await (0, register_track_1.registerVideoTrack)({
track: transformedTrack,
container: 'mp4',
logLevel,
onVideoTrack,
registerVideoSampleCallback: onlyIfMoovAtomExpected.registerVideoSampleCallback,
tracks,
});
}
if (transformedTrack && transformedTrack.type === 'audio') {
await (0, register_track_1.registerAudioTrack)({
track: transformedTrack,
container: 'mp4',
registerAudioSampleCallback: onlyIfMoovAtomExpected.registerAudioSampleCallback,
tracks,
logLevel,
onAudioTrack,
});
}
return { type: 'box', box: trakBox };
}
if (boxType === 'stts') {
return {
type: 'box',
box: (0, stts_1.parseStts)({
data: iterator,
size: boxSize,
fileOffset,
}),
};
}
if (boxType === 'avcC') {
return {
type: 'box',
box: (0, avcc_1.parseAvcc)({
data: iterator,
size: boxSize,
}),
};
}
if (boxType === 'vpcC') {
return {
type: 'box',
box: (0, vpcc_1.parseVpcc)({ data: iterator, size: boxSize }),
};
}
if (boxType === 'av1C') {
return {
type: 'box',
box: (0, av1c_1.parseAv1C)({
data: iterator,
size: boxSize,
}),
};
}
if (boxType === 'hvcC') {
return {
type: 'box',
box: (0, hvcc_1.parseHvcc)({
data: iterator,
size: boxSize,
offset: fileOffset,
}),
};
}
if (boxType === 'tfhd') {
return {
type: 'box',
box: (0, tfhd_1.getTfhd)({
iterator,
offset: fileOffset,
size: boxSize,
}),
};
}
if (boxType === 'mdhd') {
return {
type: 'box',
box: (0, mdhd_1.parseMdhd)({
data: iterator,
size: boxSize,
fileOffset,
}),
};
}
if (boxType === 'esds') {
return {
type: 'box',
box: (0, esds_1.parseEsds)({
data: iterator,
size: boxSize,
fileOffset,
}),
};
}
if (boxType === 'trex') {
return {
type: 'box',
box: (0, trex_1.parseTrex)({ iterator, offset: fileOffset, size: boxSize }),
};
}
if (boxType === 'moof') {
await ((_b = onlyIfMoovAtomExpected === null || onlyIfMoovAtomExpected === void 0 ? void 0 : onlyIfMoovAtomExpected.isoState) === null || _b === void 0 ? void 0 : _b.mfra.triggerLoad());
}
if (boxType === 'mdia' ||
boxType === 'minf' ||
boxType === 'stbl' ||
boxType === 'udta' ||
boxType === 'moof' ||
boxType === 'dims' ||
boxType === 'meta' ||
boxType === 'wave' ||
boxType === 'traf' ||
boxType === 'mfra' ||
boxType === 'edts' ||
boxType === 'mvex' ||
boxType === 'stsb') {
const children = await (0, get_children_1.getIsoBaseMediaChildren)({
iterator,
size: boxSize - 8,
logLevel,
onlyIfMoovAtomExpected,
contentLength,
});
return {
type: 'box',
box: {
type: 'regular-box',
boxType,
boxSize,
children,
offset: fileOffset,
},
};
}
iterator.discard(boxSize - 8);
log_1.Log.verbose(logLevel, 'Unknown ISO Base Media Box:', boxType);
return {
type: 'box',
box: {
type: 'regular-box',
boxType,
boxSize,
children: [],
offset: fileOffset,
},
};
};
exports.processBox = processBox;

View File

@@ -0,0 +1,16 @@
import type { IsoBaseMediaStructure } from '../../parse-result';
import type { IsoBaseMediaSeekingHints } from '../../seeking-hints';
import type { IsoBaseMediaState } from '../../state/iso-base-media/iso-state';
import type { ParserState } from '../../state/parser-state';
import type { StructureState } from '../../state/structure';
import type { MediaSectionState } from '../../state/video-section';
export declare const getSeekingHintsFromMp4: ({ structureState, isoState, mp4HeaderSegment, mediaSectionState, }: {
structureState: StructureState;
isoState: IsoBaseMediaState;
mp4HeaderSegment: IsoBaseMediaStructure | null;
mediaSectionState: MediaSectionState;
}) => IsoBaseMediaSeekingHints | null;
export declare const setSeekingHintsForMp4: ({}: {
hints: IsoBaseMediaSeekingHints;
state: ParserState;
}) => void;

View File

@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setSeekingHintsForMp4 = exports.getSeekingHintsFromMp4 = void 0;
const precomputed_moof_1 = require("../../state/iso-base-media/precomputed-moof");
const precomputed_tfra_1 = require("../../state/iso-base-media/precomputed-tfra");
const traversal_1 = require("./traversal");
const getSeekingHintsFromMp4 = ({ structureState, isoState, mp4HeaderSegment, mediaSectionState, }) => {
const structure = structureState.getIsoStructure();
const moovAtom = (0, traversal_1.getMoovBoxFromState)({
isoState,
mp4HeaderSegment,
structureState,
mayUsePrecomputed: true,
});
const moofBoxes = (0, precomputed_moof_1.deduplicateMoofBoxesByOffset)([
...isoState.moof.getMoofBoxes(),
...(0, traversal_1.getMoofBoxes)(structure.boxes),
]);
const tfraBoxes = (0, precomputed_tfra_1.deduplicateTfraBoxesByOffset)([
...isoState.tfra.getTfraBoxes(),
...(0, traversal_1.getTfraBoxes)(structure.boxes),
]);
if (!moovAtom) {
return null;
}
return {
type: 'iso-base-media-seeking-hints',
moovBox: moovAtom,
moofBoxes,
tfraBoxes,
mediaSections: mediaSectionState.getMediaSections(),
mfraAlreadyLoaded: isoState.mfra.getIfAlreadyLoaded(),
};
};
exports.getSeekingHintsFromMp4 = getSeekingHintsFromMp4;
// eslint-disable-next-line no-empty-pattern
const setSeekingHintsForMp4 = ({}) => {
// state.iso.moov.setMoovBox({
// moovBox: hints.moovBox,
// precomputed: true,
// });
// state.iso.mfra.setFromSeekingHints(hints);
// state.iso.moof.setMoofBoxes(hints.moofBoxes);
// TODO: Make use of these seeking hints and make tests pass
/*
// state.iso.tfra.setTfraBoxes(hints.tfraBoxes);
for (const mediaSection of hints.mediaSections) {
// state.mediaSection.addMediaSection(mediaSection);
}
*/
};
exports.setSeekingHintsForMp4 = setSeekingHintsForMp4;

View File

@@ -0,0 +1,6 @@
import type { TrakBox } from './trak/trak';
type ShouldGroup = {
bigEndian: boolean;
};
export declare const shouldGroupAudioSamples: (trakBox: TrakBox) => ShouldGroup | null;
export {};

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.shouldGroupAudioSamples = void 0;
const get_audio_codec_1 = require("../../get-audio-codec");
const shouldGroupAudioSamples = (trakBox) => {
const isLpcm = (0, get_audio_codec_1.isLpcmAudioCodec)(trakBox);
const isIn24 = (0, get_audio_codec_1.isIn24AudioCodec)(trakBox);
const isTwos = (0, get_audio_codec_1.isTwosAudioCodec)(trakBox);
if (isLpcm || isIn24 || isTwos) {
return {
bigEndian: isTwos || isIn24,
};
}
return null;
};
exports.shouldGroupAudioSamples = shouldGroupAudioSamples;

View File

@@ -0,0 +1,9 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
export interface Av1CBox {
type: 'av1C-box';
privateData: Uint8Array;
}
export declare const parseAv1C: ({ data, size, }: {
data: BufferIterator;
size: number;
}) => Av1CBox;

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAv1C = void 0;
const parseAv1C = ({ data, size, }) => {
return {
type: 'av1C-box',
privateData: data.getSlice(size - 8),
};
};
exports.parseAv1C = parseAv1C;

View File

@@ -0,0 +1,10 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
export interface AvccBox {
type: 'avcc-box';
privateData: Uint8Array;
configurationString: string;
}
export declare const parseAvcc: ({ data, size, }: {
data: BufferIterator;
size: number;
}) => AvccBox;

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAvcc = void 0;
const parseAvcc = ({ data, size, }) => {
const confVersion = data.getUint8();
if (confVersion !== 1) {
throw new Error(`Unsupported AVCC version ${confVersion}`);
}
const profile = data.getUint8();
const profileCompatibility = data.getUint8();
const level = data.getUint8();
const str = `${profile.toString(16).padStart(2, '0')}${profileCompatibility.toString(16).padStart(2, '0')}${level.toString(16).padStart(2, '0')}`;
data.counter.decrement(4);
const privateData = data.getSlice(size - 8);
return {
type: 'avcc-box',
privateData,
configurationString: str,
};
};
exports.parseAvcc = parseAvcc;

View File

@@ -0,0 +1,21 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { IccProfile } from '../parse-icc-profile';
type ExtraData = {
colorType: 'transfer-characteristics';
primaries: number;
transfer: number;
matrixIndex: number;
fullRangeFlag: boolean;
} | {
colorType: 'icc-profile';
profile: Uint8Array;
parsed: IccProfile;
};
export type ColorParameterBox = {
type: 'colr-box';
} & ExtraData;
export declare const parseColorParameterBox: ({ iterator, size, }: {
iterator: BufferIterator;
size: number;
}) => ColorParameterBox;
export {};

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseColorParameterBox = void 0;
const parse_icc_profile_1 = require("../parse-icc-profile");
const parseColorParameterBox = ({ iterator, size, }) => {
const byteString = iterator.getByteString(4, false);
if (byteString === 'nclx') {
const primaries = iterator.getUint16();
const transfer = iterator.getUint16();
const matrixIndex = iterator.getUint16();
iterator.startReadingBits();
const fullRangeFlag = Boolean(iterator.getBits(1));
iterator.stopReadingBits();
return {
type: 'colr-box',
colorType: 'transfer-characteristics',
fullRangeFlag,
matrixIndex,
primaries,
transfer,
};
}
if (byteString === 'nclc') {
const primaries = iterator.getUint16();
const transfer = iterator.getUint16();
const matrixIndex = iterator.getUint16();
return {
type: 'colr-box',
colorType: 'transfer-characteristics',
fullRangeFlag: false,
matrixIndex,
primaries,
transfer,
};
}
if (byteString === 'prof') {
const profile = iterator.getSlice(size - 12);
return {
type: 'colr-box',
colorType: 'icc-profile',
profile,
parsed: (0, parse_icc_profile_1.parseIccProfile)(profile),
};
}
throw new Error('Unexpected box type ' + byteString);
};
exports.parseColorParameterBox = parseColorParameterBox;

View File

@@ -0,0 +1,19 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
type CttsEntry = {
sampleCount: number;
sampleOffset: number;
};
export interface CttsBox extends BaseBox {
type: 'ctts-box';
version: number;
flags: number[];
entryCount: number;
entries: CttsEntry[];
}
export declare const parseCtts: ({ iterator, offset, size, }: {
iterator: BufferIterator;
offset: number;
size: number;
}) => CttsBox;
export {};

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCtts = void 0;
const parseCtts = ({ iterator, offset, size, }) => {
const version = iterator.getUint8();
if (version !== 0 && version !== 1) {
throw new Error(`Unsupported CTTS version ${version}`);
}
const flags = iterator.getSlice(3);
const entryCount = iterator.getUint32();
const entries = [];
for (let i = 0; i < entryCount; i++) {
const sampleCount = iterator.getUint32();
// V1 = signed, V0 = unsigned
// however some files are buggy
// Let's do the same thing as mp4box
// https://github.com/gpac/mp4box.js/blob/c6cc468145bc5b031b866446111f29c8b620dbe6/src/parsing/ctts.js#L2
const sampleOffset = iterator.getInt32();
entries.push({
sampleCount,
sampleOffset,
});
}
return {
type: 'ctts-box',
boxSize: size,
offset,
version,
flags: [...flags],
entryCount,
entries,
};
};
exports.parseCtts = parseCtts;

View File

@@ -0,0 +1,11 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
export interface HvccBox {
type: 'hvcc-box';
privateData: Uint8Array;
configurationString: string;
}
export declare const parseHvcc: ({ data, size, offset, }: {
data: BufferIterator;
size: number;
offset: number;
}) => HvccBox;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseHvcc = void 0;
const make_hvc1_codec_strings_1 = require("../../../make-hvc1-codec-strings");
const parseHvcc = ({ data, size, offset, }) => {
const privateData = data.getSlice(size - 8);
data.counter.decrement(size - 8);
const constraintString = (0, make_hvc1_codec_strings_1.getHvc1CodecString)(data);
const remaining = size - (data.counter.getOffset() - offset);
data.discard(remaining);
return {
type: 'hvcc-box',
privateData,
configurationString: constraintString,
};
};
exports.parseHvcc = parseHvcc;

View File

@@ -0,0 +1,19 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
type KeysEntry = {
keySize: number;
namespace: string;
value: string;
};
export interface KeysBox extends BaseBox {
type: 'keys-box';
version: number;
entryCount: number;
entries: KeysEntry[];
}
export declare const parseKeys: ({ iterator, offset, size, }: {
iterator: BufferIterator;
offset: number;
size: number;
}) => KeysBox;
export {};

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseKeys = void 0;
const parseKeys = ({ iterator, offset, size, }) => {
const box = iterator.startBox(size - 8);
const version = iterator.getUint8();
// flags
iterator.discard(3);
// entry_count
const entryCount = iterator.getUint32();
const entries = [];
for (let i = 0; i < entryCount; i++) {
// key_size
const keySize = iterator.getUint32();
const namespace = iterator.getAtom();
const value = iterator.getByteString(keySize - 8, false);
// data
const entry = {
keySize,
namespace,
value,
};
entries.push(entry);
}
box.discardRest();
return {
type: 'keys-box',
boxSize: size,
offset,
version,
entryCount,
entries,
};
};
exports.parseKeys = parseKeys;

View File

@@ -0,0 +1,17 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { MediaParserLogLevel } from '../../../log';
import type { AnySegment } from '../../../parse-result';
import type { BaseBox } from '../base-type';
export interface MebxBox extends BaseBox {
type: 'mebx-box';
dataReferenceIndex: number;
format: string;
children: AnySegment[];
}
export declare const parseMebx: ({ offset, size, iterator, logLevel, contentLength, }: {
offset: number;
size: number;
iterator: BufferIterator;
logLevel: MediaParserLogLevel;
contentLength: number;
}) => Promise<MebxBox>;

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMebx = void 0;
const get_children_1 = require("../get-children");
const parseMebx = async ({ offset, size, iterator, logLevel, contentLength, }) => {
// reserved, 6 bit
iterator.discard(6);
const dataReferenceIndex = iterator.getUint16();
const children = await (0, get_children_1.getIsoBaseMediaChildren)({
iterator,
size: size - 8,
logLevel,
onlyIfMoovAtomExpected: null,
contentLength,
});
return {
type: 'mebx-box',
boxSize: size,
offset,
dataReferenceIndex,
format: 'mebx',
children,
};
};
exports.parseMebx = parseMebx;

View File

@@ -0,0 +1,12 @@
import type { BufferIterator } from '../../../iterator/buffer-iterator';
import type { BaseBox } from '../base-type';
export interface PaspBox extends BaseBox {
type: 'pasp-box';
hSpacing: number;
vSpacing: number;
}
export declare const parsePasp: ({ iterator, offset, size, }: {
iterator: BufferIterator;
offset: number;
size: number;
}) => PaspBox;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePasp = void 0;
const parsePasp = ({ iterator, offset, size, }) => {
const hSpacing = iterator.getUint32();
const vSpacing = iterator.getUint32();
const bytesRemainingInBox = size - (iterator.counter.getOffset() - offset);
iterator.discard(bytesRemainingInBox);
return {
type: 'pasp-box',
boxSize: size,
offset,
hSpacing,
vSpacing,
};
};
exports.parsePasp = parsePasp;

Some files were not shown because too many files have changed in this diff Show More