mediabunny
Version:
Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.
233 lines • 8.03 kB
TypeScript
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { AacCodecInfo, AudioCodec, VideoCodec } from '../codec.js';
import { Av1CodecInfo, AvcDecoderConfigurationRecord, HevcDecoderConfigurationRecord, Vp9CodecInfo } from '../codec-data.js';
import { Demuxer } from '../demuxer.js';
import { Input } from '../input.js';
import { InputTrackBacking } from '../input-track.js';
import { Rotation } from '../misc.js';
import { PsshBox } from './isobmff-misc.js';
import { FileSlice, Reader } from '../reader.js';
import { MetadataTags, TrackDisposition } from '../metadata.js';
type InternalTrack = {
id: number;
demuxer: IsobmffDemuxer;
trackBacking: InputTrackBacking | null;
disposition: TrackDisposition;
timescale: number;
durationInMovieTimescale: number;
durationInMediaTimescale: number;
rotation: Rotation;
internalCodecId: string | null;
name: string | null;
languageCode: string;
sampleTableByteOffset: number | null;
sampleTable: SampleTable | null;
fragmentLookupTable: FragmentLookupTableEntry[];
currentFragmentState: FragmentTrackState | null;
/**
* List of all encountered fragment offsets alongside their timestamps. This list never gets truncated, but memory
* consumption should be negligible.
*/
fragmentPositionCache: {
moofOffset: number;
startTimestamp: number;
endTimestamp: number;
}[];
/** The segment durations of all edit list entries leading up to the main one (from which the offset is taken.) */
editListPreviousSegmentDurations: number;
/** The media time offset of the main edit list entry (with media time !== -1) */
editListOffset: number;
/** Set when the track's samples are encrypted using a supported scheme (cenc/cens/cbcs), parsed from sinf/tenc. */
encryptionInfo: TrackEncryptionInfo | null;
/** For non-fragmented encrypted tracks: parsed saiz+saio from stbl; aux info is fetched lazily on first use. */
encryptionAuxInfo: SampleEncryptionAuxInfo | null;
frmaCodecString: string | null;
} & ({
info: null;
} | {
info: {
type: 'video';
width: number;
height: number;
squarePixelWidth: number;
squarePixelHeight: number;
codec: VideoCodec | null;
codecDescription: Uint8Array | null;
colorSpace: VideoColorSpaceInit | null;
avcType: 1 | 3 | null;
avcCodecInfo: AvcDecoderConfigurationRecord | null;
hevcCodecInfo: HevcDecoderConfigurationRecord | null;
vp9CodecInfo: Vp9CodecInfo | null;
av1CodecInfo: Av1CodecInfo | null;
};
} | {
info: {
type: 'audio';
numberOfChannels: number;
sampleRate: number;
codec: AudioCodec | null;
codecDescription: Uint8Array | null;
aacCodecInfo: AacCodecInfo | null;
pcmLittleEndian: boolean;
pcmSampleSize: number | null;
};
});
type SampleTable = {
sampleTimingEntries: SampleTimingEntry[];
sampleCompositionTimeOffsets: SampleCompositionTimeOffsetEntry[];
sampleSizes: number[];
keySampleIndices: number[] | null;
chunkOffsets: number[];
sampleToChunk: SampleToChunkEntry[];
presentationTimestamps: {
presentationTimestamp: number;
sampleIndex: number;
}[] | null;
/**
* Provides a fast map from sample index to index in the sorted presentation timestamps array - so, a fast map from
* decode order to presentation order.
*/
presentationTimestampIndexMap: number[] | null;
};
type SampleTimingEntry = {
startIndex: number;
startDecodeTimestamp: number;
count: number;
delta: number;
};
type SampleCompositionTimeOffsetEntry = {
startIndex: number;
count: number;
offset: number;
};
type SampleToChunkEntry = {
startSampleIndex: number;
startChunkIndex: number;
samplesPerChunk: number;
sampleDescriptionIndex: number;
};
type FragmentTrackDefaults = {
trackId: number;
defaultSampleDescriptionIndex: number;
defaultSampleDuration: number;
defaultSampleSize: number;
defaultSampleFlags: number;
};
type FragmentLookupTableEntry = {
timestamp: number;
moofOffset: number;
};
type FragmentTrackState = {
baseDataOffset: number;
sampleDescriptionIndex: number | null;
defaultSampleDuration: number | null;
defaultSampleSize: number | null;
defaultSampleFlags: number | null;
startTimestamp: number | null;
encryptionAuxInfo: SampleEncryptionAuxInfo | null;
};
type FragmentTrackData = {
track: InternalTrack;
currentTimestamp: number;
currentOffset: number;
startTimestamp: number;
endTimestamp: number;
firstKeyFrameTimestamp: number | null;
samples: FragmentTrackSample[];
presentationTimestamps: {
presentationTimestamp: number;
sampleIndex: number;
}[];
startTimestampIsFinal: boolean;
encryptionAuxInfo: SampleEncryptionAuxInfo | null;
};
type FragmentTrackSample = {
presentationTimestamp: number;
duration: number;
byteOffset: number;
byteSize: number;
isKeyFrame: boolean;
encryption: SampleEncryptionInfo | null;
};
type Fragment = {
moofOffset: number;
moofSize: number;
implicitBaseDataOffset: number;
trackData: Map<InternalTrack['id'], FragmentTrackData>;
psshBoxes: PsshBox[];
};
type TrackEncryptionInfo = {
scheme: 'cenc' | 'cens' | 'cbcs';
defaultKid: string | null;
defaultIsProtected: boolean | null;
defaultPerSampleIvSize: number | null;
defaultConstantIv: Uint8Array | null;
defaultCryptByteBlock: number | null;
defaultSkipByteBlock: number | null;
};
type SampleEncryptionInfo = {
iv: Uint8Array;
subsamples: {
clearLen: number;
protectedLen: number;
}[] | null;
};
/**
* Holds parsed saiz+saio state. The encryption info itself lives at a file offset and is fetched lazily.
* For fragmented files this state is per-traf; for non-fragmented files it's per-track (on stbl).
*/
type SampleEncryptionAuxInfo = {
defaultSampleInfoSize: number;
sampleSizes: Uint8Array | null;
sampleCount: number;
offset: number | null;
resolved: SampleEncryptionInfo[] | null;
};
export declare class IsobmffDemuxer extends Demuxer {
reader: Reader;
moovSlice: FileSlice | null;
currentTrack: InternalTrack | null;
tracks: InternalTrack[];
metadataPromise: Promise<void> | null;
movieTimescale: number;
movieDurationInTimescale: number;
isQuickTime: boolean;
metadataTags: MetadataTags;
currentMetadataKeys: Map<number, string> | null;
isFragmented: boolean;
fragmentTrackDefaults: FragmentTrackDefaults[];
psshBoxes: PsshBox[];
currentFragment: Fragment | null;
/**
* Caches the last fragment that was read. Based on the assumption that there will be multiple reads to the
* same fragment in quick succession.
*/
lastReadFragment: Fragment | null;
decryptionKeyCache: Map<string, Promise<Uint8Array<ArrayBufferLike>>>;
constructor(input: Input);
getTrackBackings(): Promise<InputTrackBacking[]>;
getMimeType(): Promise<string>;
getMetadataTags(): Promise<MetadataTags>;
readMetadata(): Promise<void>;
getSampleTableForTrack(internalTrack: InternalTrack): SampleTable;
readFragment(startPos: number): Promise<Fragment>;
readContiguousBoxes(slice: FileSlice): void;
iterateContiguousBoxes(slice: FileSlice): Generator<{
boxInfo: {
name: string;
totalSize: number;
headerSize: number;
contentSize: number;
};
slice: FileSlice;
}, void, unknown>;
traverseBox(slice: FileSlice): boolean;
}
export {};
//# sourceMappingURL=isobmff-demuxer.d.ts.map