@remotion/media-parser 4.0.198 → 4.0.200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boxes/iso-base-media/mdat/mdat.js +0 -1
- package/dist/boxes/webm/ebml.d.ts +2 -0
- package/dist/boxes/webm/ebml.js +72 -0
- package/dist/boxes/webm/make-header.d.ts +2 -0
- package/dist/boxes/webm/make-header.js +44 -0
- package/dist/boxes/webm/segments/all-segments.d.ts +5 -0
- package/dist/boxes/webm/segments/all-segments.js +5 -0
- package/dist/boxes/webm/segments/block-simple-block-flags.d.ts +9 -0
- package/dist/boxes/webm/segments/block-simple-block-flags.js +38 -0
- package/dist/boxes/webm/segments/track-entry.d.ts +20 -8
- package/dist/boxes/webm/segments/track-entry.js +64 -23
- package/dist/boxes/webm/segments.d.ts +2 -2
- package/dist/boxes/webm/segments.js +35 -6
- package/dist/buffer-iterator.d.ts +1 -0
- package/dist/buffer-iterator.js +9 -2
- package/dist/get-audio-codec.d.ts +1 -1
- package/dist/parser-state.d.ts +4 -6
- package/dist/parser-state.js +24 -16
- package/dist/webcodec-sample-types.d.ts +0 -1
- package/package.json +2 -2
- package/src/boxes/iso-base-media/mdat/mdat.ts +0 -1
- package/src/boxes/webm/ebml.ts +78 -0
- package/src/boxes/webm/make-header.ts +48 -0
- package/src/boxes/webm/segments/all-segments.ts +5 -0
- package/src/boxes/webm/segments/block-simple-block-flags.ts +52 -0
- package/src/boxes/webm/segments/track-entry.ts +108 -29
- package/src/boxes/webm/segments.ts +71 -9
- package/src/buffer-iterator.ts +8 -1
- package/src/parser-state.ts +30 -20
- package/src/test/create-matroska.test.ts +14 -0
- package/src/test/matroska.test.ts +75 -100
- package/src/test/stream-local.test.ts +47 -5
- package/src/webcodec-sample-types.ts +0 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -62,7 +62,6 @@ const parseMdat = async ({ data, size, fileOffset, existingBoxes, options, }) =>
|
|
|
62
62
|
await options.parserState.onAudioSample(sampleWithIndex.track.trackId, {
|
|
63
63
|
data: bytes,
|
|
64
64
|
timestamp: sampleWithIndex.samplePosition.offset,
|
|
65
|
-
offset: data.counter.getOffset(),
|
|
66
65
|
trackId: sampleWithIndex.track.trackId,
|
|
67
66
|
type: sampleWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
|
|
68
67
|
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// https://github.com/Vanilagy/webm-muxer/blob/main/src/ebml.ts#L101
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.getVariableInt = exports.measureEBMLVarInt = void 0;
|
|
5
|
+
const measureEBMLVarInt = (value) => {
|
|
6
|
+
if (value < (1 << 7) - 1) {
|
|
7
|
+
/** Top bit is set, leaving 7 bits to hold the integer, but we can't store
|
|
8
|
+
* 127 because "all bits set to one" is a reserved value. Same thing for the
|
|
9
|
+
* other cases below:
|
|
10
|
+
*/
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
if (value < (1 << 14) - 1) {
|
|
14
|
+
return 2;
|
|
15
|
+
}
|
|
16
|
+
if (value < (1 << 21) - 1) {
|
|
17
|
+
return 3;
|
|
18
|
+
}
|
|
19
|
+
if (value < (1 << 28) - 1) {
|
|
20
|
+
return 4;
|
|
21
|
+
}
|
|
22
|
+
if (value < 2 ** 35 - 1) {
|
|
23
|
+
return 5;
|
|
24
|
+
}
|
|
25
|
+
if (value < 2 ** 42 - 1) {
|
|
26
|
+
return 6;
|
|
27
|
+
}
|
|
28
|
+
throw new Error('EBML VINT size not supported ' + value);
|
|
29
|
+
};
|
|
30
|
+
exports.measureEBMLVarInt = measureEBMLVarInt;
|
|
31
|
+
const getVariableInt = (value, width = (0, exports.measureEBMLVarInt)(value)) => {
|
|
32
|
+
switch (width) {
|
|
33
|
+
case 1:
|
|
34
|
+
return new Uint8Array([(1 << 7) | value]);
|
|
35
|
+
case 2:
|
|
36
|
+
return new Uint8Array([(1 << 6) | (value >> 8), value]);
|
|
37
|
+
case 3:
|
|
38
|
+
return new Uint8Array([(1 << 5) | (value >> 16), value >> 8, value]);
|
|
39
|
+
case 4:
|
|
40
|
+
return new Uint8Array([
|
|
41
|
+
(1 << 4) | (value >> 24),
|
|
42
|
+
value >> 16,
|
|
43
|
+
value >> 8,
|
|
44
|
+
value,
|
|
45
|
+
]);
|
|
46
|
+
case 5:
|
|
47
|
+
/**
|
|
48
|
+
* JavaScript converts its doubles to 32-bit integers for bitwise
|
|
49
|
+
* operations, so we need to do a division by 2^32 instead of a
|
|
50
|
+
* right-shift of 32 to retain those top 3 bits
|
|
51
|
+
*/
|
|
52
|
+
return new Uint8Array([
|
|
53
|
+
(1 << 3) | ((value / 2 ** 32) & 0x7),
|
|
54
|
+
value >> 24,
|
|
55
|
+
value >> 16,
|
|
56
|
+
value >> 8,
|
|
57
|
+
value,
|
|
58
|
+
]);
|
|
59
|
+
case 6:
|
|
60
|
+
return new Uint8Array([
|
|
61
|
+
(1 << 2) | ((value / 2 ** 40) & 0x3),
|
|
62
|
+
(value / 2 ** 32) | 0,
|
|
63
|
+
value >> 24,
|
|
64
|
+
value >> 16,
|
|
65
|
+
value >> 8,
|
|
66
|
+
value,
|
|
67
|
+
]);
|
|
68
|
+
default:
|
|
69
|
+
throw new Error('Bad EBML VINT size ' + width);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.getVariableInt = getVariableInt;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeMatroskaHeader = exports.webmPattern = void 0;
|
|
4
|
+
const ebml_1 = require("./ebml");
|
|
5
|
+
const all_segments_1 = require("./segments/all-segments");
|
|
6
|
+
exports.webmPattern = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
|
|
7
|
+
const matroskaToHex = (matrId) => {
|
|
8
|
+
const numbers = [];
|
|
9
|
+
for (let i = 2; i < matrId.length; i += 2) {
|
|
10
|
+
const hex = matrId.substr(i, 2);
|
|
11
|
+
numbers.push(parseInt(hex, 16));
|
|
12
|
+
}
|
|
13
|
+
return numbers;
|
|
14
|
+
};
|
|
15
|
+
const makeMatroskaHeader = () => {
|
|
16
|
+
const size = 0x23;
|
|
17
|
+
const array = new Uint8Array([
|
|
18
|
+
...exports.webmPattern,
|
|
19
|
+
...(0, ebml_1.getVariableInt)(size),
|
|
20
|
+
...matroskaToHex(all_segments_1.matroskaElements.EBMLVersion),
|
|
21
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
22
|
+
1,
|
|
23
|
+
...matroskaToHex(all_segments_1.matroskaElements.EBMLReadVersion),
|
|
24
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
25
|
+
1,
|
|
26
|
+
...matroskaToHex(all_segments_1.matroskaElements.EBMLMaxIDLength),
|
|
27
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
28
|
+
4,
|
|
29
|
+
...matroskaToHex(all_segments_1.matroskaElements.EBMLMaxSizeLength),
|
|
30
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
31
|
+
8,
|
|
32
|
+
...matroskaToHex(all_segments_1.matroskaElements.DocType),
|
|
33
|
+
...(0, ebml_1.getVariableInt)(8),
|
|
34
|
+
...new TextEncoder().encode('matroska'),
|
|
35
|
+
...matroskaToHex(all_segments_1.matroskaElements.DocTypeVersion),
|
|
36
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
37
|
+
4,
|
|
38
|
+
...matroskaToHex(all_segments_1.matroskaElements.DocTypeReadVersion),
|
|
39
|
+
...(0, ebml_1.getVariableInt)(1),
|
|
40
|
+
2,
|
|
41
|
+
]);
|
|
42
|
+
return array;
|
|
43
|
+
};
|
|
44
|
+
exports.makeMatroskaHeader = makeMatroskaHeader;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export declare const matroskaElements: {
|
|
2
2
|
readonly EBMLMaxIDLength: "0x42f2";
|
|
3
|
+
readonly EBMLVersion: "0x4286";
|
|
4
|
+
readonly EBMLReadVersion: "0x42F7";
|
|
3
5
|
readonly EBMLMaxSizeLength: "0x42f3";
|
|
6
|
+
readonly DocType: "0x4282";
|
|
7
|
+
readonly DocTypeVersion: "0x4287";
|
|
8
|
+
readonly DocTypeReadVersion: "0x4285";
|
|
4
9
|
readonly Segment: "0x18538067";
|
|
5
10
|
readonly SeekHead: "0x114d9b74";
|
|
6
11
|
readonly Seek: "0x4dbb";
|
|
@@ -3,7 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getSegmentName = exports.knownIdsWithThreeLength = exports.knownIdsWithTwoLength = exports.knownIdsWithOneLength = exports.matroskaElements = void 0;
|
|
4
4
|
exports.matroskaElements = {
|
|
5
5
|
EBMLMaxIDLength: '0x42f2',
|
|
6
|
+
EBMLVersion: '0x4286',
|
|
7
|
+
EBMLReadVersion: '0x42F7',
|
|
6
8
|
EBMLMaxSizeLength: '0x42f3',
|
|
9
|
+
DocType: '0x4282',
|
|
10
|
+
DocTypeVersion: '0x4287',
|
|
11
|
+
DocTypeReadVersion: '0x4285',
|
|
7
12
|
Segment: '0x18538067',
|
|
8
13
|
SeekHead: '0x114d9b74',
|
|
9
14
|
Seek: '0x4dbb',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BufferIterator } from '../../../buffer-iterator';
|
|
2
|
+
import { matroskaElements } from './all-segments';
|
|
3
|
+
type BlockFlags = {
|
|
4
|
+
invisible: boolean;
|
|
5
|
+
lacing: number;
|
|
6
|
+
keyframe: boolean | null;
|
|
7
|
+
};
|
|
8
|
+
export declare const parseBlockFlags: (iterator: BufferIterator, type: (typeof matroskaElements)['Block'] | (typeof matroskaElements)['SimpleBlock']) => BlockFlags;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseBlockFlags = void 0;
|
|
4
|
+
const all_segments_1 = require("./all-segments");
|
|
5
|
+
const parseBlockFlags = (iterator, type) => {
|
|
6
|
+
if (type === all_segments_1.matroskaElements.Block) {
|
|
7
|
+
iterator.startReadingBits();
|
|
8
|
+
// Reserved
|
|
9
|
+
iterator.getBits(4);
|
|
10
|
+
const invisible = Boolean(iterator.getBits(1));
|
|
11
|
+
const lacing = iterator.getBits(2);
|
|
12
|
+
// unused
|
|
13
|
+
iterator.getBits(1);
|
|
14
|
+
iterator.stopReadingBits();
|
|
15
|
+
return {
|
|
16
|
+
invisible,
|
|
17
|
+
lacing,
|
|
18
|
+
keyframe: null,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (type === all_segments_1.matroskaElements.SimpleBlock) {
|
|
22
|
+
iterator.startReadingBits();
|
|
23
|
+
const keyframe = Boolean(iterator.getBits(1));
|
|
24
|
+
// Reserved
|
|
25
|
+
iterator.getBits(3);
|
|
26
|
+
const invisible = Boolean(iterator.getBits(1));
|
|
27
|
+
const lacing = iterator.getBits(2);
|
|
28
|
+
iterator.getBits(1);
|
|
29
|
+
iterator.stopReadingBits();
|
|
30
|
+
return {
|
|
31
|
+
invisible,
|
|
32
|
+
lacing,
|
|
33
|
+
keyframe,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Unexpected type');
|
|
37
|
+
};
|
|
38
|
+
exports.parseBlockFlags = parseBlockFlags;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { BufferIterator } from '../../../buffer-iterator';
|
|
2
2
|
import type { ParserContext } from '../../../parser-context';
|
|
3
|
+
import type { VideoSample } from '../../../webcodec-sample-types';
|
|
3
4
|
import type { MatroskaSegment } from '../segments';
|
|
5
|
+
import type { matroskaElements } from './all-segments';
|
|
4
6
|
export type TrackEntrySegment = {
|
|
5
7
|
type: 'track-entry-segment';
|
|
6
8
|
children: MatroskaSegment[];
|
|
@@ -135,29 +137,39 @@ export type TimestampSegment = {
|
|
|
135
137
|
timestamp: number;
|
|
136
138
|
};
|
|
137
139
|
export declare const parseTimestampSegment: (iterator: BufferIterator, length: number) => TimestampSegment;
|
|
138
|
-
export type
|
|
139
|
-
type: 'simple-block-segment';
|
|
140
|
+
export type SimpleBlockOrBlockSegment = {
|
|
141
|
+
type: 'simple-block-or-block-segment';
|
|
140
142
|
length: number;
|
|
141
143
|
trackNumber: number;
|
|
142
144
|
timecode: number;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
lacing: [number, number];
|
|
145
|
+
keyframe: boolean | null;
|
|
146
|
+
lacing: number;
|
|
146
147
|
invisible: boolean;
|
|
147
|
-
|
|
148
|
+
videoSample: Omit<VideoSample, 'type'> | null;
|
|
148
149
|
};
|
|
149
150
|
export type GetTracks = () => TrackEntrySegment[];
|
|
150
|
-
export declare const
|
|
151
|
+
export declare const parseSimpleBlockOrBlockSegment: ({ iterator, length, parserContext, type, }: {
|
|
151
152
|
iterator: BufferIterator;
|
|
152
153
|
length: number;
|
|
153
154
|
parserContext: ParserContext;
|
|
154
|
-
|
|
155
|
+
type: (typeof matroskaElements)['Block'] | (typeof matroskaElements)['SimpleBlock'];
|
|
156
|
+
}) => Promise<SimpleBlockOrBlockSegment>;
|
|
155
157
|
export declare const parseTrackNumber: (iterator: BufferIterator, length: number) => TrackNumberSegment;
|
|
156
158
|
export type BlockGroupSegment = {
|
|
157
159
|
type: 'block-group-segment';
|
|
158
160
|
children: MatroskaSegment[];
|
|
159
161
|
};
|
|
160
162
|
export declare const parseBlockGroupSegment: (iterator: BufferIterator, length: number, parserContext: ParserContext) => Promise<BlockGroupSegment>;
|
|
163
|
+
export type ReferenceBlockSegment = {
|
|
164
|
+
type: 'reference-block-segment';
|
|
165
|
+
referenceBlock: number;
|
|
166
|
+
};
|
|
167
|
+
export declare const parseReferenceBlockSegment: (iterator: BufferIterator, length: number) => ReferenceBlockSegment;
|
|
168
|
+
export type BlockAdditionsSegment = {
|
|
169
|
+
type: 'block-additions-segment';
|
|
170
|
+
blockAdditions: Uint8Array;
|
|
171
|
+
};
|
|
172
|
+
export declare const parseBlockAdditionsSegment: (iterator: BufferIterator, length: number) => BlockAdditionsSegment;
|
|
161
173
|
export type BlockElement = {
|
|
162
174
|
type: 'block-element-segment';
|
|
163
175
|
length: number;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseBitDepthSegment = exports.parseChannelsSegment = exports.parseSamplingFrequencySegment = exports.parseBlockElementSegment = exports.parseBlockGroupSegment = exports.parseTrackNumber = exports.
|
|
3
|
+
exports.parseBitDepthSegment = exports.parseChannelsSegment = exports.parseSamplingFrequencySegment = exports.parseBlockElementSegment = exports.parseBlockAdditionsSegment = exports.parseReferenceBlockSegment = exports.parseBlockGroupSegment = exports.parseTrackNumber = exports.parseSimpleBlockOrBlockSegment = exports.parseTimestampSegment = exports.parseTagSegment = exports.parseTagsSegment = exports.parseDefaultFlagSegment = exports.parseSegmentUUIDSegment = exports.parseCrc32Segment = exports.parseCodecPrivateSegment = exports.parseInterlacedSegment = exports.parseTitleSegment = exports.parseColorSegment = exports.parseMaxBlockAdditionId = exports.parseAlphaModeSegment = exports.parseDisplayHeightSegment = exports.parseDisplayWidthSegment = exports.parseHeightSegment = exports.parseWidthSegment = exports.parseAudioSegment = exports.parseVideoSegment = exports.parseDefaultDurationSegment = exports.parseTrackTypeSegment = exports.parseCodecSegment = exports.parseLanguageSegment = exports.parseFlagLacing = exports.parseTrackUID = exports.parseTrackEntry = void 0;
|
|
4
|
+
const block_simple_block_flags_1 = require("./block-simple-block-flags");
|
|
4
5
|
const parse_children_1 = require("./parse-children");
|
|
5
6
|
const parseTrackEntry = async (iterator, length, parserContext) => {
|
|
6
7
|
const children = await (0, parse_children_1.expectChildren)({
|
|
@@ -299,8 +300,15 @@ const parseTagSegment = (iterator, length) => {
|
|
|
299
300
|
};
|
|
300
301
|
exports.parseTagSegment = parseTagSegment;
|
|
301
302
|
const parseTimestampSegment = (iterator, length) => {
|
|
302
|
-
if (length >
|
|
303
|
-
throw new Error('Expected timestamp segment to be 1 byte or 2 bytes');
|
|
303
|
+
if (length > 3) {
|
|
304
|
+
throw new Error('Expected timestamp segment to be 1 byte or 2 bytes, but is ' + length);
|
|
305
|
+
}
|
|
306
|
+
if (length === 3) {
|
|
307
|
+
const val = iterator.getUint24();
|
|
308
|
+
return {
|
|
309
|
+
type: 'timestamp-segment',
|
|
310
|
+
timestamp: val,
|
|
311
|
+
};
|
|
304
312
|
}
|
|
305
313
|
const value = length === 2 ? iterator.getUint16() : iterator.getUint8();
|
|
306
314
|
return {
|
|
@@ -309,37 +317,46 @@ const parseTimestampSegment = (iterator, length) => {
|
|
|
309
317
|
};
|
|
310
318
|
};
|
|
311
319
|
exports.parseTimestampSegment = parseTimestampSegment;
|
|
312
|
-
const
|
|
320
|
+
const parseSimpleBlockOrBlockSegment = async ({ iterator, length, parserContext, type, }) => {
|
|
313
321
|
const start = iterator.counter.getOffset();
|
|
314
322
|
const trackNumber = iterator.getVint();
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
const invisible = Boolean((headerFlags >> 5) & 1);
|
|
318
|
-
const pos6 = (headerFlags >> 6) & 1;
|
|
319
|
-
const pos7 = (headerFlags >> 6) & 1;
|
|
320
|
-
const keyframe = Boolean((headerFlags >> 7) & 1);
|
|
323
|
+
const timecodeRelativeToCluster = iterator.getUint16();
|
|
324
|
+
const { invisible, lacing, keyframe } = (0, block_simple_block_flags_1.parseBlockFlags)(iterator, type);
|
|
321
325
|
const codec = parserContext.parserState.getTrackInfoByNumber(trackNumber);
|
|
326
|
+
const clusterOffset = parserContext.parserState.getTimestampOffsetForByteOffset(iterator.counter.getOffset());
|
|
327
|
+
if (clusterOffset === undefined) {
|
|
328
|
+
throw new Error('Could not find offset for byte offset ' + iterator.counter.getOffset());
|
|
329
|
+
}
|
|
330
|
+
const timecode = timecodeRelativeToCluster + clusterOffset;
|
|
322
331
|
if (!codec) {
|
|
323
332
|
throw new Error('Could not find codec for track ' + trackNumber);
|
|
324
333
|
}
|
|
325
|
-
const
|
|
334
|
+
const remainingNow = length - (iterator.counter.getOffset() - start);
|
|
335
|
+
let videoSample = null;
|
|
326
336
|
if (codec.codec.startsWith('V_')) {
|
|
327
|
-
const
|
|
328
|
-
await parserContext.parserState.onVideoSample(trackNumber, {
|
|
337
|
+
const partialVideoSample = {
|
|
329
338
|
data: iterator.getSlice(remainingNow),
|
|
330
339
|
cts: null,
|
|
331
340
|
dts: null,
|
|
332
341
|
duration: undefined,
|
|
333
|
-
type: keyframe ? 'key' : 'delta',
|
|
334
342
|
trackId: trackNumber,
|
|
335
343
|
timestamp: timecode,
|
|
336
|
-
}
|
|
344
|
+
};
|
|
345
|
+
if (keyframe === null) {
|
|
346
|
+
// If we don't know if this is a keyframe, we know after we emit the BlockGroup
|
|
347
|
+
videoSample = partialVideoSample;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const sample = {
|
|
351
|
+
...partialVideoSample,
|
|
352
|
+
type: keyframe ? 'key' : 'delta',
|
|
353
|
+
};
|
|
354
|
+
await parserContext.parserState.onVideoSample(trackNumber, sample);
|
|
355
|
+
}
|
|
337
356
|
}
|
|
338
357
|
if (codec.codec.startsWith('A_')) {
|
|
339
|
-
const vorbisRemaining = length - (iterator.counter.getOffset() - start);
|
|
340
358
|
await parserContext.parserState.onAudioSample(trackNumber, {
|
|
341
|
-
data: iterator.getSlice(
|
|
342
|
-
offset: timecode,
|
|
359
|
+
data: iterator.getSlice(remainingNow),
|
|
343
360
|
trackId: trackNumber,
|
|
344
361
|
timestamp: timecode,
|
|
345
362
|
type: 'key',
|
|
@@ -350,18 +367,17 @@ const parseSimpleBlockSegment = async ({ iterator, length, parserContext, }) =>
|
|
|
350
367
|
iterator.discard(remainingNowAfter);
|
|
351
368
|
}
|
|
352
369
|
return {
|
|
353
|
-
type: 'simple-block-segment',
|
|
370
|
+
type: 'simple-block-or-block-segment',
|
|
354
371
|
length,
|
|
355
372
|
trackNumber,
|
|
356
373
|
timecode,
|
|
357
|
-
headerFlags,
|
|
358
374
|
keyframe,
|
|
359
|
-
lacing
|
|
375
|
+
lacing,
|
|
360
376
|
invisible,
|
|
361
|
-
|
|
377
|
+
videoSample,
|
|
362
378
|
};
|
|
363
379
|
};
|
|
364
|
-
exports.
|
|
380
|
+
exports.parseSimpleBlockOrBlockSegment = parseSimpleBlockOrBlockSegment;
|
|
365
381
|
const parseTrackNumber = (iterator, length) => {
|
|
366
382
|
if (length !== 1) {
|
|
367
383
|
throw new Error('Expected track number to be 1 byte');
|
|
@@ -390,6 +406,31 @@ const parseBlockGroupSegment = async (iterator, length, parserContext) => {
|
|
|
390
406
|
};
|
|
391
407
|
};
|
|
392
408
|
exports.parseBlockGroupSegment = parseBlockGroupSegment;
|
|
409
|
+
const parseReferenceBlockSegment = (iterator, length) => {
|
|
410
|
+
if (length > 4) {
|
|
411
|
+
throw new Error(`Expected reference block segment to be 4 bytes, but got ${length}`);
|
|
412
|
+
}
|
|
413
|
+
const referenceBlock = length === 4
|
|
414
|
+
? iterator.getUint32()
|
|
415
|
+
: length === 3
|
|
416
|
+
? iterator.getUint24()
|
|
417
|
+
: length === 2
|
|
418
|
+
? iterator.getUint16()
|
|
419
|
+
: iterator.getUint8();
|
|
420
|
+
return {
|
|
421
|
+
type: 'reference-block-segment',
|
|
422
|
+
referenceBlock,
|
|
423
|
+
};
|
|
424
|
+
};
|
|
425
|
+
exports.parseReferenceBlockSegment = parseReferenceBlockSegment;
|
|
426
|
+
const parseBlockAdditionsSegment = (iterator, length) => {
|
|
427
|
+
const blockAdditions = iterator.getSlice(length);
|
|
428
|
+
return {
|
|
429
|
+
type: 'block-additions-segment',
|
|
430
|
+
blockAdditions,
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
exports.parseBlockAdditionsSegment = parseBlockAdditionsSegment;
|
|
393
434
|
const parseBlockElementSegment = (iterator, length) => {
|
|
394
435
|
iterator.discard(length);
|
|
395
436
|
return {
|
|
@@ -10,11 +10,11 @@ import { type SeekSegment } from './segments/seek';
|
|
|
10
10
|
import type { SeekHeadSegment } from './segments/seek-head';
|
|
11
11
|
import { type SeekPositionSegment } from './segments/seek-position';
|
|
12
12
|
import type { TimestampScaleSegment } from './segments/timestamp-scale';
|
|
13
|
-
import type { AlphaModeSegment, AudioSegment, BitDepthSegment, BlockElement, BlockGroupSegment, ChannelsSegment, ClusterSegment, CodecPrivateSegment, CodecSegment, ColorSegment, Crc32Segment, DefaultDurationSegment, DefaultFlagSegment, DisplayHeightSegment, DisplayWidthSegment, FlagLacingSegment, HeightSegment, InterlacedSegment, LanguageSegment, MaxBlockAdditionId, SamplingFrequencySegment, SegmentUUIDSegment,
|
|
13
|
+
import type { AlphaModeSegment, AudioSegment, BitDepthSegment, BlockAdditionsSegment, BlockElement, BlockGroupSegment, ChannelsSegment, ClusterSegment, CodecPrivateSegment, CodecSegment, ColorSegment, Crc32Segment, DefaultDurationSegment, DefaultFlagSegment, DisplayHeightSegment, DisplayWidthSegment, FlagLacingSegment, HeightSegment, InterlacedSegment, LanguageSegment, MaxBlockAdditionId, ReferenceBlockSegment, SamplingFrequencySegment, SegmentUUIDSegment, SimpleBlockOrBlockSegment, TagSegment, TagsSegment, TimestampSegment, TitleSegment, TrackEntrySegment, TrackNumberSegment, TrackTypeSegment, TrackUIDSegment, VideoSegment, WidthSegment } from './segments/track-entry';
|
|
14
14
|
import type { TracksSegment } from './segments/tracks';
|
|
15
15
|
import type { UnknownSegment } from './segments/unknown';
|
|
16
16
|
import type { VoidSegment } from './segments/void';
|
|
17
17
|
import type { WritingAppSegment } from './segments/writing';
|
|
18
|
-
export type MatroskaSegment = MainSegment | UnknownSegment | SeekHeadSegment | SeekSegment | SeekPositionSegment | VoidSegment | InfoSegment | TimestampScaleSegment | MuxingAppSegment | WritingAppSegment | DurationSegment | TracksSegment | TrackEntrySegment | TrackNumberSegment | TrackUIDSegment | FlagLacingSegment | LanguageSegment | CodecSegment | TrackTypeSegment | DefaultDurationSegment | VideoSegment | WidthSegment | HeightSegment | DisplayWidthSegment | DisplayHeightSegment | AlphaModeSegment | MaxBlockAdditionId | ColorSegment | TitleSegment | InterlacedSegment | CodecPrivateSegment | Crc32Segment | SegmentUUIDSegment | DefaultFlagSegment | TagsSegment | TagSegment | ClusterSegment | TimestampSegment |
|
|
18
|
+
export type MatroskaSegment = MainSegment | UnknownSegment | SeekHeadSegment | SeekSegment | SeekPositionSegment | VoidSegment | InfoSegment | TimestampScaleSegment | MuxingAppSegment | WritingAppSegment | DurationSegment | TracksSegment | TrackEntrySegment | TrackNumberSegment | TrackUIDSegment | FlagLacingSegment | LanguageSegment | CodecSegment | TrackTypeSegment | DefaultDurationSegment | VideoSegment | WidthSegment | HeightSegment | DisplayWidthSegment | DisplayHeightSegment | AlphaModeSegment | MaxBlockAdditionId | ColorSegment | TitleSegment | InterlacedSegment | CodecPrivateSegment | Crc32Segment | SegmentUUIDSegment | DefaultFlagSegment | TagsSegment | TagSegment | ClusterSegment | TimestampSegment | SimpleBlockOrBlockSegment | BlockGroupSegment | BlockElement | SeekIdSegment | AudioSegment | SamplingFrequencySegment | ChannelsSegment | BitDepthSegment | ReferenceBlockSegment | BlockAdditionsSegment;
|
|
19
19
|
export type OnTrackEntrySegment = (trackEntry: TrackEntrySegment) => void;
|
|
20
20
|
export declare const expectSegment: (iterator: BufferIterator, parserContext: ParserContext) => Promise<ParseResult>;
|
|
@@ -168,18 +168,47 @@ const parseSegment = async ({ segmentId, iterator, length, parserContext, }) =>
|
|
|
168
168
|
if (segmentId === all_segments_1.matroskaElements.BitDepth) {
|
|
169
169
|
return (0, track_entry_1.parseBitDepthSegment)(iterator, length);
|
|
170
170
|
}
|
|
171
|
-
if (segmentId ===
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return
|
|
171
|
+
if (segmentId === all_segments_1.matroskaElements.Timestamp) {
|
|
172
|
+
const offset = iterator.counter.getOffset();
|
|
173
|
+
const timestampSegment = (0, track_entry_1.parseTimestampSegment)(iterator, length);
|
|
174
|
+
parserContext.parserState.setTimestampOffset(offset, timestampSegment.timestamp);
|
|
175
|
+
return timestampSegment;
|
|
176
|
+
}
|
|
177
|
+
if (segmentId === all_segments_1.matroskaElements.SimpleBlock ||
|
|
178
|
+
segmentId === all_segments_1.matroskaElements.Block) {
|
|
179
|
+
return (0, track_entry_1.parseSimpleBlockOrBlockSegment)({
|
|
176
180
|
iterator,
|
|
177
181
|
length,
|
|
178
182
|
parserContext,
|
|
183
|
+
type: segmentId,
|
|
179
184
|
});
|
|
180
185
|
}
|
|
186
|
+
if (segmentId === all_segments_1.matroskaElements.ReferenceBlock) {
|
|
187
|
+
return (0, track_entry_1.parseReferenceBlockSegment)(iterator, length);
|
|
188
|
+
}
|
|
189
|
+
if (segmentId === all_segments_1.matroskaElements.BlockAdditions) {
|
|
190
|
+
return (0, track_entry_1.parseBlockAdditionsSegment)(iterator, length);
|
|
191
|
+
}
|
|
181
192
|
if (segmentId === '0xa0') {
|
|
182
|
-
|
|
193
|
+
const blockGroup = await (0, track_entry_1.parseBlockGroupSegment)(iterator, length, parserContext);
|
|
194
|
+
// Blocks don't have information about keyframes.
|
|
195
|
+
// https://ffmpeg.org/pipermail/ffmpeg-devel/2015-June/173825.html
|
|
196
|
+
// "For Blocks, keyframes is
|
|
197
|
+
// inferred by the absence of ReferenceBlock element (as done by matroskadec).""
|
|
198
|
+
const block = blockGroup.children.find((c) => c.type === 'simple-block-or-block-segment');
|
|
199
|
+
if (!block || block.type !== 'simple-block-or-block-segment') {
|
|
200
|
+
throw new Error('Expected block segment');
|
|
201
|
+
}
|
|
202
|
+
const hasReferenceBlock = blockGroup.children.find((c) => c.type === 'reference-block-segment');
|
|
203
|
+
const partialVideoSample = block.videoSample;
|
|
204
|
+
if (partialVideoSample) {
|
|
205
|
+
const completeFrame = {
|
|
206
|
+
...partialVideoSample,
|
|
207
|
+
type: hasReferenceBlock ? 'delta' : 'key',
|
|
208
|
+
};
|
|
209
|
+
await parserContext.parserState.onVideoSample(partialVideoSample.trackId, completeFrame);
|
|
210
|
+
}
|
|
211
|
+
return blockGroup;
|
|
183
212
|
}
|
|
184
213
|
if (segmentId === '0xa1') {
|
|
185
214
|
return (0, track_entry_1.parseBlockElementSegment)(iterator, length);
|
|
@@ -34,6 +34,7 @@ export declare const getArrayBufferIterator: (initialData: Uint8Array, maxBytes?
|
|
|
34
34
|
getEBML: () => number;
|
|
35
35
|
getInt8: () => number;
|
|
36
36
|
getUint16: () => number;
|
|
37
|
+
getUint24: () => number;
|
|
37
38
|
getInt16: () => number;
|
|
38
39
|
getUint32: () => number;
|
|
39
40
|
getFixedPointUnsigned1616Number: () => number;
|
package/dist/buffer-iterator.js
CHANGED
|
@@ -13,6 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var _OffsetCounter_offset, _OffsetCounter_discardedBytes;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.getArrayBufferIterator = exports.OffsetCounter = void 0;
|
|
16
|
+
const make_header_1 = require("./boxes/webm/make-header");
|
|
16
17
|
const all_segments_1 = require("./boxes/webm/segments/all-segments");
|
|
17
18
|
class OffsetCounter {
|
|
18
19
|
constructor(initial) {
|
|
@@ -49,7 +50,6 @@ class OffsetCounter {
|
|
|
49
50
|
exports.OffsetCounter = OffsetCounter;
|
|
50
51
|
_OffsetCounter_offset = new WeakMap(), _OffsetCounter_discardedBytes = new WeakMap();
|
|
51
52
|
const isoBaseMediaMp4Pattern = new TextEncoder().encode('ftyp');
|
|
52
|
-
const webmPattern = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
|
|
53
53
|
const mpegPattern = new Uint8Array([0xff, 0xf3, 0xe4, 0x64]);
|
|
54
54
|
const matchesPattern = (pattern) => {
|
|
55
55
|
return (data) => {
|
|
@@ -129,7 +129,7 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
|
|
|
129
129
|
return matchesPattern(isoBaseMediaMp4Pattern)(data.subarray(4, 8));
|
|
130
130
|
};
|
|
131
131
|
const isWebm = () => {
|
|
132
|
-
return matchesPattern(webmPattern)(data.subarray(0, 4));
|
|
132
|
+
return matchesPattern(make_header_1.webmPattern)(data.subarray(0, 4));
|
|
133
133
|
};
|
|
134
134
|
const isMp3 = () => {
|
|
135
135
|
return matchesPattern(mpegPattern)(data.subarray(0, 4));
|
|
@@ -315,6 +315,13 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
|
|
|
315
315
|
counter.increment(2);
|
|
316
316
|
return val;
|
|
317
317
|
},
|
|
318
|
+
getUint24: () => {
|
|
319
|
+
const val1 = view.getUint8(counter.getDiscardedOffset());
|
|
320
|
+
const val2 = view.getUint8(counter.getDiscardedOffset());
|
|
321
|
+
const val3 = view.getUint8(counter.getDiscardedOffset());
|
|
322
|
+
counter.increment(3);
|
|
323
|
+
return (val1 << 16) | (val2 << 8) | val3;
|
|
324
|
+
},
|
|
318
325
|
getInt16: () => {
|
|
319
326
|
const val = view.getInt16(counter.getDiscardedOffset());
|
|
320
327
|
counter.increment(2);
|
|
@@ -14,7 +14,7 @@ export declare const getNumberOfChannelsFromTrak: (trak: TrakBox) => number | nu
|
|
|
14
14
|
export declare const getSampleRate: (trak: TrakBox) => number | null;
|
|
15
15
|
export declare const getAudioCodecFromTrak: (trak: TrakBox) => AudioCodecInfo | null;
|
|
16
16
|
export declare const getAudioCodecFromIso: (moov: MoovBox) => AudioCodecInfo | null;
|
|
17
|
-
export declare const getAudioCodecFromMatroska: (mainSegment: MainSegment) => "
|
|
17
|
+
export declare const getAudioCodecFromMatroska: (mainSegment: MainSegment) => "vorbis" | "opus" | "mp3" | "aac" | "pcm" | null;
|
|
18
18
|
export declare const getAudioCodecStringFromTrak: (trak: TrakBox) => {
|
|
19
19
|
codecString: string;
|
|
20
20
|
description: Uint8Array | undefined;
|
package/dist/parser-state.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { OnTrackEntrySegment } from './boxes/webm/segments';
|
|
2
2
|
import type { CodecSegment } from './boxes/webm/segments/track-entry';
|
|
3
3
|
import type { AudioSample, OnAudioSample, OnVideoSample, VideoSample } from './webcodec-sample-types';
|
|
4
|
-
export type InternalStats = {
|
|
5
|
-
samplesThatHadToBeQueued: number;
|
|
6
|
-
};
|
|
4
|
+
export type InternalStats = {};
|
|
7
5
|
export declare const makeParserState: ({ hasAudioCallbacks, hasVideoCallbacks, }: {
|
|
8
6
|
hasAudioCallbacks: boolean;
|
|
9
7
|
hasVideoCallbacks: boolean;
|
|
@@ -11,12 +9,12 @@ export declare const makeParserState: ({ hasAudioCallbacks, hasVideoCallbacks, }
|
|
|
11
9
|
onTrackEntrySegment: OnTrackEntrySegment;
|
|
12
10
|
getTrackInfoByNumber: (id: number) => CodecSegment;
|
|
13
11
|
registerVideoSampleCallback: (id: number, callback: OnVideoSample | null) => Promise<void>;
|
|
12
|
+
setTimestampOffset: (byteOffset: number, timestamp: number) => void;
|
|
13
|
+
getTimestampOffsetForByteOffset: (byteOffset: number) => number | undefined;
|
|
14
14
|
registerAudioSampleCallback: (id: number, callback: OnAudioSample | null) => Promise<void>;
|
|
15
15
|
onAudioSample: (trackId: number, audioSample: AudioSample) => Promise<void>;
|
|
16
16
|
onVideoSample: (trackId: number, videoSample: VideoSample) => Promise<void>;
|
|
17
|
-
getInternalStats: () => {
|
|
18
|
-
samplesThatHadToBeQueued: number;
|
|
19
|
-
};
|
|
17
|
+
getInternalStats: () => {};
|
|
20
18
|
getTimescale: () => number;
|
|
21
19
|
setTimescale: (newTimescale: number) => void;
|
|
22
20
|
};
|