Add .gitignore to exclude all node packages and lock files
This commit is contained in:
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import type { SeekResolution } from '../../work-on-seek-request';
|
||||
import type { AacSeekingHints } from './seeking-hints';
|
||||
export declare const getSeekingByteForAac: ({ time, seekingHints, }: {
|
||||
time: number;
|
||||
seekingHints: AacSeekingHints;
|
||||
}) => SeekResolution;
|
||||
Generated
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getSeekingByteForAac = void 0;
|
||||
const getSeekingByteForAac = ({ time, seekingHints, }) => {
|
||||
let bestAudioSample;
|
||||
for (const hint of seekingHints.audioSampleMap) {
|
||||
if (hint.timeInSeconds > time) {
|
||||
continue;
|
||||
}
|
||||
// Everything is a keyframe in flac, so if this sample does not cover the time, it's not a good candidate.
|
||||
// Let's go to the next one. Exception: If we already saw the last sample, we use it so we find can at least
|
||||
// find the closest one.
|
||||
if (hint.timeInSeconds + hint.durationInSeconds < time &&
|
||||
!seekingHints.lastSampleObserved) {
|
||||
continue;
|
||||
}
|
||||
if (!bestAudioSample) {
|
||||
bestAudioSample = hint;
|
||||
continue;
|
||||
}
|
||||
if (bestAudioSample.timeInSeconds < hint.timeInSeconds) {
|
||||
bestAudioSample = hint;
|
||||
}
|
||||
}
|
||||
if (bestAudioSample) {
|
||||
return {
|
||||
type: 'do-seek',
|
||||
byte: bestAudioSample.offset,
|
||||
timeInSeconds: bestAudioSample.timeInSeconds,
|
||||
};
|
||||
}
|
||||
return { type: 'valid-but-must-wait' };
|
||||
};
|
||||
exports.getSeekingByteForAac = getSeekingByteForAac;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseAac: (state: ParserState) => Promise<ParseResult>;
|
||||
Generated
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseAac = void 0;
|
||||
const aac_codecprivate_1 = require("../../aac-codecprivate");
|
||||
const convert_audio_or_video_sample_1 = require("../../convert-audio-or-video-sample");
|
||||
const register_track_1 = require("../../register-track");
|
||||
const webcodecs_timescale_1 = require("../../webcodecs-timescale");
|
||||
const parseAac = async (state) => {
|
||||
const { iterator } = state;
|
||||
const startOffset = iterator.counter.getOffset();
|
||||
iterator.startReadingBits();
|
||||
const syncWord = iterator.getBits(12);
|
||||
if (syncWord !== 0xfff) {
|
||||
throw new Error('Invalid syncword: ' + syncWord);
|
||||
}
|
||||
const id = iterator.getBits(1);
|
||||
if (id !== 0) {
|
||||
throw new Error('Only supporting MPEG-4 for .aac');
|
||||
}
|
||||
const layer = iterator.getBits(2);
|
||||
if (layer !== 0) {
|
||||
throw new Error('Only supporting layer 0 for .aac');
|
||||
}
|
||||
const protectionAbsent = iterator.getBits(1); // protection absent
|
||||
const audioObjectType = iterator.getBits(2); // 1 = 'AAC-LC'
|
||||
const samplingFrequencyIndex = iterator.getBits(4);
|
||||
const sampleRate = (0, aac_codecprivate_1.getSampleRateFromSampleFrequencyIndex)(samplingFrequencyIndex);
|
||||
iterator.getBits(1); // private bit
|
||||
const channelConfiguration = iterator.getBits(3);
|
||||
const codecPrivate = (0, aac_codecprivate_1.createAacCodecPrivate)({
|
||||
audioObjectType,
|
||||
sampleRate,
|
||||
channelConfiguration,
|
||||
codecPrivate: null,
|
||||
});
|
||||
iterator.getBits(1); // originality
|
||||
iterator.getBits(1); // home
|
||||
iterator.getBits(1); // copyright bit
|
||||
iterator.getBits(1); // copy start
|
||||
const frameLength = iterator.getBits(13); // frame length
|
||||
iterator.getBits(11); // buffer fullness
|
||||
iterator.getBits(2); // number of AAC frames minus 1
|
||||
if (!protectionAbsent) {
|
||||
iterator.getBits(16); // crc
|
||||
}
|
||||
iterator.stopReadingBits();
|
||||
iterator.counter.decrement(iterator.counter.getOffset() - startOffset);
|
||||
const data = iterator.getSlice(frameLength);
|
||||
if (state.callbacks.tracks.getTracks().length === 0) {
|
||||
state.mediaSection.addMediaSection({
|
||||
start: startOffset,
|
||||
size: state.contentLength - startOffset,
|
||||
});
|
||||
await (0, register_track_1.registerAudioTrack)({
|
||||
container: 'aac',
|
||||
track: {
|
||||
codec: (0, aac_codecprivate_1.mapAudioObjectTypeToCodecString)(audioObjectType),
|
||||
codecEnum: 'aac',
|
||||
codecData: { type: 'aac-config', data: codecPrivate },
|
||||
description: codecPrivate,
|
||||
numberOfChannels: channelConfiguration,
|
||||
sampleRate,
|
||||
originalTimescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
|
||||
trackId: 0,
|
||||
type: 'audio',
|
||||
startInSeconds: 0,
|
||||
timescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
|
||||
trackMediaTimeOffsetInTrackTimescale: 0,
|
||||
},
|
||||
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
|
||||
tracks: state.callbacks.tracks,
|
||||
logLevel: state.logLevel,
|
||||
onAudioTrack: state.onAudioTrack,
|
||||
});
|
||||
state.callbacks.tracks.setIsDone(state.logLevel);
|
||||
}
|
||||
const duration = 1024 / sampleRate;
|
||||
const { index } = state.aac.addSample({ offset: startOffset, size: frameLength });
|
||||
const timestamp = (1024 / sampleRate) * index;
|
||||
state.aac.audioSamples.addSample({
|
||||
timeInSeconds: timestamp,
|
||||
offset: startOffset,
|
||||
durationInSeconds: duration,
|
||||
});
|
||||
// One ADTS frame contains 1024 samples
|
||||
const audioSample = (0, convert_audio_or_video_sample_1.convertAudioOrVideoSampleToWebCodecsTimestamps)({
|
||||
sample: {
|
||||
duration,
|
||||
type: 'key',
|
||||
data,
|
||||
offset: startOffset,
|
||||
decodingTimestamp: timestamp,
|
||||
timestamp,
|
||||
},
|
||||
timescale: 1,
|
||||
});
|
||||
await state.callbacks.onAudioSample({
|
||||
audioSample,
|
||||
trackId: 0,
|
||||
});
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
exports.parseAac = parseAac;
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import type { AacState } from '../../state/aac-state';
|
||||
import type { AudioSampleOffset } from '../../state/audio-sample-map';
|
||||
import type { SamplesObservedState } from '../../state/samples-observed/slow-duration-fps';
|
||||
export type AacSeekingHints = {
|
||||
type: 'aac-seeking-hints';
|
||||
audioSampleMap: AudioSampleOffset[];
|
||||
lastSampleObserved: boolean;
|
||||
};
|
||||
export declare const getSeekingHintsForAac: ({ aacState, samplesObserved, }: {
|
||||
aacState: AacState;
|
||||
samplesObserved: SamplesObservedState;
|
||||
}) => AacSeekingHints;
|
||||
export declare const setSeekingHintsForAac: () => void;
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setSeekingHintsForAac = exports.getSeekingHintsForAac = void 0;
|
||||
const getSeekingHintsForAac = ({ aacState, samplesObserved, }) => {
|
||||
return {
|
||||
type: 'aac-seeking-hints',
|
||||
audioSampleMap: aacState.audioSamples.getSamples(),
|
||||
lastSampleObserved: samplesObserved.getLastSampleObserved(),
|
||||
};
|
||||
};
|
||||
exports.getSeekingHintsForAac = getSeekingHintsForAac;
|
||||
// TODO: Implement this and maintain index
|
||||
const setSeekingHintsForAac = () => { };
|
||||
exports.setSeekingHintsForAac = setSeekingHintsForAac;
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export type AacStructure = {
|
||||
type: 'aac';
|
||||
boxes: never[];
|
||||
};
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { AvcProfileInfo } from './parse-avc';
|
||||
export declare const getCodecStringFromSpsAndPps: (sps: AvcProfileInfo) => string;
|
||||
skills/remotion-prompt-video/node_modules/@remotion/media-parser/dist/containers/avc/codec-string.js
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getCodecStringFromSpsAndPps = void 0;
|
||||
const getCodecStringFromSpsAndPps = (sps) => {
|
||||
return `avc1.${sps.spsData.profile.toString(16).padStart(2, '0')}${sps.spsData.compatibility.toString(16).padStart(2, '0')}${sps.spsData.level.toString(16).padStart(2, '0')}`;
|
||||
};
|
||||
exports.getCodecStringFromSpsAndPps = getCodecStringFromSpsAndPps;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
export type MediaParserMatrixCoefficients = 'rgb' | 'bt709' | 'bt470bg' | 'smpte170m' | 'bt2020-ncl';
|
||||
export declare const getMatrixCoefficientsFromIndex: (index: number) => MediaParserMatrixCoefficients | null;
|
||||
export type MediaParserTransferCharacteristics = 'bt709' | 'smpte170m' | 'iec61966-2-1' | 'linear' | 'pq' | 'hlg';
|
||||
export declare const getTransferCharacteristicsFromIndex: (index: number) => MediaParserTransferCharacteristics | null;
|
||||
export type MediaParserPrimaries = 'bt709' | 'bt470bg' | 'smpte170m' | 'bt2020' | 'smpte432' | null;
|
||||
export declare const getPrimariesFromIndex: (index: number) => MediaParserPrimaries | null;
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getPrimariesFromIndex = exports.getTransferCharacteristicsFromIndex = exports.getMatrixCoefficientsFromIndex = void 0;
|
||||
const getMatrixCoefficientsFromIndex = (index) => {
|
||||
if (index === 0) {
|
||||
return 'rgb';
|
||||
}
|
||||
if (index === 1) {
|
||||
return 'bt709';
|
||||
}
|
||||
if (index === 5) {
|
||||
return 'bt470bg';
|
||||
}
|
||||
if (index === 6) {
|
||||
return 'smpte170m';
|
||||
}
|
||||
if (index === 9) {
|
||||
return 'bt2020-ncl';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getMatrixCoefficientsFromIndex = getMatrixCoefficientsFromIndex;
|
||||
// https://w3c.github.io/webcodecs/#videotransfercharacteristics
|
||||
// But we may support more than that
|
||||
const getTransferCharacteristicsFromIndex = (index) => {
|
||||
if (index === 1) {
|
||||
return 'bt709';
|
||||
}
|
||||
if (index === 6) {
|
||||
return 'smpte170m';
|
||||
}
|
||||
if (index === 8) {
|
||||
return 'linear';
|
||||
}
|
||||
if (index === 13) {
|
||||
return 'iec61966-2-1';
|
||||
}
|
||||
if (index === 16) {
|
||||
return 'pq';
|
||||
}
|
||||
if (index === 18) {
|
||||
return 'hlg';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getTransferCharacteristicsFromIndex = getTransferCharacteristicsFromIndex;
|
||||
const getPrimariesFromIndex = (index) => {
|
||||
if (index === 1) {
|
||||
return 'bt709';
|
||||
}
|
||||
if (index === 5) {
|
||||
return 'bt470bg';
|
||||
}
|
||||
if (index === 6) {
|
||||
return 'smpte170m';
|
||||
}
|
||||
if (index === 9) {
|
||||
return 'bt2020';
|
||||
}
|
||||
if (index === 12) {
|
||||
return 'smpte432';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getPrimariesFromIndex = getPrimariesFromIndex;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { SpsAndPps } from '../../state/parser-state';
|
||||
export declare const createSpsPpsData: (avc1Profile: SpsAndPps) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createSpsPpsData = void 0;
|
||||
const combine_uint8_arrays_1 = require("../../combine-uint8-arrays");
|
||||
const truthy_1 = require("../../truthy");
|
||||
function serializeUint16(value) {
|
||||
const buffer = new ArrayBuffer(2);
|
||||
const view = new DataView(buffer);
|
||||
view.setUint16(0, value);
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
const createSpsPpsData = (avc1Profile) => {
|
||||
return (0, combine_uint8_arrays_1.combineUint8Arrays)([
|
||||
new Uint8Array([
|
||||
// https://gist.github.com/uupaa/8493378ec15f644a3d2b
|
||||
1, // version
|
||||
avc1Profile.sps.spsData.profile,
|
||||
avc1Profile.sps.spsData.compatibility,
|
||||
avc1Profile.sps.spsData.level,
|
||||
0xff,
|
||||
0xe1,
|
||||
]),
|
||||
// sequence parameter set length
|
||||
serializeUint16(avc1Profile.sps.sps.length),
|
||||
// sequence parameter set
|
||||
avc1Profile.sps.sps,
|
||||
// num of PPS
|
||||
new Uint8Array([0x01]),
|
||||
// picture parameter set length
|
||||
serializeUint16(avc1Profile.pps.pps.length),
|
||||
// PPS
|
||||
avc1Profile.pps.pps,
|
||||
// if AVCProfileIndication != 66 && AVCProfileIndication != 77 && AVCProfileIndication != 88
|
||||
[66, 77, 88].some((b) => avc1Profile.sps.spsData.profile === b)
|
||||
? null
|
||||
: /**
|
||||
* reserved 63 (0x3F)
|
||||
chroma_format 1, '4:2:0'
|
||||
reserved 31 (0x1F)
|
||||
bit_depth_luma_minus8 0
|
||||
reserved 31 (0x1F)
|
||||
bit_depth_chroma_minus8 0
|
||||
numOfSequenceParameterSetExt 0
|
||||
*/
|
||||
new Uint8Array([0xfd, 0xf8, 0xf8, 0]),
|
||||
].filter(truthy_1.truthy));
|
||||
};
|
||||
exports.createSpsPpsData = createSpsPpsData;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import type { MediaParserAdvancedColor } from '../../get-tracks';
|
||||
import type { SpsInfo } from './parse-avc';
|
||||
export declare const getDimensionsFromSps: (sps: SpsInfo) => {
|
||||
height: number;
|
||||
width: number;
|
||||
};
|
||||
export declare const getSampleAspectRatioFromSps: (sps: SpsInfo) => {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
export declare const getVideoColorFromSps: (sps: SpsInfo) => MediaParserAdvancedColor;
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getVideoColorFromSps = exports.getSampleAspectRatioFromSps = exports.getDimensionsFromSps = void 0;
|
||||
const color_1 = require("./color");
|
||||
const getDimensionsFromSps = (sps) => {
|
||||
var _a, _b, _c, _d;
|
||||
const height = sps.pic_height_in_map_units_minus1;
|
||||
const width = sps.pic_width_in_mbs_minus1;
|
||||
// https://stackoverflow.com/questions/12018535/get-the-width-height-of-the-video-from-h-264-nalu
|
||||
return {
|
||||
height: (height + 1) * 16 -
|
||||
((_a = sps.frame_crop_bottom_offset) !== null && _a !== void 0 ? _a : 0) * 2 -
|
||||
((_b = sps.frame_crop_top_offset) !== null && _b !== void 0 ? _b : 0) * 2,
|
||||
width: (width + 1) * 16 -
|
||||
((_c = sps.frame_crop_right_offset) !== null && _c !== void 0 ? _c : 0) * 2 -
|
||||
((_d = sps.frame_crop_left_offset) !== null && _d !== void 0 ? _d : 0) * 2,
|
||||
};
|
||||
};
|
||||
exports.getDimensionsFromSps = getDimensionsFromSps;
|
||||
const getSampleAspectRatioFromSps = (sps) => {
|
||||
var _a;
|
||||
if (((_a = sps.vui_parameters) === null || _a === void 0 ? void 0 : _a.sar_height) && sps.vui_parameters.sar_width) {
|
||||
return {
|
||||
width: sps.vui_parameters.sar_width,
|
||||
height: sps.vui_parameters.sar_height,
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
||||
exports.getSampleAspectRatioFromSps = getSampleAspectRatioFromSps;
|
||||
const getVideoColorFromSps = (sps) => {
|
||||
var _a, _b, _c, _d, _e;
|
||||
const matrixCoefficients = (_a = sps.vui_parameters) === null || _a === void 0 ? void 0 : _a.matrix_coefficients;
|
||||
const transferCharacteristics = (_b = sps.vui_parameters) === null || _b === void 0 ? void 0 : _b.transfer_characteristics;
|
||||
const colorPrimaries = (_c = sps.vui_parameters) === null || _c === void 0 ? void 0 : _c.colour_primaries;
|
||||
return {
|
||||
matrix: matrixCoefficients
|
||||
? (0, color_1.getMatrixCoefficientsFromIndex)(matrixCoefficients)
|
||||
: null,
|
||||
transfer: transferCharacteristics
|
||||
? (0, color_1.getTransferCharacteristicsFromIndex)(transferCharacteristics)
|
||||
: null,
|
||||
primaries: colorPrimaries ? (0, color_1.getPrimariesFromIndex)(colorPrimaries) : null,
|
||||
fullRange: (_e = (_d = sps.vui_parameters) === null || _d === void 0 ? void 0 : _d.video_full_range_flag) !== null && _e !== void 0 ? _e : null,
|
||||
};
|
||||
};
|
||||
exports.getVideoColorFromSps = getVideoColorFromSps;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { AvcInfo } from './parse-avc';
|
||||
export declare const getKeyFrameOrDeltaFromAvcInfo: (infos: AvcInfo[]) => "key" | "delta" | "bidirectional";
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getKeyFrameOrDeltaFromAvcInfo = void 0;
|
||||
const getKeyFrameOrDeltaFromAvcInfo = (infos) => {
|
||||
const keyOrDelta = infos.find((i) => i.type === 'keyframe' || i.type === 'delta-frame');
|
||||
if (!keyOrDelta) {
|
||||
throw new Error('expected avc to contain info about key or delta');
|
||||
}
|
||||
return keyOrDelta.type === 'keyframe'
|
||||
? 'key'
|
||||
: keyOrDelta.isBidirectionalFrame
|
||||
? 'bidirectional'
|
||||
: 'delta';
|
||||
};
|
||||
exports.getKeyFrameOrDeltaFromAvcInfo = getKeyFrameOrDeltaFromAvcInfo;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { SpsInfo } from './parse-avc';
|
||||
export declare const macroBlocksPerFrame: (sps: SpsInfo) => number;
|
||||
export declare const maxMacroblockBufferSize: (sps: SpsInfo) => number;
|
||||
Generated
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.maxMacroblockBufferSize = exports.macroBlocksPerFrame = void 0;
|
||||
// https://www.itu.int/rec/T-REC-H.264-202408-I
|
||||
// Table A-1 – Level limits
|
||||
const maxMacroblocksByLevel = {
|
||||
10: 396, // Level 1.0
|
||||
11: 900, // Level 1.1
|
||||
12: 2376, // Level 1.2
|
||||
13: 2376, // Level 1.3
|
||||
20: 2376, // Level 2.0
|
||||
21: 4752, // Level 2.1
|
||||
22: 8100, // Level 2.2
|
||||
30: 8100, // Level 3.0
|
||||
31: 18000, // Level 3.1
|
||||
32: 20480, // Level 3.2
|
||||
40: 32768, // Level 4.0
|
||||
41: 32768, // Level 4.1
|
||||
42: 34816, // Level 4.2
|
||||
50: 110400, // Level 5.0
|
||||
51: 184320, // Level 5.1
|
||||
52: 184320, // Level 5.2
|
||||
60: 696320, // Level 6.0
|
||||
61: 696320, // Level 6.1
|
||||
62: 696320, // Level 6.2
|
||||
};
|
||||
const macroBlocksPerFrame = (sps) => {
|
||||
const { pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 } = sps;
|
||||
return (pic_width_in_mbs_minus1 + 1) * (pic_height_in_map_units_minus1 + 1);
|
||||
};
|
||||
exports.macroBlocksPerFrame = macroBlocksPerFrame;
|
||||
const maxMacroblockBufferSize = (sps) => {
|
||||
const { level } = sps;
|
||||
const maxMacroblocks = maxMacroblocksByLevel[level];
|
||||
if (maxMacroblocks === undefined) {
|
||||
throw new Error(`Unsupported level: ${level.toString(16)}`);
|
||||
}
|
||||
return maxMacroblocks;
|
||||
};
|
||||
exports.maxMacroblockBufferSize = maxMacroblockBufferSize;
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
import type { AvcState } from '../../state/avc/avc-state';
|
||||
import type { MediaParserAvcDeltaFrameInfo, MediaParserAvcKeyframeInfo } from '../../webcodec-sample-types';
|
||||
type VuiParameters = {
|
||||
sar_width: number | null;
|
||||
sar_height: number | null;
|
||||
overscan_appropriate_flag: number | null;
|
||||
video_format: number | null;
|
||||
video_full_range_flag: boolean | null;
|
||||
colour_primaries: number | null;
|
||||
transfer_characteristics: number | null;
|
||||
matrix_coefficients: number | null;
|
||||
chroma_sample_loc_type_top_field: number | null;
|
||||
chroma_sample_loc_type_bottom_field: number | null;
|
||||
};
|
||||
export type SpsInfo = {
|
||||
profile: number;
|
||||
compatibility: number;
|
||||
level: number;
|
||||
seq_parameter_set_id: number;
|
||||
separate_colour_plane_flag: number | null;
|
||||
bit_depth_luma_minus8: number | null;
|
||||
bit_depth_chroma_minus8: number | null;
|
||||
qpprime_y_zero_transform_bypass_flag: number | null;
|
||||
log2_max_frame_num_minus4: number;
|
||||
log2_max_pic_order_cnt_lsb_minus4: number | null;
|
||||
max_num_ref_frames: number | null;
|
||||
gaps_in_frame_num_value_allowed_flag: number | null;
|
||||
pic_width_in_mbs_minus1: number;
|
||||
pic_height_in_map_units_minus1: number;
|
||||
mb_adaptive_frame_field_flag: number | null;
|
||||
direct_8x8_inference_flag: number | null;
|
||||
frame_crop_left_offset: number | null;
|
||||
frame_crop_right_offset: number | null;
|
||||
frame_crop_top_offset: number | null;
|
||||
frame_crop_bottom_offset: number | null;
|
||||
vui_parameters: VuiParameters | null;
|
||||
pic_order_cnt_type: number;
|
||||
};
|
||||
export type AvcProfileInfo = {
|
||||
spsData: SpsInfo;
|
||||
sps: Uint8Array;
|
||||
type: 'avc-profile';
|
||||
};
|
||||
export type AvcPPs = {
|
||||
type: 'avc-pps';
|
||||
pps: Uint8Array;
|
||||
};
|
||||
export type AvcInfo = AvcProfileInfo | AvcPPs | MediaParserAvcKeyframeInfo | MediaParserAvcDeltaFrameInfo;
|
||||
export declare const parseAvc: (buffer: Uint8Array, avcState: AvcState) => AvcInfo[];
|
||||
export {};
|
||||
Generated
Vendored
+332
@@ -0,0 +1,332 @@
|
||||
"use strict";
|
||||
// https://www.itu.int/rec/T-REC-H.264-202408-I/en
|
||||
// Page 455
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseAvc = void 0;
|
||||
const buffer_iterator_1 = require("../../iterator/buffer-iterator");
|
||||
const Extended_SAR = 255;
|
||||
const getPoc = (iterator, sps, avcState, isReferencePicture) => {
|
||||
const { pic_order_cnt_type, log2_max_pic_order_cnt_lsb_minus4 } = sps;
|
||||
if (pic_order_cnt_type !== 0) {
|
||||
return null;
|
||||
}
|
||||
const prevPicOrderCntLsb = avcState.getPrevPicOrderCntLsb();
|
||||
const prevPicOrderCntMsb = avcState.getPrevPicOrderCntMsb();
|
||||
if (log2_max_pic_order_cnt_lsb_minus4 === null) {
|
||||
throw new Error('log2_max_pic_order_cnt_lsb_minus4 is null');
|
||||
}
|
||||
const max_pic_order_cnt_lsb = 2 ** (log2_max_pic_order_cnt_lsb_minus4 + 4);
|
||||
const pic_order_cnt_lsb = iterator.getBits(log2_max_pic_order_cnt_lsb_minus4 + 4);
|
||||
let picOrderCntMsb;
|
||||
if (pic_order_cnt_lsb < prevPicOrderCntLsb &&
|
||||
prevPicOrderCntLsb - pic_order_cnt_lsb >= max_pic_order_cnt_lsb / 2) {
|
||||
picOrderCntMsb = prevPicOrderCntMsb + max_pic_order_cnt_lsb;
|
||||
}
|
||||
else if (pic_order_cnt_lsb > prevPicOrderCntLsb &&
|
||||
pic_order_cnt_lsb - prevPicOrderCntLsb > max_pic_order_cnt_lsb / 2) {
|
||||
picOrderCntMsb = prevPicOrderCntMsb - max_pic_order_cnt_lsb;
|
||||
}
|
||||
else {
|
||||
picOrderCntMsb = prevPicOrderCntMsb;
|
||||
}
|
||||
const poc = picOrderCntMsb + pic_order_cnt_lsb;
|
||||
if (isReferencePicture) {
|
||||
avcState.setPrevPicOrderCntLsb(pic_order_cnt_lsb);
|
||||
avcState.setPrevPicOrderCntMsb(picOrderCntMsb);
|
||||
}
|
||||
return poc;
|
||||
};
|
||||
const readVuiParameters = (iterator) => {
|
||||
let sar_width = null;
|
||||
let sar_height = null;
|
||||
let overscan_appropriate_flag = null;
|
||||
let video_format = null;
|
||||
let video_full_range_flag = null;
|
||||
let colour_primaries = null;
|
||||
let transfer_characteristics = null;
|
||||
let matrix_coefficients = null;
|
||||
let chroma_sample_loc_type_top_field = null;
|
||||
let chroma_sample_loc_type_bottom_field = null;
|
||||
const aspect_ratio_info_present_flag = iterator.getBits(1);
|
||||
if (aspect_ratio_info_present_flag) {
|
||||
const aspect_ratio_idc = iterator.getBits(8);
|
||||
if (aspect_ratio_idc === Extended_SAR) {
|
||||
sar_width = iterator.getBits(16);
|
||||
sar_height = iterator.getBits(16);
|
||||
}
|
||||
}
|
||||
const overscan_info_present_flag = iterator.getBits(1);
|
||||
if (overscan_info_present_flag) {
|
||||
overscan_appropriate_flag = iterator.getBits(1);
|
||||
}
|
||||
const video_signal_type_present_flag = iterator.getBits(1);
|
||||
if (video_signal_type_present_flag) {
|
||||
video_format = iterator.getBits(3);
|
||||
video_full_range_flag = Boolean(iterator.getBits(1));
|
||||
const colour_description_present_flag = iterator.getBits(1);
|
||||
if (colour_description_present_flag) {
|
||||
colour_primaries = iterator.getBits(8);
|
||||
transfer_characteristics = iterator.getBits(8);
|
||||
matrix_coefficients = iterator.getBits(8);
|
||||
}
|
||||
}
|
||||
const chroma_loc_info_present_flag = iterator.getBits(1);
|
||||
if (chroma_loc_info_present_flag) {
|
||||
chroma_sample_loc_type_top_field = iterator.readExpGolomb();
|
||||
chroma_sample_loc_type_bottom_field = iterator.readExpGolomb();
|
||||
}
|
||||
return {
|
||||
sar_width,
|
||||
sar_height,
|
||||
overscan_appropriate_flag,
|
||||
chroma_sample_loc_type_bottom_field,
|
||||
chroma_sample_loc_type_top_field,
|
||||
colour_primaries,
|
||||
matrix_coefficients,
|
||||
transfer_characteristics,
|
||||
video_format,
|
||||
video_full_range_flag,
|
||||
};
|
||||
};
|
||||
const readSps = (iterator) => {
|
||||
const profile = iterator.getUint8();
|
||||
const compatibility = iterator.getUint8();
|
||||
const level = iterator.getUint8();
|
||||
iterator.startReadingBits();
|
||||
const seq_parameter_set_id = iterator.readExpGolomb();
|
||||
let separate_colour_plane_flag = null;
|
||||
let bit_depth_luma_minus8 = null;
|
||||
let bit_depth_chroma_minus8 = null;
|
||||
let qpprime_y_zero_transform_bypass_flag = null;
|
||||
let log2_max_frame_num_minus4 = null;
|
||||
let log2_max_pic_order_cnt_lsb_minus4 = null;
|
||||
let max_num_ref_frames = null;
|
||||
let gaps_in_frame_num_value_allowed_flag = null;
|
||||
let mb_adaptive_frame_field_flag = null;
|
||||
let direct_8x8_inference_flag = null;
|
||||
let frame_crop_left_offset = null;
|
||||
let frame_crop_right_offset = null;
|
||||
let frame_crop_top_offset = null;
|
||||
let frame_crop_bottom_offset = null;
|
||||
let vui_parameters = null;
|
||||
// Page 71
|
||||
if (profile === 100 ||
|
||||
profile === 110 ||
|
||||
profile === 122 ||
|
||||
profile === 244 ||
|
||||
profile === 44 ||
|
||||
profile === 83 ||
|
||||
profile === 86 ||
|
||||
profile === 118 ||
|
||||
profile === 128 ||
|
||||
profile === 138 ||
|
||||
profile === 139 ||
|
||||
profile === 134 ||
|
||||
profile === 135) {
|
||||
const chromaFormat = iterator.readExpGolomb();
|
||||
if (chromaFormat === 3) {
|
||||
separate_colour_plane_flag = iterator.getBits(1);
|
||||
}
|
||||
bit_depth_luma_minus8 = iterator.readExpGolomb();
|
||||
bit_depth_chroma_minus8 = iterator.readExpGolomb();
|
||||
qpprime_y_zero_transform_bypass_flag = iterator.getBits(1);
|
||||
const seq_scaling_matrix_present_flag = iterator.getBits(1);
|
||||
const seq_scaling_list_present_flag = [];
|
||||
if (seq_scaling_matrix_present_flag) {
|
||||
for (let i = 0; i < (chromaFormat !== 3 ? 8 : 12); i++) {
|
||||
seq_scaling_list_present_flag[i] = iterator.getBits(1);
|
||||
if (seq_scaling_list_present_flag[i]) {
|
||||
if (i < 6) {
|
||||
// scaling_list not implemented
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
else {
|
||||
// scaling_list not implemented
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log2_max_frame_num_minus4 = iterator.readExpGolomb();
|
||||
const pic_order_cnt_type = iterator.readExpGolomb();
|
||||
if (pic_order_cnt_type === 0) {
|
||||
log2_max_pic_order_cnt_lsb_minus4 = iterator.readExpGolomb();
|
||||
}
|
||||
else if (pic_order_cnt_type === 1) {
|
||||
throw new Error('pic_order_cnt_type = 1 not implemented');
|
||||
}
|
||||
max_num_ref_frames = iterator.readExpGolomb();
|
||||
gaps_in_frame_num_value_allowed_flag = iterator.getBits(1);
|
||||
const pic_width_in_mbs_minus1 = iterator.readExpGolomb();
|
||||
const pic_height_in_map_units_minus1 = iterator.readExpGolomb();
|
||||
const frame_mbs_only_flag = iterator.getBits(1);
|
||||
if (!frame_mbs_only_flag) {
|
||||
mb_adaptive_frame_field_flag = iterator.getBits(1);
|
||||
}
|
||||
direct_8x8_inference_flag = iterator.getBits(1);
|
||||
const frame_cropping_flag = iterator.getBits(1);
|
||||
if (frame_cropping_flag) {
|
||||
frame_crop_left_offset = iterator.readExpGolomb();
|
||||
frame_crop_right_offset = iterator.readExpGolomb();
|
||||
frame_crop_top_offset = iterator.readExpGolomb();
|
||||
frame_crop_bottom_offset = iterator.readExpGolomb();
|
||||
}
|
||||
const vui_parameters_present_flag = iterator.getBits(1);
|
||||
if (vui_parameters_present_flag) {
|
||||
vui_parameters = readVuiParameters(iterator);
|
||||
}
|
||||
iterator.stopReadingBits();
|
||||
return {
|
||||
profile,
|
||||
compatibility,
|
||||
level,
|
||||
bit_depth_chroma_minus8,
|
||||
bit_depth_luma_minus8,
|
||||
gaps_in_frame_num_value_allowed_flag,
|
||||
log2_max_frame_num_minus4,
|
||||
log2_max_pic_order_cnt_lsb_minus4,
|
||||
max_num_ref_frames,
|
||||
pic_height_in_map_units_minus1,
|
||||
pic_width_in_mbs_minus1,
|
||||
qpprime_y_zero_transform_bypass_flag,
|
||||
separate_colour_plane_flag,
|
||||
seq_parameter_set_id,
|
||||
direct_8x8_inference_flag,
|
||||
frame_crop_bottom_offset,
|
||||
frame_crop_left_offset,
|
||||
frame_crop_right_offset,
|
||||
frame_crop_top_offset,
|
||||
mb_adaptive_frame_field_flag,
|
||||
vui_parameters,
|
||||
pic_order_cnt_type,
|
||||
};
|
||||
};
|
||||
const findEnd = (buffer) => {
|
||||
let zeroesInARow = 0;
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const val = buffer[i];
|
||||
if (val === 0) {
|
||||
zeroesInARow++;
|
||||
continue;
|
||||
}
|
||||
if (zeroesInARow >= 2 && val === 1) {
|
||||
return i - zeroesInARow;
|
||||
}
|
||||
zeroesInARow = 0;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const inspect = (buffer, avcState) => {
|
||||
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
|
||||
initialData: buffer,
|
||||
maxBytes: buffer.byteLength,
|
||||
logLevel: 'error',
|
||||
});
|
||||
iterator.startReadingBits();
|
||||
iterator.getBits(1); // forbidden_zero_bit
|
||||
const nal_ref_idc = iterator.getBits(2); // nal_ref_idc
|
||||
const isReferencePicture = nal_ref_idc !== 0;
|
||||
const type = iterator.getBits(5); // nal_unit_type
|
||||
if (type === 7) {
|
||||
iterator.stopReadingBits();
|
||||
const end = findEnd(buffer);
|
||||
const data = readSps(iterator);
|
||||
const sps = buffer.slice(0, end === null ? Infinity : end);
|
||||
avcState.setSps(data);
|
||||
if (isReferencePicture) {
|
||||
avcState.setPrevPicOrderCntLsb(0);
|
||||
avcState.setPrevPicOrderCntMsb(0);
|
||||
}
|
||||
return {
|
||||
spsData: data,
|
||||
sps,
|
||||
type: 'avc-profile',
|
||||
};
|
||||
}
|
||||
if (type === 5) {
|
||||
avcState.setPrevPicOrderCntLsb(0);
|
||||
avcState.setPrevPicOrderCntMsb(0);
|
||||
iterator.readExpGolomb(); // ignore first_mb_in_slice
|
||||
iterator.readExpGolomb(); // slice_type
|
||||
iterator.readExpGolomb(); // pic_parameter_set_id
|
||||
const sps = avcState.getSps();
|
||||
if (!sps) {
|
||||
throw new Error('SPS not found');
|
||||
}
|
||||
const numberOfBitsForFrameNum = sps.log2_max_frame_num_minus4 + 4;
|
||||
iterator.getBits(numberOfBitsForFrameNum); // frame_num
|
||||
iterator.readExpGolomb(); // idr_pic_id
|
||||
const { pic_order_cnt_type } = sps;
|
||||
let poc = null;
|
||||
if (pic_order_cnt_type === 0) {
|
||||
poc = getPoc(iterator, sps, avcState, isReferencePicture);
|
||||
}
|
||||
iterator.stopReadingBits();
|
||||
return {
|
||||
type: 'keyframe',
|
||||
poc,
|
||||
};
|
||||
}
|
||||
if (type === 8) {
|
||||
iterator.stopReadingBits();
|
||||
const end = findEnd(buffer);
|
||||
const pps = buffer.slice(0, end === null ? Infinity : end);
|
||||
return {
|
||||
type: 'avc-pps',
|
||||
pps,
|
||||
};
|
||||
}
|
||||
if (type === 1) {
|
||||
iterator.readExpGolomb(); // ignore first_mb_in_slice
|
||||
const slice_type = iterator.readExpGolomb();
|
||||
const isBidirectionalFrame = slice_type === 6;
|
||||
iterator.readExpGolomb(); // pic_parameter_set_id
|
||||
const sps = avcState.getSps();
|
||||
if (!sps) {
|
||||
throw new Error('SPS not found');
|
||||
}
|
||||
const numberOfBitsForFrameNum = sps.log2_max_frame_num_minus4 + 4;
|
||||
iterator.getBits(numberOfBitsForFrameNum); // frame_num
|
||||
const { pic_order_cnt_type } = sps;
|
||||
let poc = null;
|
||||
if (pic_order_cnt_type === 0) {
|
||||
poc = getPoc(iterator, sps, avcState, isReferencePicture);
|
||||
}
|
||||
iterator.stopReadingBits();
|
||||
return {
|
||||
type: 'delta-frame',
|
||||
isBidirectionalFrame,
|
||||
poc,
|
||||
};
|
||||
}
|
||||
iterator.destroy();
|
||||
return null;
|
||||
};
|
||||
// https://stackoverflow.com/questions/24884827/possible-locations-for-sequence-picture-parameter-sets-for-h-264-stream
|
||||
const parseAvc = (buffer, avcState) => {
|
||||
let zeroesInARow = 0;
|
||||
const infos = [];
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const val = buffer[i];
|
||||
if (val === 0) {
|
||||
zeroesInARow++;
|
||||
continue;
|
||||
}
|
||||
if (zeroesInARow >= 2 && val === 1) {
|
||||
zeroesInARow = 0;
|
||||
const info = inspect(buffer.slice(i + 1, i + 100), avcState);
|
||||
if (info) {
|
||||
infos.push(info);
|
||||
if (info.type === 'keyframe' || info.type === 'delta-frame') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val !== 1) {
|
||||
zeroesInARow = 0;
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
};
|
||||
exports.parseAvc = parseAvc;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { SpsAndPps } from '../../state/parser-state';
|
||||
import type { AvcInfo } from './parse-avc';
|
||||
export declare const getSpsAndPps: (infos: AvcInfo[]) => SpsAndPps;
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getSpsAndPps = void 0;
|
||||
const getSpsAndPps = (infos) => {
|
||||
const avcProfile = infos.find((i) => i.type === 'avc-profile');
|
||||
const ppsProfile = infos.find((i) => i.type === 'avc-pps');
|
||||
if (!avcProfile || !ppsProfile) {
|
||||
throw new Error('Expected avcProfile and ppsProfile');
|
||||
}
|
||||
return { pps: ppsProfile, sps: avcProfile };
|
||||
};
|
||||
exports.getSpsAndPps = getSpsAndPps;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
export declare const getBlockSize: (iterator: BufferIterator) => number | "uncommon-u16" | "uncommon-u8" | null;
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getBlockSize = void 0;
|
||||
const getBlockSize = (iterator) => {
|
||||
const bits = iterator.getBits(4);
|
||||
if (bits === 0b0000) {
|
||||
// Probably we are in the wrong spot overall, and just landed on a spot that incidentially hit the syncword.
|
||||
// Don't throw an error, in the parent function just keep reading.
|
||||
// Internal message with repro: https://discord.com/channels/@me/1314232261008162876/1410312296709881988
|
||||
return null;
|
||||
}
|
||||
if (bits === 0b0001) {
|
||||
return 192;
|
||||
}
|
||||
if (bits >= 0b0010 && bits <= 0b0101) {
|
||||
return 144 * 2 ** bits;
|
||||
}
|
||||
if (bits === 0b0110) {
|
||||
return 'uncommon-u8';
|
||||
}
|
||||
if (bits === 0b0111) {
|
||||
return 'uncommon-u16';
|
||||
}
|
||||
if (bits >= 0b1000 && bits <= 0b1111) {
|
||||
return 2 ** bits;
|
||||
}
|
||||
throw new Error('Invalid block size');
|
||||
};
|
||||
exports.getBlockSize = getBlockSize;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
export declare const getChannelCount: (iterator: BufferIterator) => 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getChannelCount = void 0;
|
||||
// https://www.rfc-editor.org/rfc/rfc9639.html#name-channels-bits
|
||||
const getChannelCount = (iterator) => {
|
||||
const bits = iterator.getBits(4);
|
||||
if (bits === 0b0000) {
|
||||
return 1;
|
||||
}
|
||||
if (bits === 0b0001) {
|
||||
return 2;
|
||||
}
|
||||
if (bits === 0b0010) {
|
||||
return 3;
|
||||
}
|
||||
if (bits === 0b0011) {
|
||||
return 4;
|
||||
}
|
||||
if (bits === 0b0100) {
|
||||
return 5;
|
||||
}
|
||||
if (bits === 0b0101) {
|
||||
return 6;
|
||||
}
|
||||
if (bits === 0b0110) {
|
||||
return 7;
|
||||
}
|
||||
if (bits === 0b0111) {
|
||||
return 8;
|
||||
}
|
||||
if (bits === 0b1000 || bits === 0b1001 || bits === 0b1010) {
|
||||
return 2;
|
||||
}
|
||||
// 0b1011..0b1111 are reserved per RFC 9639 §9.1.3 (Channels Bits).
|
||||
// Some encoders/files in the wild may nonetheless use these values.
|
||||
// Be lenient and treat them as stereo (2 channels) to keep parsing robust.
|
||||
if (bits >= 0b1011 && bits <= 0b1111) {
|
||||
return 2;
|
||||
}
|
||||
throw new Error(`Invalid channel count: ${bits.toString(2)}`);
|
||||
};
|
||||
exports.getChannelCount = getChannelCount;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const getDurationFromFlac: (parserState: ParserState) => number;
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getDurationFromFlac = void 0;
|
||||
const getDurationFromFlac = (parserState) => {
|
||||
const structure = parserState.structure.getFlacStructure();
|
||||
const streaminfo = structure.boxes.find((b) => b.type === 'flac-streaminfo');
|
||||
if (!streaminfo) {
|
||||
throw new Error('Streaminfo not found');
|
||||
}
|
||||
return streaminfo.totalSamples / streaminfo.sampleRate;
|
||||
};
|
||||
exports.getDurationFromFlac = getDurationFromFlac;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { FlacStructure } from './types';
|
||||
export declare const getMetadataFromFlac: (structure: FlacStructure) => import("../..").MediaParserMetadataEntry[] | null;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getMetadataFromFlac = void 0;
|
||||
const getMetadataFromFlac = (structure) => {
|
||||
const box = structure.boxes.find((b) => b.type === 'flac-vorbis-comment');
|
||||
if (!box) {
|
||||
return null;
|
||||
}
|
||||
return box.fields;
|
||||
};
|
||||
exports.getMetadataFromFlac = getMetadataFromFlac;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const getSampleRate: (iterator: BufferIterator, state: ParserState) => number | "uncommon-u8" | "uncommon-u16" | "uncommon-u16-10";
|
||||
Generated
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getSampleRate = void 0;
|
||||
// https://www.rfc-editor.org/rfc/rfc9639.html#name-sample-rate-bits
|
||||
const getSampleRate = (iterator, state) => {
|
||||
var _a, _b;
|
||||
const mode = iterator.getBits(4);
|
||||
if (mode === 0b0000 || mode === 0b1111) {
|
||||
const structure = state.structure.getFlacStructure();
|
||||
const sampleRate = (_b = (_a = structure.boxes.find((box) => box.type === 'flac-streaminfo')) === null || _a === void 0 ? void 0 : _a.sampleRate) !== null && _b !== void 0 ? _b : null;
|
||||
if (sampleRate === null) {
|
||||
throw new Error('Sample rate not found');
|
||||
}
|
||||
return sampleRate;
|
||||
}
|
||||
if (mode === 0b0001) {
|
||||
return 88200;
|
||||
}
|
||||
if (mode === 0b0010) {
|
||||
return 176400;
|
||||
}
|
||||
if (mode === 0b0011) {
|
||||
return 192000;
|
||||
}
|
||||
if (mode === 0b0100) {
|
||||
return 8000;
|
||||
}
|
||||
if (mode === 0b0101) {
|
||||
return 16000;
|
||||
}
|
||||
if (mode === 0b0110) {
|
||||
return 22050;
|
||||
}
|
||||
if (mode === 0b0111) {
|
||||
return 24000;
|
||||
}
|
||||
if (mode === 0b1000) {
|
||||
return 32000;
|
||||
}
|
||||
if (mode === 0b1001) {
|
||||
return 44100;
|
||||
}
|
||||
if (mode === 0b1010) {
|
||||
return 48000;
|
||||
}
|
||||
if (mode === 0b1011) {
|
||||
return 96000;
|
||||
}
|
||||
if (mode === 0b1100) {
|
||||
return 'uncommon-u8';
|
||||
}
|
||||
if (mode === 0b1101) {
|
||||
return 'uncommon-u16';
|
||||
}
|
||||
if (mode === 0b1110) {
|
||||
return 'uncommon-u16-10';
|
||||
}
|
||||
throw new Error(`Invalid sample rate mode: ${mode.toString(2)}`);
|
||||
};
|
||||
exports.getSampleRate = getSampleRate;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import type { AudioSampleOffset } from '../../state/audio-sample-map';
|
||||
import type { FlacSeekingHints } from './seeking-hints';
|
||||
export declare const getSeekingByteForFlac: ({ time, seekingHints, }: {
|
||||
time: number;
|
||||
seekingHints: FlacSeekingHints;
|
||||
}) => AudioSampleOffset | null;
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getSeekingByteForFlac = void 0;
|
||||
const getSeekingByteForFlac = ({ time, seekingHints, }) => {
|
||||
let bestAudioSample;
|
||||
for (const hint of seekingHints.audioSampleMap) {
|
||||
if (hint.timeInSeconds > time) {
|
||||
continue;
|
||||
}
|
||||
// Everything is a keyframe in flac, so if this sample does not cover the time, it's not a good candidate.
|
||||
// Let's go to the next one. Exception: If we already saw the last sample, we use it so we find can at least
|
||||
// find the closest one.
|
||||
if (hint.timeInSeconds + hint.durationInSeconds < time &&
|
||||
!seekingHints.lastSampleObserved) {
|
||||
continue;
|
||||
}
|
||||
if (!bestAudioSample) {
|
||||
bestAudioSample = hint;
|
||||
continue;
|
||||
}
|
||||
if (bestAudioSample.timeInSeconds < hint.timeInSeconds) {
|
||||
bestAudioSample = hint;
|
||||
}
|
||||
}
|
||||
if (bestAudioSample) {
|
||||
return bestAudioSample;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getSeekingByteForFlac = getSeekingByteForFlac;
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
import { type BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseFrameHeader: ({ iterator, state, }: {
|
||||
iterator: BufferIterator;
|
||||
state: ParserState;
|
||||
}) => {
|
||||
num: number;
|
||||
blockSize: number;
|
||||
sampleRate: number;
|
||||
} | null;
|
||||
export declare const parseFlacFrame: ({ state, iterator, }: {
|
||||
state: ParserState;
|
||||
iterator: BufferIterator;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+185
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseFlacFrame = exports.parseFrameHeader = void 0;
|
||||
const convert_audio_or_video_sample_1 = require("../../convert-audio-or-video-sample");
|
||||
const buffer_iterator_1 = require("../../iterator/buffer-iterator");
|
||||
const get_block_size_1 = require("./get-block-size");
|
||||
const get_channel_count_1 = require("./get-channel-count");
|
||||
const get_sample_rate_1 = require("./get-sample-rate");
|
||||
// https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1.1
|
||||
function calculateCRC8(data) {
|
||||
const polynomial = 0x07; // x^8 + x^2 + x^1 + x^0
|
||||
let crc = 0x00; // Initialize CRC to 0
|
||||
for (const byte of data) {
|
||||
crc ^= byte; // XOR byte into least significant byte of crc
|
||||
for (let i = 0; i < 8; i++) {
|
||||
// For each bit in the byte
|
||||
if ((crc & 0x80) !== 0) {
|
||||
// If the leftmost bit (MSB) is set
|
||||
crc = (crc << 1) ^ polynomial; // Shift left and XOR with polynomial
|
||||
}
|
||||
else {
|
||||
crc <<= 1; // Just shift left
|
||||
}
|
||||
crc &= 0xff; // Ensure CRC remains 8-bit
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
const parseFrameHeader = ({ iterator, state, }) => {
|
||||
if (iterator.bytesRemaining() < 10) {
|
||||
return null;
|
||||
}
|
||||
const startOffset = iterator.counter.getOffset();
|
||||
iterator.discard(2); // sync code
|
||||
iterator.startReadingBits();
|
||||
const blockSizeBits = (0, get_block_size_1.getBlockSize)(iterator);
|
||||
if (blockSizeBits === null) {
|
||||
return null;
|
||||
}
|
||||
const sampleRateBits = (0, get_sample_rate_1.getSampleRate)(iterator, state);
|
||||
(0, get_channel_count_1.getChannelCount)(iterator); // channel count
|
||||
iterator.getBits(3); // bit depth
|
||||
iterator.getBits(1);
|
||||
const num = iterator.getFlacCodecNumber();
|
||||
const blockSize = blockSizeBits === 'uncommon-u16'
|
||||
? iterator.getBits(16) + 1
|
||||
: blockSizeBits === 'uncommon-u8'
|
||||
? iterator.getBits(8) + 1
|
||||
: blockSizeBits;
|
||||
const sampleRate = sampleRateBits === 'uncommon-u16'
|
||||
? iterator.getBits(16)
|
||||
: sampleRateBits === 'uncommon-u16-10'
|
||||
? iterator.getBits(16) * 10
|
||||
: sampleRateBits === 'uncommon-u8'
|
||||
? iterator.getBits(8)
|
||||
: sampleRateBits;
|
||||
iterator.stopReadingBits();
|
||||
const size = iterator.counter.getOffset() - startOffset;
|
||||
const crc = iterator.getUint8();
|
||||
iterator.counter.decrement(size + 1);
|
||||
const crcCalculated = calculateCRC8(iterator.getSlice(size));
|
||||
iterator.counter.decrement(size);
|
||||
if (crcCalculated !== crc) {
|
||||
return null;
|
||||
}
|
||||
return { num, blockSize, sampleRate };
|
||||
};
|
||||
exports.parseFrameHeader = parseFrameHeader;
|
||||
const emitSample = async ({ state, data, offset, }) => {
|
||||
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)({
|
||||
initialData: data,
|
||||
maxBytes: data.length,
|
||||
logLevel: 'error',
|
||||
});
|
||||
const parsed = (0, exports.parseFrameHeader)({ iterator, state });
|
||||
if (!parsed) {
|
||||
throw new Error('Invalid CRC');
|
||||
}
|
||||
const { blockSize, num, sampleRate } = parsed;
|
||||
const duration = blockSize / sampleRate;
|
||||
const structure = state.structure.getFlacStructure();
|
||||
const streamInfo = structure.boxes.find((box) => box.type === 'flac-streaminfo');
|
||||
if (!streamInfo) {
|
||||
throw new Error('Stream info not found');
|
||||
}
|
||||
if (streamInfo.minimumBlockSize !== streamInfo.maximumBlockSize) {
|
||||
throw new Error('Cannot determine timestamp');
|
||||
}
|
||||
const timestamp = (num * streamInfo.maximumBlockSize) / streamInfo.sampleRate;
|
||||
state.flac.audioSamples.addSample({
|
||||
timeInSeconds: timestamp,
|
||||
offset,
|
||||
durationInSeconds: duration,
|
||||
});
|
||||
const audioSample = (0, convert_audio_or_video_sample_1.convertAudioOrVideoSampleToWebCodecsTimestamps)({
|
||||
sample: {
|
||||
data,
|
||||
duration,
|
||||
decodingTimestamp: timestamp,
|
||||
timestamp,
|
||||
type: 'key',
|
||||
offset,
|
||||
},
|
||||
timescale: 1,
|
||||
});
|
||||
await state.callbacks.onAudioSample({
|
||||
audioSample,
|
||||
trackId: 0,
|
||||
});
|
||||
iterator.destroy();
|
||||
};
|
||||
const parseFlacFrame = async ({ state, iterator, }) => {
|
||||
var _a, _b;
|
||||
const blockingBit = state.flac.getBlockingBitStrategy();
|
||||
const offset = iterator.counter.getOffset();
|
||||
const { returnToCheckpoint } = iterator.startCheckpoint();
|
||||
iterator.startReadingBits();
|
||||
if (blockingBit === undefined) {
|
||||
const bits = iterator.getBits(15);
|
||||
if (bits !== 0b111111111111100) {
|
||||
throw new Error('Invalid sync code');
|
||||
}
|
||||
state.flac.setBlockingBitStrategy(iterator.getBits(1));
|
||||
}
|
||||
else if (blockingBit === 1) {
|
||||
const bits = iterator.getBits(16);
|
||||
if (bits !== 0b1111111111111001) {
|
||||
throw new Error('Blocking bit changed, it should not');
|
||||
}
|
||||
}
|
||||
else if (blockingBit === 0) {
|
||||
const bits = iterator.getBits(16);
|
||||
if (bits !== 0b1111111111111000) {
|
||||
throw new Error('Blocking bit changed, it should not');
|
||||
}
|
||||
}
|
||||
const setBlockingBit = state.flac.getBlockingBitStrategy();
|
||||
if (setBlockingBit === undefined) {
|
||||
throw new Error('Blocking bit should be set');
|
||||
}
|
||||
iterator.stopReadingBits();
|
||||
const structure = state.structure.getFlacStructure();
|
||||
const minimumFrameSize = (_b = (_a = structure.boxes.find((b) => b.type === 'flac-streaminfo')) === null || _a === void 0 ? void 0 : _a.minimumFrameSize) !== null && _b !== void 0 ? _b : null;
|
||||
if (minimumFrameSize === null) {
|
||||
throw new Error('Expected flac-streaminfo');
|
||||
}
|
||||
if (minimumFrameSize !== 0) {
|
||||
iterator.getSlice(minimumFrameSize - 2);
|
||||
}
|
||||
while (true) {
|
||||
if (iterator.counter.getOffset() === state.contentLength) {
|
||||
const size = iterator.counter.getOffset() - offset;
|
||||
returnToCheckpoint();
|
||||
const slice = iterator.getSlice(size);
|
||||
await emitSample({ state, data: slice, offset });
|
||||
break;
|
||||
}
|
||||
if (iterator.bytesRemaining() === 0) {
|
||||
returnToCheckpoint();
|
||||
break;
|
||||
}
|
||||
const nextByte = iterator.getUint8();
|
||||
if (nextByte === 0xff) {
|
||||
const nextBits = iterator.getUint8();
|
||||
const expected = setBlockingBit === 1 ? 249 : 248;
|
||||
if (nextBits !== expected) {
|
||||
iterator.counter.decrement(1);
|
||||
continue;
|
||||
}
|
||||
iterator.counter.decrement(2);
|
||||
const nextIsLegit = (0, exports.parseFrameHeader)({ iterator, state });
|
||||
if (!nextIsLegit) {
|
||||
iterator.discard(1);
|
||||
continue;
|
||||
}
|
||||
const size = iterator.counter.getOffset() - offset;
|
||||
returnToCheckpoint();
|
||||
const data = iterator.getSlice(size);
|
||||
await emitSample({ state, data, offset });
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.parseFlacFrame = parseFlacFrame;
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseFlac: ({ iterator, state, }: {
|
||||
iterator: BufferIterator;
|
||||
state: ParserState;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseFlac = void 0;
|
||||
const skip_1 = require("../../skip");
|
||||
const may_skip_video_data_1 = require("../../state/may-skip-video-data");
|
||||
const parse_flac_frame_1 = require("./parse-flac-frame");
|
||||
const parse_header_1 = require("./parse-header");
|
||||
const parse_meta_1 = require("./parse-meta");
|
||||
const parseFlac = ({ iterator, state, }) => {
|
||||
const mediaSectionState = state.mediaSection.isCurrentByteInMediaSection(iterator);
|
||||
if (mediaSectionState === 'in-section') {
|
||||
if ((0, may_skip_video_data_1.maySkipVideoData)({ state })) {
|
||||
return Promise.resolve((0, skip_1.makeSkip)(state.contentLength));
|
||||
}
|
||||
return (0, parse_flac_frame_1.parseFlacFrame)({ state, iterator });
|
||||
}
|
||||
const bytes = iterator.getByteString(4, true);
|
||||
if (bytes === 'fLaC') {
|
||||
return (0, parse_header_1.parseFlacHeader)({ state, iterator });
|
||||
}
|
||||
iterator.counter.decrement(4);
|
||||
// https://www.rfc-editor.org/rfc/rfc9639.html#name-streaminfo
|
||||
// section 8.1
|
||||
return (0, parse_meta_1.parseMetaBlock)({
|
||||
iterator,
|
||||
state,
|
||||
});
|
||||
};
|
||||
exports.parseFlac = parseFlac;
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseFlacHeader: ({ state, }: {
|
||||
state: ParserState;
|
||||
iterator: BufferIterator;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseFlacHeader = void 0;
|
||||
const parseFlacHeader = ({ state, }) => {
|
||||
state.structure.getFlacStructure().boxes.push({
|
||||
type: 'flac-header',
|
||||
});
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
exports.parseFlacHeader = parseFlacHeader;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseMetaBlock: ({ iterator, state, }: {
|
||||
iterator: BufferIterator;
|
||||
state: ParserState;
|
||||
}) => Promise<import("../../parse-result").ParseResult>;
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseMetaBlock = void 0;
|
||||
const parse_metadata_1 = require("./parse-metadata");
|
||||
const parse_streaminfo_1 = require("./parse-streaminfo");
|
||||
const parse_unknown_block_1 = require("./parse-unknown-block");
|
||||
const flacTypes = {
|
||||
streaminfo: 0,
|
||||
vorbisComment: 4,
|
||||
};
|
||||
const parseMetaBlock = ({ iterator, state, }) => {
|
||||
iterator.startReadingBits();
|
||||
const isLastMetadata = iterator.getBits(1);
|
||||
const metaBlockType = iterator.getBits(7);
|
||||
iterator.stopReadingBits();
|
||||
const size = iterator.getUint24();
|
||||
if (isLastMetadata) {
|
||||
state.mediaSection.addMediaSection({
|
||||
start: iterator.counter.getOffset() + size,
|
||||
size: state.contentLength - iterator.counter.getOffset() - size,
|
||||
});
|
||||
}
|
||||
if (metaBlockType === flacTypes.streaminfo) {
|
||||
return (0, parse_streaminfo_1.parseStreamInfo)({ iterator, state });
|
||||
}
|
||||
if (metaBlockType === flacTypes.vorbisComment) {
|
||||
return (0, parse_metadata_1.parseVorbisComment)({ iterator, state, size });
|
||||
}
|
||||
return (0, parse_unknown_block_1.parseFlacUnkownBlock)({ iterator, state, size });
|
||||
};
|
||||
exports.parseMetaBlock = parseMetaBlock;
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseVorbisComment: ({ state, iterator, size, }: {
|
||||
state: ParserState;
|
||||
iterator: BufferIterator;
|
||||
size: number;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseVorbisComment = void 0;
|
||||
const parseVorbisComment = ({ state, iterator, size, }) => {
|
||||
const { expectNoMoreBytes } = iterator.startBox(size);
|
||||
const box = {
|
||||
type: 'flac-vorbis-comment',
|
||||
fields: [],
|
||||
};
|
||||
const vendorLength = iterator.getUint32Le();
|
||||
const vendorString = iterator.getByteString(vendorLength, true);
|
||||
const numberOfFields = iterator.getUint32Le();
|
||||
box.fields.push({ key: 'vendor', value: vendorString, trackId: null });
|
||||
for (let i = 0; i < numberOfFields; i++) {
|
||||
const fieldLength = iterator.getUint32Le();
|
||||
const field = iterator.getByteString(fieldLength, true);
|
||||
const [key, value] = field.split('=');
|
||||
box.fields.push({ key: key.toLowerCase(), value, trackId: null });
|
||||
}
|
||||
state.structure.getFlacStructure().boxes.push(box);
|
||||
expectNoMoreBytes();
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
exports.parseVorbisComment = parseVorbisComment;
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseStreamInfo: ({ iterator, state, }: {
|
||||
iterator: BufferIterator;
|
||||
state: ParserState;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseStreamInfo = void 0;
|
||||
const register_track_1 = require("../../register-track");
|
||||
const webcodecs_timescale_1 = require("../../webcodecs-timescale");
|
||||
const parseStreamInfo = async ({ iterator, state, }) => {
|
||||
const counter = iterator.counter.getOffset();
|
||||
const minimumBlockSize = iterator.getUint16();
|
||||
const maximumBlockSize = iterator.getUint16();
|
||||
const minimumFrameSize = iterator.getUint24();
|
||||
const maximumFrameSize = iterator.getUint24();
|
||||
iterator.startReadingBits();
|
||||
const sampleRate = iterator.getBits(20);
|
||||
const channels = iterator.getBits(3) + 1;
|
||||
const bitsPerSample = iterator.getBits(5);
|
||||
const totalSamples = iterator.getBits(36);
|
||||
iterator.getBits(128); // md5
|
||||
iterator.stopReadingBits();
|
||||
const counterNow = iterator.counter.getOffset();
|
||||
const size = counterNow - counter;
|
||||
iterator.counter.decrement(size);
|
||||
const asUint8Array = iterator.getSlice(size);
|
||||
const flacStreamInfo = {
|
||||
type: 'flac-streaminfo',
|
||||
bitsPerSample,
|
||||
channels,
|
||||
maximumBlockSize,
|
||||
maximumFrameSize,
|
||||
minimumBlockSize,
|
||||
minimumFrameSize,
|
||||
sampleRate,
|
||||
totalSamples,
|
||||
};
|
||||
state.structure.getFlacStructure().boxes.push(flacStreamInfo);
|
||||
await (0, register_track_1.registerAudioTrack)({
|
||||
container: 'flac',
|
||||
track: {
|
||||
codec: 'flac',
|
||||
type: 'audio',
|
||||
description: asUint8Array,
|
||||
codecData: { type: 'flac-description', data: asUint8Array },
|
||||
codecEnum: 'flac',
|
||||
numberOfChannels: channels,
|
||||
sampleRate,
|
||||
originalTimescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
|
||||
trackId: 0,
|
||||
startInSeconds: 0,
|
||||
timescale: webcodecs_timescale_1.WEBCODECS_TIMESCALE,
|
||||
trackMediaTimeOffsetInTrackTimescale: 0,
|
||||
},
|
||||
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
|
||||
tracks: state.callbacks.tracks,
|
||||
logLevel: state.logLevel,
|
||||
onAudioTrack: state.onAudioTrack,
|
||||
});
|
||||
state.callbacks.tracks.setIsDone(state.logLevel);
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
exports.parseStreamInfo = parseStreamInfo;
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import type { BufferIterator } from '../../iterator/buffer-iterator';
|
||||
import type { ParseResult } from '../../parse-result';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const parseFlacUnkownBlock: ({ iterator, state, size, }: {
|
||||
iterator: BufferIterator;
|
||||
state: ParserState;
|
||||
size: number;
|
||||
}) => Promise<ParseResult>;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseFlacUnkownBlock = void 0;
|
||||
const parseFlacUnkownBlock = ({ iterator, state, size, }) => {
|
||||
iterator.discard(size);
|
||||
state.structure.getFlacStructure().boxes.push({
|
||||
type: 'flac-header',
|
||||
});
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
exports.parseFlacUnkownBlock = parseFlacUnkownBlock;
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import type { AudioSampleOffset } from '../../state/audio-sample-map';
|
||||
import type { FlacState } from '../../state/flac-state';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
import type { SamplesObservedState } from '../../state/samples-observed/slow-duration-fps';
|
||||
export type FlacSeekingHints = {
|
||||
type: 'flac-seeking-hints';
|
||||
audioSampleMap: AudioSampleOffset[];
|
||||
blockingBitStrategy: number | null;
|
||||
lastSampleObserved: boolean;
|
||||
};
|
||||
export declare const getSeekingHintsForFlac: ({ flacState, samplesObserved, }: {
|
||||
flacState: FlacState;
|
||||
samplesObserved: SamplesObservedState;
|
||||
}) => FlacSeekingHints;
|
||||
export declare const setSeekingHintsForFlac: ({ hints, state, }: {
|
||||
hints: FlacSeekingHints;
|
||||
state: ParserState;
|
||||
}) => void;
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setSeekingHintsForFlac = exports.getSeekingHintsForFlac = void 0;
|
||||
const getSeekingHintsForFlac = ({ flacState, samplesObserved, }) => {
|
||||
var _a;
|
||||
return {
|
||||
type: 'flac-seeking-hints',
|
||||
audioSampleMap: flacState.audioSamples.getSamples(),
|
||||
blockingBitStrategy: (_a = flacState.getBlockingBitStrategy()) !== null && _a !== void 0 ? _a : null,
|
||||
lastSampleObserved: samplesObserved.getLastSampleObserved(),
|
||||
};
|
||||
};
|
||||
exports.getSeekingHintsForFlac = getSeekingHintsForFlac;
|
||||
const setSeekingHintsForFlac = ({ hints, state, }) => {
|
||||
if (hints.blockingBitStrategy !== null) {
|
||||
state.flac.setBlockingBitStrategy(hints.blockingBitStrategy);
|
||||
}
|
||||
state.flac.audioSamples.setFromSeekingHints(hints.audioSampleMap);
|
||||
};
|
||||
exports.setSeekingHintsForFlac = setSeekingHintsForFlac;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
import type { MediaParserMetadataEntry } from '../../metadata/get-metadata';
|
||||
export type FlacHeader = {
|
||||
type: 'flac-header';
|
||||
};
|
||||
export type FlacStreamInfo = {
|
||||
type: 'flac-streaminfo';
|
||||
minimumBlockSize: number;
|
||||
maximumBlockSize: number;
|
||||
minimumFrameSize: number;
|
||||
maximumFrameSize: number;
|
||||
sampleRate: number;
|
||||
channels: number;
|
||||
bitsPerSample: number;
|
||||
totalSamples: number;
|
||||
};
|
||||
export type FlacVorbisComment = {
|
||||
type: 'flac-vorbis-comment';
|
||||
fields: MediaParserMetadataEntry[];
|
||||
};
|
||||
export type FlacUnknownBlock = {
|
||||
type: 'flac-unknown-block';
|
||||
};
|
||||
export type FlacStructure = {
|
||||
type: 'flac';
|
||||
boxes: (FlacHeader | FlacStreamInfo | FlacUnknownBlock | FlacVorbisComment)[];
|
||||
};
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Generated
Vendored
+6
@@ -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;
|
||||
Generated
Vendored
+11
@@ -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;
|
||||
Generated
Vendored
+40
@@ -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;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export interface BaseBox {
|
||||
boxSize: number;
|
||||
offset: number;
|
||||
}
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Generated
Vendored
+15
@@ -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;
|
||||
};
|
||||
Generated
Vendored
+19
@@ -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;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { TrakBox } from './trak/trak';
|
||||
export declare const collectSamplePositionsFromTrak: (trakBox: TrakBox) => import("../../get-sample-positions").SamplePosition[];
|
||||
Generated
Vendored
+49
@@ -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;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { MediaParserAdvancedColor } from '../../get-tracks';
|
||||
export declare const mediaParserAdvancedColorToWebCodecsColor: (color: MediaParserAdvancedColor) => VideoColorSpaceInit;
|
||||
Generated
Vendored
+12
@@ -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;
|
||||
Generated
Vendored
+19
@@ -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;
|
||||
Generated
Vendored
+33
@@ -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;
|
||||
Generated
Vendored
+14
@@ -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 {};
|
||||
Generated
Vendored
+38
@@ -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;
|
||||
Generated
Vendored
+29
@@ -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 {};
|
||||
Generated
Vendored
+82
@@ -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;
|
||||
Generated
Vendored
+15
@@ -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;
|
||||
Generated
Vendored
+27
@@ -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;
|
||||
Generated
Vendored
+11
@@ -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;
|
||||
Generated
Vendored
+33
@@ -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;
|
||||
Generated
Vendored
+14
@@ -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 {};
|
||||
Generated
Vendored
+64
@@ -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;
|
||||
Generated
Vendored
+13
@@ -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;
|
||||
Generated
Vendored
+22
@@ -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;
|
||||
Generated
Vendored
+14
@@ -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 {};
|
||||
Generated
Vendored
+33
@@ -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;
|
||||
Generated
Vendored
+11
@@ -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[]>;
|
||||
Generated
Vendored
+26
@@ -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;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { MediaParserKeyframe } from '../../options';
|
||||
import type { ParserState } from '../../state/parser-state';
|
||||
export declare const getKeyframesFromIsoBaseMedia: (state: ParserState) => MediaParserKeyframe[];
|
||||
Generated
Vendored
+58
@@ -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;
|
||||
Generated
Vendored
+15
@@ -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>;
|
||||
Generated
Vendored
+37
@@ -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;
|
||||
Generated
Vendored
+6
@@ -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>;
|
||||
Generated
Vendored
+112
@@ -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;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import type { SamplePosition } from '../../get-sample-positions';
|
||||
export declare const getSamplePositionBounds: (samplePositions: SamplePosition[], timescale: number) => {
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
Generated
Vendored
+21
@@ -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;
|
||||
Generated
Vendored
+13
@@ -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;
|
||||
};
|
||||
Generated
Vendored
+29
@@ -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;
|
||||
Generated
Vendored
+18
@@ -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>;
|
||||
Generated
Vendored
+111
@@ -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;
|
||||
Generated
Vendored
+15
@@ -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>;
|
||||
Generated
Vendored
+70
@@ -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;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { TrakBox } from './trak/trak';
|
||||
export declare const getVideoCodecFromIsoTrak: (trakBox: TrakBox) => "h264" | "vp9" | "av1" | "h265" | "prores";
|
||||
Generated
Vendored
+58
@@ -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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user