@remotion/media-parser 4.0.230 → 4.0.231
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/webm/parse-webm-header.js +23 -4
- package/dist/boxes/webm/segments/parse-children.d.ts +12 -7
- package/dist/boxes/webm/segments/parse-children.js +67 -57
- package/dist/boxes/webm/segments.d.ts +8 -3
- package/dist/boxes/webm/segments.js +70 -39
- package/dist/create/iso-base-media/create-iso-base-media.d.ts +1 -1
- package/dist/create/iso-base-media/create-iso-base-media.js +4 -5
- package/dist/create/matroska/cluster.d.ts +7 -1
- package/dist/create/matroska/cluster.js +8 -5
- package/dist/create/matroska/create-matroska-media.d.ts +1 -1
- package/dist/create/matroska/create-matroska-media.js +27 -10
- package/dist/create/media-fn.d.ts +1 -0
- package/dist/emit-available-info.d.ts +1 -1
- package/dist/emit-available-info.js +23 -10
- package/dist/esm/buffer.mjs +2 -2
- package/dist/esm/index.mjs +591 -465
- package/dist/esm/web-fs.mjs +2 -2
- package/dist/get-audio-codec.d.ts +1 -1
- package/dist/get-audio-codec.js +2 -7
- package/dist/get-duration.d.ts +5 -0
- package/dist/get-duration.js +7 -3
- package/dist/get-fps.js +7 -0
- package/dist/get-video-codec.d.ts +2 -2
- package/dist/get-video-codec.js +2 -6
- package/dist/has-all-info.d.ts +1 -1
- package/dist/has-all-info.js +8 -8
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -1
- package/dist/options.d.ts +8 -1
- package/dist/parse-media.js +41 -14
- package/dist/parse-result.d.ts +15 -0
- package/dist/parse-video.d.ts +1 -1
- package/dist/parse-video.js +3 -0
- package/dist/readers/reader.d.ts +2 -2
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/writers/buffer-implementation/writer.d.ts +2 -2
- package/dist/writers/buffer-implementation/writer.js +2 -2
- package/dist/writers/web-fs.js +2 -3
- package/dist/writers/writer.d.ts +5 -3
- package/package.json +3 -3
|
@@ -2,14 +2,33 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseWebm = void 0;
|
|
4
4
|
const parse_children_1 = require("./segments/parse-children");
|
|
5
|
+
const continueAfterMatroskaResult = (result, children) => {
|
|
6
|
+
if (result.status === 'done') {
|
|
7
|
+
return {
|
|
8
|
+
status: 'done',
|
|
9
|
+
segments: children,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
status: 'incomplete',
|
|
14
|
+
segments: children,
|
|
15
|
+
continueParsing: async () => {
|
|
16
|
+
const newResult = await result.continueParsing();
|
|
17
|
+
return continueAfterMatroskaResult(newResult, children);
|
|
18
|
+
},
|
|
19
|
+
skipTo: null,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
5
22
|
// Parsing according to https://darkcoding.net/software/reading-mediarecorders-webm-opus-output/
|
|
6
|
-
const parseWebm = (counter, parserContext) => {
|
|
7
|
-
|
|
23
|
+
const parseWebm = async (counter, parserContext) => {
|
|
24
|
+
const children = [];
|
|
25
|
+
const results = await (0, parse_children_1.expectChildren)({
|
|
8
26
|
iterator: counter,
|
|
9
27
|
length: Infinity,
|
|
10
|
-
|
|
11
|
-
wrap: null,
|
|
28
|
+
children,
|
|
12
29
|
parserContext,
|
|
30
|
+
startOffset: counter.counter.getOffset(),
|
|
13
31
|
});
|
|
32
|
+
return continueAfterMatroskaResult(results, children);
|
|
14
33
|
};
|
|
15
34
|
exports.parseWebm = parseWebm;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import type { BufferIterator } from '../../../buffer-iterator';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ExpectSegmentParseResult, MatroskaParseResult } from '../../../parse-result';
|
|
3
3
|
import type { ParserContext } from '../../../parser-context';
|
|
4
4
|
import type { MatroskaSegment } from '../segments';
|
|
5
|
-
type
|
|
6
|
-
export declare const
|
|
5
|
+
import type { PossibleEbml } from './all-segments';
|
|
6
|
+
export declare const expectAndProcessSegment: ({ iterator, parserContext, offset, children, }: {
|
|
7
|
+
iterator: BufferIterator;
|
|
8
|
+
parserContext: ParserContext;
|
|
9
|
+
offset: number;
|
|
10
|
+
children: PossibleEbml[];
|
|
11
|
+
}) => Promise<ExpectSegmentParseResult>;
|
|
12
|
+
export declare const expectChildren: ({ iterator, length, children, parserContext, startOffset, }: {
|
|
7
13
|
iterator: BufferIterator;
|
|
8
14
|
length: number;
|
|
9
|
-
|
|
10
|
-
wrap: WrapChildren | null;
|
|
15
|
+
children: MatroskaSegment[];
|
|
11
16
|
parserContext: ParserContext;
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
startOffset: number;
|
|
18
|
+
}) => Promise<MatroskaParseResult>;
|
|
@@ -1,100 +1,110 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.expectChildren = void 0;
|
|
3
|
+
exports.expectChildren = exports.expectAndProcessSegment = void 0;
|
|
4
4
|
const segments_1 = require("../segments");
|
|
5
|
-
const processParseResult = ({ parseResult, children,
|
|
5
|
+
const processParseResult = ({ parseResult, children, }) => {
|
|
6
|
+
if (parseResult.segment && !children.includes(parseResult.segment)) {
|
|
7
|
+
children.push(parseResult.segment);
|
|
8
|
+
}
|
|
6
9
|
if (parseResult.status === 'incomplete') {
|
|
7
10
|
// No need to decrement because expectSegment already does it
|
|
8
11
|
return {
|
|
9
12
|
status: 'incomplete',
|
|
10
|
-
|
|
13
|
+
segment: parseResult.segment,
|
|
11
14
|
continueParsing: async () => {
|
|
12
15
|
const newParseResult = await parseResult.continueParsing();
|
|
13
16
|
return processParseResult({
|
|
14
17
|
children,
|
|
15
18
|
parseResult: newParseResult,
|
|
16
|
-
wrap,
|
|
17
19
|
});
|
|
18
20
|
},
|
|
19
|
-
skipTo: null,
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
|
-
for (const segment of parseResult.segments) {
|
|
23
|
-
children.push(segment);
|
|
24
|
-
}
|
|
25
23
|
return {
|
|
26
24
|
status: 'done',
|
|
27
|
-
|
|
25
|
+
segment: parseResult.segment,
|
|
28
26
|
};
|
|
29
27
|
};
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
const expectAndProcessSegment = async ({ iterator, parserContext, offset, children, }) => {
|
|
29
|
+
const segment = await (0, segments_1.expectSegment)({
|
|
30
|
+
iterator,
|
|
31
|
+
parserContext,
|
|
32
|
+
offset,
|
|
33
|
+
children,
|
|
34
|
+
});
|
|
35
|
+
return processParseResult({
|
|
36
|
+
children,
|
|
37
|
+
parseResult: segment,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
exports.expectAndProcessSegment = expectAndProcessSegment;
|
|
41
|
+
const continueAfterSegmentResult = async ({ result, length, children, parserContext, iterator, startOffset, }) => {
|
|
42
|
+
if (result.status === 'done') {
|
|
43
|
+
throw new Error('Should not continue after done');
|
|
33
44
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
if (continued.status === 'incomplete') {
|
|
37
|
-
if (!parserContext.supportsContentRange) {
|
|
38
|
-
throw new Error('Content-Range header is not supported by the reader, but was asked to seek');
|
|
39
|
-
}
|
|
45
|
+
const segmentResult = await result.continueParsing();
|
|
46
|
+
if (segmentResult.status === 'done') {
|
|
40
47
|
return {
|
|
41
48
|
status: 'incomplete',
|
|
42
|
-
continueParsing:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
continueParsing: () => {
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
51
|
+
return (0, exports.expectChildren)({
|
|
52
|
+
children,
|
|
53
|
+
iterator,
|
|
54
|
+
length,
|
|
55
|
+
parserContext,
|
|
56
|
+
startOffset,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
skipTo: null,
|
|
52
60
|
};
|
|
53
61
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
return {
|
|
63
|
+
status: 'incomplete',
|
|
64
|
+
continueParsing: () => {
|
|
65
|
+
return continueAfterSegmentResult({
|
|
66
|
+
result: segmentResult,
|
|
67
|
+
children,
|
|
68
|
+
iterator,
|
|
69
|
+
length,
|
|
70
|
+
parserContext,
|
|
71
|
+
startOffset,
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
skipTo: null,
|
|
75
|
+
};
|
|
62
76
|
};
|
|
63
|
-
const expectChildren = async ({ iterator, length,
|
|
64
|
-
const children = [...initialChildren];
|
|
65
|
-
const startOffset = iterator.counter.getOffset();
|
|
77
|
+
const expectChildren = async ({ iterator, length, children, parserContext, startOffset, }) => {
|
|
66
78
|
while (iterator.counter.getOffset() < startOffset + length) {
|
|
67
79
|
if (iterator.bytesRemaining() === 0) {
|
|
68
80
|
break;
|
|
69
81
|
}
|
|
70
|
-
const
|
|
71
|
-
const child =
|
|
82
|
+
const currentOffset = iterator.counter.getOffset();
|
|
83
|
+
const child = await (0, exports.expectAndProcessSegment)({
|
|
84
|
+
iterator,
|
|
85
|
+
parserContext,
|
|
86
|
+
offset: currentOffset,
|
|
72
87
|
children,
|
|
73
|
-
parseResult,
|
|
74
|
-
wrap,
|
|
75
88
|
});
|
|
76
89
|
if (child.status === 'incomplete') {
|
|
77
|
-
if (!parserContext.supportsContentRange) {
|
|
78
|
-
throw new Error('Content-Range header is not supported by the reader, but was asked to seek');
|
|
79
|
-
}
|
|
80
90
|
return {
|
|
81
91
|
status: 'incomplete',
|
|
82
|
-
continueParsing:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
continueParsing: () => {
|
|
93
|
+
return continueAfterSegmentResult({
|
|
94
|
+
result: child,
|
|
95
|
+
children,
|
|
96
|
+
iterator,
|
|
97
|
+
length: length - (currentOffset - startOffset),
|
|
98
|
+
parserContext,
|
|
99
|
+
startOffset: currentOffset,
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
skipTo: null,
|
|
92
103
|
};
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
return {
|
|
96
107
|
status: 'done',
|
|
97
|
-
segments: wrap ? [wrap(children)] : children,
|
|
98
108
|
};
|
|
99
109
|
};
|
|
100
110
|
exports.expectChildren = expectChildren;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { BufferIterator } from '../../buffer-iterator';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ExpectSegmentParseResult } from '../../parse-result';
|
|
3
3
|
import type { ParserContext } from '../../parser-context';
|
|
4
|
-
import type
|
|
4
|
+
import { type PossibleEbml, type TrackEntry } from './segments/all-segments';
|
|
5
5
|
export type MatroskaSegment = PossibleEbml;
|
|
6
6
|
export type OnTrackEntrySegment = (trackEntry: TrackEntry) => void;
|
|
7
|
-
export declare const expectSegment: (iterator
|
|
7
|
+
export declare const expectSegment: ({ iterator, parserContext, offset, children, }: {
|
|
8
|
+
iterator: BufferIterator;
|
|
9
|
+
parserContext: ParserContext;
|
|
10
|
+
offset: number;
|
|
11
|
+
children: PossibleEbml[];
|
|
12
|
+
}) => Promise<ExpectSegmentParseResult>;
|
|
@@ -3,26 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.expectSegment = void 0;
|
|
4
4
|
const parse_ebml_1 = require("./parse-ebml");
|
|
5
5
|
const parse_children_1 = require("./segments/parse-children");
|
|
6
|
-
const
|
|
7
|
-
if (
|
|
8
|
-
throw new Error(
|
|
6
|
+
const continueAfterMatroskaParseResult = async ({ result, iterator, parserContext, segment, }) => {
|
|
7
|
+
if (result.status === 'done') {
|
|
8
|
+
throw new Error('Should not continue after done');
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const proceeded = await result.continueParsing();
|
|
11
|
+
if (proceeded.status === 'done') {
|
|
12
|
+
return {
|
|
13
|
+
status: 'done',
|
|
14
|
+
segment,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
continueParsing() {
|
|
19
|
+
return continueAfterMatroskaParseResult({
|
|
20
|
+
result: proceeded,
|
|
21
|
+
iterator,
|
|
22
|
+
parserContext,
|
|
23
|
+
segment,
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
segment: null,
|
|
27
|
+
status: 'incomplete',
|
|
28
|
+
};
|
|
15
29
|
};
|
|
16
|
-
const expectSegment = async (iterator, parserContext) => {
|
|
17
|
-
|
|
30
|
+
const expectSegment = async ({ iterator, parserContext, offset, children, }) => {
|
|
31
|
+
iterator.counter.decrement(iterator.counter.getOffset() - offset);
|
|
18
32
|
if (iterator.bytesRemaining() === 0) {
|
|
19
33
|
return {
|
|
20
34
|
status: 'incomplete',
|
|
21
|
-
segments: [],
|
|
22
35
|
continueParsing: () => {
|
|
23
|
-
return
|
|
36
|
+
return (0, parse_children_1.expectAndProcessSegment)({
|
|
37
|
+
iterator,
|
|
38
|
+
parserContext,
|
|
39
|
+
offset,
|
|
40
|
+
children,
|
|
41
|
+
});
|
|
24
42
|
},
|
|
25
|
-
|
|
43
|
+
segment: null,
|
|
26
44
|
};
|
|
27
45
|
}
|
|
28
46
|
const segmentId = iterator.getMatroskaSegmentId();
|
|
@@ -30,11 +48,15 @@ const expectSegment = async (iterator, parserContext) => {
|
|
|
30
48
|
iterator.counter.decrement(iterator.counter.getOffset() - offset);
|
|
31
49
|
return {
|
|
32
50
|
status: 'incomplete',
|
|
33
|
-
segments: [],
|
|
34
51
|
continueParsing: () => {
|
|
35
|
-
return
|
|
52
|
+
return (0, parse_children_1.expectAndProcessSegment)({
|
|
53
|
+
iterator,
|
|
54
|
+
parserContext,
|
|
55
|
+
offset,
|
|
56
|
+
children,
|
|
57
|
+
});
|
|
36
58
|
},
|
|
37
|
-
|
|
59
|
+
segment: null,
|
|
38
60
|
};
|
|
39
61
|
}
|
|
40
62
|
const offsetBeforeVInt = iterator.counter.getOffset();
|
|
@@ -44,43 +66,43 @@ const expectSegment = async (iterator, parserContext) => {
|
|
|
44
66
|
iterator.counter.decrement(iterator.counter.getOffset() - offset);
|
|
45
67
|
return {
|
|
46
68
|
status: 'incomplete',
|
|
47
|
-
segments: [],
|
|
48
69
|
continueParsing: () => {
|
|
49
|
-
return
|
|
70
|
+
return (0, exports.expectSegment)({ iterator, parserContext, offset, children });
|
|
50
71
|
},
|
|
51
|
-
|
|
72
|
+
segment: null,
|
|
52
73
|
};
|
|
53
74
|
}
|
|
54
75
|
const bytesRemainingNow = iterator.byteLength() - iterator.counter.getOffset();
|
|
55
76
|
if (segmentId === '0x18538067' || segmentId === '0x1f43b675') {
|
|
77
|
+
const newSegment = {
|
|
78
|
+
type: segmentId === '0x18538067' ? 'Segment' : 'Cluster',
|
|
79
|
+
minVintWidth: offsetAfterVInt - offsetBeforeVInt,
|
|
80
|
+
value: [],
|
|
81
|
+
};
|
|
56
82
|
const main = await (0, parse_children_1.expectChildren)({
|
|
57
83
|
iterator,
|
|
58
84
|
length,
|
|
59
|
-
|
|
60
|
-
wrap: segmentId === '0x18538067'
|
|
61
|
-
? (s) => ({
|
|
62
|
-
type: 'Segment',
|
|
63
|
-
value: s,
|
|
64
|
-
minVintWidth: offsetAfterVInt - offsetBeforeVInt,
|
|
65
|
-
})
|
|
66
|
-
: (s) => ({
|
|
67
|
-
type: 'Cluster',
|
|
68
|
-
value: s,
|
|
69
|
-
minVintWidth: offsetAfterVInt - offsetBeforeVInt,
|
|
70
|
-
}),
|
|
85
|
+
children: newSegment.value,
|
|
71
86
|
parserContext,
|
|
87
|
+
startOffset: iterator.counter.getOffset(),
|
|
72
88
|
});
|
|
73
89
|
if (main.status === 'incomplete') {
|
|
74
90
|
return {
|
|
75
91
|
status: 'incomplete',
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
continueParsing: () => {
|
|
93
|
+
return continueAfterMatroskaParseResult({
|
|
94
|
+
iterator,
|
|
95
|
+
parserContext,
|
|
96
|
+
result: main,
|
|
97
|
+
segment: newSegment,
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
segment: newSegment,
|
|
79
101
|
};
|
|
80
102
|
}
|
|
81
103
|
return {
|
|
82
104
|
status: 'done',
|
|
83
|
-
|
|
105
|
+
segment: newSegment,
|
|
84
106
|
};
|
|
85
107
|
}
|
|
86
108
|
if (bytesRemainingNow < length) {
|
|
@@ -88,11 +110,10 @@ const expectSegment = async (iterator, parserContext) => {
|
|
|
88
110
|
iterator.counter.decrement(bytesRead);
|
|
89
111
|
return {
|
|
90
112
|
status: 'incomplete',
|
|
91
|
-
|
|
113
|
+
segment: null,
|
|
92
114
|
continueParsing: () => {
|
|
93
|
-
return
|
|
115
|
+
return (0, exports.expectSegment)({ iterator, parserContext, offset, children });
|
|
94
116
|
},
|
|
95
|
-
skipTo: null,
|
|
96
117
|
};
|
|
97
118
|
}
|
|
98
119
|
const segment = await parseSegment({
|
|
@@ -104,7 +125,17 @@ const expectSegment = async (iterator, parserContext) => {
|
|
|
104
125
|
});
|
|
105
126
|
return {
|
|
106
127
|
status: 'done',
|
|
107
|
-
|
|
128
|
+
segment,
|
|
108
129
|
};
|
|
109
130
|
};
|
|
110
131
|
exports.expectSegment = expectSegment;
|
|
132
|
+
const parseSegment = async ({ segmentId, iterator, length, parserContext, headerReadSoFar, }) => {
|
|
133
|
+
if (length < 0) {
|
|
134
|
+
throw new Error(`Expected length of ${segmentId} to be greater or equal 0`);
|
|
135
|
+
}
|
|
136
|
+
iterator.counter.decrement(headerReadSoFar);
|
|
137
|
+
const offset = iterator.counter.getOffset();
|
|
138
|
+
const ebml = await (0, parse_ebml_1.parseEbml)(iterator, parserContext);
|
|
139
|
+
const remapped = await (0, parse_ebml_1.postprocessEbml)({ offset, ebml, parserContext });
|
|
140
|
+
return remapped;
|
|
141
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
|
|
2
|
-
export declare const createIsoBaseMedia: ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, }: MediaFnGeneratorInput) => Promise<MediaFn>;
|
|
2
|
+
export declare const createIsoBaseMedia: ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, }: MediaFnGeneratorInput) => Promise<MediaFn>;
|
|
@@ -6,13 +6,13 @@ const log_1 = require("../../log");
|
|
|
6
6
|
const create_ftyp_1 = require("./create-ftyp");
|
|
7
7
|
const mp4_header_1 = require("./mp4-header");
|
|
8
8
|
const primitives_1 = require("./primitives");
|
|
9
|
-
const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, }) => {
|
|
9
|
+
const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, }) => {
|
|
10
10
|
const header = (0, create_ftyp_1.createIsoBaseMediaFtyp)({
|
|
11
11
|
compatibleBrands: ['isom', 'iso2', 'avc1', 'mp42'],
|
|
12
12
|
majorBrand: 'isom',
|
|
13
13
|
minorBrand: 512,
|
|
14
14
|
});
|
|
15
|
-
const w = await writer.createContent();
|
|
15
|
+
const w = await writer.createContent({ filename, mimeType: 'video/mp4' });
|
|
16
16
|
await w.write(header);
|
|
17
17
|
let durationInUnits = 0;
|
|
18
18
|
const currentTracks = [];
|
|
@@ -113,9 +113,8 @@ const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgr
|
|
|
113
113
|
};
|
|
114
114
|
const waitForFinishPromises = [];
|
|
115
115
|
return {
|
|
116
|
-
save:
|
|
117
|
-
|
|
118
|
-
return file;
|
|
116
|
+
save: () => {
|
|
117
|
+
return w.save();
|
|
119
118
|
},
|
|
120
119
|
remove: async () => {
|
|
121
120
|
await w.remove();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LogLevel } from '../../log';
|
|
1
2
|
import type { AudioOrVideoSample } from '../../webcodec-sample-types';
|
|
2
3
|
import type { Writer } from '../../writers/writer';
|
|
3
4
|
export declare const timestampToClusterTimestamp: (timestamp: number, timescale: number) => number;
|
|
@@ -6,7 +7,12 @@ export declare const canFitInCluster: ({ clusterStartTimestamp, chunk, timescale
|
|
|
6
7
|
chunk: AudioOrVideoSample;
|
|
7
8
|
timescale: number;
|
|
8
9
|
}) => boolean;
|
|
9
|
-
export declare const makeCluster: (
|
|
10
|
+
export declare const makeCluster: ({ writer, clusterStartTimestamp, timescale, logLevel, }: {
|
|
11
|
+
writer: Writer;
|
|
12
|
+
clusterStartTimestamp: number;
|
|
13
|
+
timescale: number;
|
|
14
|
+
logLevel: LogLevel;
|
|
15
|
+
}) => Promise<{
|
|
10
16
|
addSample: (chunk: AudioOrVideoSample, trackNumber: number) => Promise<{
|
|
11
17
|
timecodeRelativeToCluster: number;
|
|
12
18
|
}>;
|
|
@@ -4,6 +4,7 @@ exports.makeCluster = exports.canFitInCluster = exports.timestampToClusterTimest
|
|
|
4
4
|
const ebml_1 = require("../../boxes/webm/ebml");
|
|
5
5
|
const make_header_1 = require("../../boxes/webm/make-header");
|
|
6
6
|
const all_segments_1 = require("../../boxes/webm/segments/all-segments");
|
|
7
|
+
const log_1 = require("../../log");
|
|
7
8
|
const cluster_segment_1 = require("./cluster-segment");
|
|
8
9
|
const maxClusterTimestamp = 2 ** 15;
|
|
9
10
|
const timestampToClusterTimestamp = (timestamp, timescale) => {
|
|
@@ -19,15 +20,16 @@ const canFitInCluster = ({ clusterStartTimestamp, chunk, timescale, }) => {
|
|
|
19
20
|
return timecodeRelativeToCluster <= maxClusterTimestamp;
|
|
20
21
|
};
|
|
21
22
|
exports.canFitInCluster = canFitInCluster;
|
|
22
|
-
const makeCluster = async (
|
|
23
|
+
const makeCluster = async ({ writer, clusterStartTimestamp, timescale, logLevel, }) => {
|
|
24
|
+
log_1.Log.verbose(logLevel, `Making new Matroska cluster with timestamp ${clusterStartTimestamp}`);
|
|
23
25
|
const cluster = (0, cluster_segment_1.createClusterSegment)((0, exports.timestampToClusterTimestamp)(clusterStartTimestamp, timescale));
|
|
24
|
-
const clusterVIntPosition =
|
|
26
|
+
const clusterVIntPosition = writer.getWrittenByteCount() +
|
|
25
27
|
cluster.offsets.offset +
|
|
26
28
|
(0, make_header_1.matroskaToHex)(all_segments_1.matroskaElements.Cluster).byteLength;
|
|
27
29
|
let clusterSize = cluster.bytes.byteLength -
|
|
28
30
|
(0, make_header_1.matroskaToHex)(all_segments_1.matroskaElements.Cluster).byteLength -
|
|
29
31
|
cluster_segment_1.CLUSTER_MIN_VINT_WIDTH;
|
|
30
|
-
await
|
|
32
|
+
await writer.write(cluster.bytes);
|
|
31
33
|
const addSample = async (chunk, trackNumber) => {
|
|
32
34
|
const timecodeRelativeToCluster = (0, exports.timestampToClusterTimestamp)(chunk.timestamp, timescale) -
|
|
33
35
|
(0, exports.timestampToClusterTimestamp)(clusterStartTimestamp, timescale);
|
|
@@ -44,8 +46,8 @@ const makeCluster = async (w, clusterStartTimestamp, timescale) => {
|
|
|
44
46
|
timecodeRelativeToCluster,
|
|
45
47
|
});
|
|
46
48
|
clusterSize += simpleBlock.byteLength;
|
|
47
|
-
await
|
|
48
|
-
await
|
|
49
|
+
await writer.updateDataAt(clusterVIntPosition, (0, ebml_1.getVariableInt)(clusterSize, cluster_segment_1.CLUSTER_MIN_VINT_WIDTH));
|
|
50
|
+
await writer.write(simpleBlock);
|
|
49
51
|
return { timecodeRelativeToCluster };
|
|
50
52
|
};
|
|
51
53
|
const shouldMakeNewCluster = ({ isVideo, chunk, newT, }) => {
|
|
@@ -59,6 +61,7 @@ const makeCluster = async (w, clusterStartTimestamp, timescale) => {
|
|
|
59
61
|
if (!canFit) {
|
|
60
62
|
// We must create a new cluster
|
|
61
63
|
// This is for example if we have an audio-only file
|
|
64
|
+
log_1.Log.verbose(logLevel, `Cannot fit ${chunk.timestamp} in cluster ${clusterStartTimestamp}. Creating new cluster`);
|
|
62
65
|
return true;
|
|
63
66
|
}
|
|
64
67
|
const keyframe = chunk.type === 'key';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
|
|
2
|
-
export declare const createMatroskaMedia: ({ writer, onBytesProgress, onMillisecondsProgress, }: MediaFnGeneratorInput) => Promise<MediaFn>;
|
|
2
|
+
export declare const createMatroskaMedia: ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, }: MediaFnGeneratorInput) => Promise<MediaFn>;
|
|
@@ -13,10 +13,10 @@ const matroska_seek_1 = require("./matroska-seek");
|
|
|
13
13
|
const matroska_segment_1 = require("./matroska-segment");
|
|
14
14
|
const matroska_trackentry_1 = require("./matroska-trackentry");
|
|
15
15
|
const timescale = 1000000;
|
|
16
|
-
const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, }) => {
|
|
16
|
+
const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, }) => {
|
|
17
17
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
18
18
|
const header = (0, matroska_header_1.makeMatroskaHeader)();
|
|
19
|
-
const w = await writer.createContent();
|
|
19
|
+
const w = await writer.createContent({ filename, mimeType: 'video/webm' });
|
|
20
20
|
await w.write(header.bytes);
|
|
21
21
|
const matroskaInfo = (0, matroska_info_1.makeMatroskaInfo)({
|
|
22
22
|
timescale,
|
|
@@ -69,14 +69,27 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
|
|
|
69
69
|
};
|
|
70
70
|
await w.write(matroskaSegment.bytes);
|
|
71
71
|
const clusterOffset = w.getWrittenByteCount();
|
|
72
|
-
let currentCluster = await (0, cluster_1.makeCluster)(
|
|
72
|
+
let currentCluster = await (0, cluster_1.makeCluster)({
|
|
73
|
+
writer: w,
|
|
74
|
+
clusterStartTimestamp: 0,
|
|
75
|
+
timescale,
|
|
76
|
+
logLevel,
|
|
77
|
+
});
|
|
73
78
|
seeks.push({
|
|
74
79
|
hexString: all_segments_1.matroskaElements.Cluster,
|
|
75
80
|
byte: clusterOffset - seekHeadOffset,
|
|
76
81
|
});
|
|
77
82
|
const trackNumberProgresses = {};
|
|
78
|
-
const getClusterOrMakeNew = async ({ chunk, isVideo, }) => {
|
|
79
|
-
const
|
|
83
|
+
const getClusterOrMakeNew = async ({ chunk, isVideo, trackNumber, }) => {
|
|
84
|
+
const trackProgressValues = Object.values(trackNumberProgresses);
|
|
85
|
+
const smallestProgress = trackProgressValues.length === 0 ? 0 : Math.min(...trackProgressValues);
|
|
86
|
+
// In Safari, samples can arrive out of order, e.g public/bigbuckbunny.mp4
|
|
87
|
+
// Therefore, only updating track number progress if it is a keyframe
|
|
88
|
+
// to allow for timestamps to be lower than the previous one
|
|
89
|
+
// Also doing this AFTER smallestProgress is calculated
|
|
90
|
+
if (chunk.type === 'key') {
|
|
91
|
+
trackNumberProgresses[trackNumber] = chunk.timestamp;
|
|
92
|
+
}
|
|
80
93
|
if (!currentCluster.shouldMakeNewCluster({
|
|
81
94
|
newT: smallestProgress,
|
|
82
95
|
isVideo,
|
|
@@ -84,7 +97,12 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
|
|
|
84
97
|
})) {
|
|
85
98
|
return { cluster: currentCluster, isNew: false, smallestProgress };
|
|
86
99
|
}
|
|
87
|
-
currentCluster = await (0, cluster_1.makeCluster)(
|
|
100
|
+
currentCluster = await (0, cluster_1.makeCluster)({
|
|
101
|
+
writer: w,
|
|
102
|
+
clusterStartTimestamp: smallestProgress,
|
|
103
|
+
timescale,
|
|
104
|
+
logLevel,
|
|
105
|
+
});
|
|
88
106
|
return { cluster: currentCluster, isNew: true, smallestProgress };
|
|
89
107
|
};
|
|
90
108
|
const updateDuration = async (newDuration) => {
|
|
@@ -94,10 +112,10 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
|
|
|
94
112
|
};
|
|
95
113
|
const addSample = async ({ chunk, trackNumber, isVideo, }) => {
|
|
96
114
|
var _a;
|
|
97
|
-
trackNumberProgresses[trackNumber] = chunk.timestamp;
|
|
98
115
|
const { cluster, isNew, smallestProgress } = await getClusterOrMakeNew({
|
|
99
116
|
chunk,
|
|
100
117
|
isVideo,
|
|
118
|
+
trackNumber,
|
|
101
119
|
});
|
|
102
120
|
const newDuration = Math.round((chunk.timestamp + ((_a = chunk.duration) !== null && _a !== void 0 ? _a : 0)) / 1000);
|
|
103
121
|
await updateDuration(newDuration);
|
|
@@ -132,9 +150,8 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
|
|
|
132
150
|
}
|
|
133
151
|
});
|
|
134
152
|
},
|
|
135
|
-
save:
|
|
136
|
-
|
|
137
|
-
return file;
|
|
153
|
+
save: () => {
|
|
154
|
+
return w.save();
|
|
138
155
|
},
|
|
139
156
|
remove: async () => {
|
|
140
157
|
await w.remove();
|
|
@@ -3,7 +3,7 @@ import type { ParseResult } from './parse-result';
|
|
|
3
3
|
import type { ParserState } from './parser-state';
|
|
4
4
|
export declare const emitAvailableInfo: ({ hasInfo, parseResult, moreFields, state, returnValue, contentLength, name, }: {
|
|
5
5
|
hasInfo: Record<keyof Options<ParseMediaFields>, boolean>;
|
|
6
|
-
parseResult: ParseResult;
|
|
6
|
+
parseResult: ParseResult | null;
|
|
7
7
|
moreFields: ParseMediaCallbacks<AllParseMediaFields>;
|
|
8
8
|
state: ParserState;
|
|
9
9
|
returnValue: ParseMediaResult<AllParseMediaFields>;
|