@remotion/media-parser
Version:
A pure JavaScript library for parsing video files
236 lines (235 loc) • 8.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.postprocessEbml = exports.parseEbml = void 0;
const log_1 = require("../../log");
const register_track_1 = require("../../register-track");
const get_sample_from_block_1 = require("./get-sample-from-block");
const make_track_1 = require("./make-track");
const all_segments_1 = require("./segments/all-segments");
const parseEbml = async (iterator, statesForProcessing, logLevel) => {
const hex = iterator.getMatroskaSegmentId();
if (hex === null) {
throw new Error('Not enough bytes left to parse EBML - this should not happen');
}
const off = iterator.counter.getOffset();
const size = iterator.getVint();
const minVintWidth = iterator.counter.getOffset() - off;
if (size === null) {
throw new Error('Not enough bytes left to parse EBML - this should not happen');
}
const hasInMap = all_segments_1.ebmlMap[hex];
if (!hasInMap) {
log_1.Log.verbose(logLevel, `Unknown EBML hex ID ${JSON.stringify(hex)}`);
iterator.discard(size);
return null;
}
if (hasInMap.type === 'uint') {
const beforeUintOffset = iterator.counter.getOffset();
const value = size === 0 ? 0 : iterator.getUint(size);
const { name } = hasInMap;
return {
// To work around TS limit
type: name,
value: {
value,
byteLength: iterator.counter.getOffset() - beforeUintOffset,
},
minVintWidth,
};
}
if (hasInMap.type === 'string') {
const value = iterator.getByteString(size, true);
return {
type: hasInMap.name,
value,
minVintWidth,
};
}
if (hasInMap.type === 'float') {
const value = size === 0
? 0.0
: size === 4
? iterator.getFloat32()
: iterator.getFloat64();
return {
type: hasInMap.name,
value: {
value,
size: size === 4 ? '32' : '64',
},
minVintWidth,
};
}
if (hasInMap.type === 'hex-string') {
return {
type: hasInMap.name,
value: '0x' +
[...iterator.getSlice(size)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
.replace(new RegExp('^' + hex), ''),
minVintWidth,
};
}
if (hasInMap.type === 'uint8array') {
return {
type: hasInMap.name,
value: iterator.getSlice(size),
minVintWidth,
};
}
if (hasInMap.type === 'children') {
const children = [];
const startOffset = iterator.counter.getOffset();
while (true) {
if (size === 0) {
break;
}
const offset = iterator.counter.getOffset();
const value = await (0, exports.parseEbml)(iterator, statesForProcessing, logLevel);
if (value) {
const remapped = statesForProcessing
? // eslint-disable-next-line @typescript-eslint/no-use-before-define
await (0, exports.postprocessEbml)({
offset,
ebml: value,
statesForProcessing,
})
: value;
children.push(remapped);
}
const offsetNow = iterator.counter.getOffset();
if (offsetNow - startOffset > size) {
throw new Error(`Offset ${offsetNow - startOffset} is larger than the length of the hex ${size}`);
}
if (offsetNow - startOffset === size) {
break;
}
}
return { type: hasInMap.name, value: children, minVintWidth };
}
// @ts-expect-error
throw new Error(`Unknown segment type ${hasInMap.type}`);
};
exports.parseEbml = parseEbml;
const postprocessEbml = async ({ offset, ebml, statesForProcessing: { webmState, callbacks, logLevel, onAudioTrack, onVideoTrack, structureState, avcState, }, }) => {
if (ebml.type === 'TimestampScale') {
webmState.setTimescale(ebml.value.value);
}
if (ebml.type === 'Tracks') {
callbacks.tracks.setIsDone(logLevel);
}
if (ebml.type === 'TrackEntry') {
webmState.onTrackEntrySegment(ebml);
const track = (0, make_track_1.getTrack)({
track: ebml,
timescale: webmState.getTimescale(),
});
if (track && track.type === 'audio') {
await (0, register_track_1.registerAudioTrack)({
track,
container: 'webm',
registerAudioSampleCallback: callbacks.registerAudioSampleCallback,
tracks: callbacks.tracks,
logLevel,
onAudioTrack,
});
}
if (track && track.type === 'video') {
if (track.codec !== make_track_1.NO_CODEC_PRIVATE_SHOULD_BE_DERIVED_FROM_SPS) {
await (0, register_track_1.registerVideoTrack)({
track,
container: 'webm',
logLevel,
onVideoTrack,
registerVideoSampleCallback: callbacks.registerVideoSampleCallback,
tracks: callbacks.tracks,
});
}
}
}
if (ebml.type === 'Timestamp') {
webmState.setTimestampOffset(offset, ebml.value.value);
}
if (ebml.type === 'Block' || ebml.type === 'SimpleBlock') {
const sample = await (0, get_sample_from_block_1.getSampleFromBlock)({
ebml,
webmState,
offset,
structureState,
callbacks,
logLevel,
onVideoTrack,
avcState,
});
if (sample.type === 'video-sample') {
await callbacks.onVideoSample({
videoSample: sample.videoSample,
trackId: sample.trackId,
});
return {
type: 'Block',
value: new Uint8Array([]),
minVintWidth: ebml.minVintWidth,
};
}
if (sample.type === 'audio-sample') {
await callbacks.onAudioSample({
audioSample: sample.audioSample,
trackId: sample.trackId,
});
return {
type: 'Block',
value: new Uint8Array([]),
minVintWidth: ebml.minVintWidth,
};
}
if (sample.type === 'no-sample') {
return {
type: 'Block',
value: new Uint8Array([]),
minVintWidth: ebml.minVintWidth,
};
}
}
if (ebml.type === 'BlockGroup') {
// Blocks don't have information about keyframes.
// https://ffmpeg.org/pipermail/ffmpeg-devel/2015-June/173825.html
// "For Blocks, keyframes is
// inferred by the absence of ReferenceBlock element (as done by matroskadec).""
const block = ebml.value.find((c) => c.type === 'SimpleBlock' || c.type === 'Block');
if (!block || (block.type !== 'SimpleBlock' && block.type !== 'Block')) {
throw new Error('Expected block segment');
}
const hasReferenceBlock = ebml.value.find((c) => c.type === 'ReferenceBlock');
const sample = block.value.length === 0
? null
: await (0, get_sample_from_block_1.getSampleFromBlock)({
ebml: block,
webmState,
offset,
structureState,
callbacks,
logLevel,
onVideoTrack,
avcState,
});
if (sample && sample.type === 'partial-video-sample') {
const completeFrame = {
...sample.partialVideoSample,
type: hasReferenceBlock ? 'delta' : 'key',
};
await callbacks.onVideoSample({
videoSample: completeFrame,
trackId: sample.trackId,
});
}
return {
type: 'BlockGroup',
value: [],
minVintWidth: ebml.minVintWidth,
};
}
return ebml;
};
exports.postprocessEbml = postprocessEbml;