mediabunny 1.0.2 → 1.0.4
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/README.md +4 -4
- package/dist/mediabunny.d.ts +4 -2
- package/dist/modules/codec-data.d.ts +95 -0
- package/dist/modules/codec-data.d.ts.map +1 -0
- package/dist/modules/codec-data.js +1103 -0
- package/dist/modules/codec.d.ts +250 -0
- package/dist/modules/codec.d.ts.map +1 -0
- package/dist/modules/codec.js +1100 -0
- package/dist/modules/conversion.d.ts +158 -0
- package/dist/modules/conversion.d.ts.map +1 -0
- package/dist/modules/conversion.js +866 -0
- package/dist/modules/custom-coder.d.ts +119 -0
- package/dist/modules/custom-coder.d.ts.map +1 -0
- package/dist/modules/custom-coder.js +91 -0
- package/dist/modules/demuxer.d.ts +17 -0
- package/dist/modules/demuxer.d.ts.map +1 -0
- package/dist/modules/demuxer.js +12 -0
- package/dist/modules/index.d.ts +25 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +24 -0
- package/dist/modules/input-format.d.ts +121 -0
- package/dist/modules/input-format.d.ts.map +1 -0
- package/dist/modules/input-format.js +333 -0
- package/dist/modules/input-track.d.ts +138 -0
- package/dist/modules/input-track.d.ts.map +1 -0
- package/dist/modules/input-track.js +230 -0
- package/dist/modules/input.d.ts +55 -0
- package/dist/modules/input.d.ts.map +1 -0
- package/dist/modules/input.js +104 -0
- package/dist/modules/isobmff/isobmff-boxes.d.ts +202 -0
- package/dist/modules/isobmff/isobmff-boxes.d.ts.map +1 -0
- package/dist/modules/isobmff/isobmff-boxes.js +1111 -0
- package/dist/modules/isobmff/isobmff-demuxer.d.ts +163 -0
- package/dist/modules/isobmff/isobmff-demuxer.d.ts.map +1 -0
- package/dist/modules/isobmff/isobmff-demuxer.js +2080 -0
- package/dist/modules/isobmff/isobmff-misc.d.ts +14 -0
- package/dist/modules/isobmff/isobmff-misc.d.ts.map +1 -0
- package/dist/modules/isobmff/isobmff-misc.js +20 -0
- package/dist/modules/isobmff/isobmff-muxer.d.ts +142 -0
- package/dist/modules/isobmff/isobmff-muxer.d.ts.map +1 -0
- package/dist/modules/isobmff/isobmff-muxer.js +858 -0
- package/dist/modules/isobmff/isobmff-reader.d.ts +36 -0
- package/dist/modules/isobmff/isobmff-reader.d.ts.map +1 -0
- package/dist/modules/isobmff/isobmff-reader.js +105 -0
- package/dist/modules/matroska/ebml.d.ts +156 -0
- package/dist/modules/matroska/ebml.d.ts.map +1 -0
- package/dist/modules/matroska/ebml.js +529 -0
- package/dist/modules/matroska/matroska-demuxer.d.ts +119 -0
- package/dist/modules/matroska/matroska-demuxer.d.ts.map +1 -0
- package/dist/modules/matroska/matroska-demuxer.js +1330 -0
- package/dist/modules/matroska/matroska-misc.d.ts +14 -0
- package/dist/modules/matroska/matroska-misc.d.ts.map +1 -0
- package/dist/modules/matroska/matroska-misc.js +20 -0
- package/dist/modules/matroska/matroska-muxer.d.ts +71 -0
- package/dist/modules/matroska/matroska-muxer.d.ts.map +1 -0
- package/dist/modules/matroska/matroska-muxer.js +729 -0
- package/dist/modules/media-sink.d.ts +272 -0
- package/dist/modules/media-sink.d.ts.map +1 -0
- package/dist/modules/media-sink.js +1264 -0
- package/dist/modules/media-source.d.ts +230 -0
- package/dist/modules/media-source.d.ts.map +1 -0
- package/dist/modules/media-source.js +1095 -0
- package/dist/modules/misc.d.ts +127 -0
- package/dist/modules/misc.d.ts.map +1 -0
- package/dist/modules/misc.js +462 -0
- package/dist/modules/mp3/mp3-demuxer.d.ts +32 -0
- package/dist/modules/mp3/mp3-demuxer.d.ts.map +1 -0
- package/dist/modules/mp3/mp3-demuxer.js +160 -0
- package/dist/modules/mp3/mp3-misc.d.ts +37 -0
- package/dist/modules/mp3/mp3-misc.d.ts.map +1 -0
- package/dist/modules/mp3/mp3-misc.js +122 -0
- package/dist/modules/mp3/mp3-muxer.d.ts +27 -0
- package/dist/modules/mp3/mp3-muxer.d.ts.map +1 -0
- package/dist/modules/mp3/mp3-muxer.js +112 -0
- package/dist/modules/mp3/mp3-reader.d.ts +25 -0
- package/dist/modules/mp3/mp3-reader.d.ts.map +1 -0
- package/dist/modules/mp3/mp3-reader.js +73 -0
- package/dist/modules/mp3/mp3-writer.d.ts +30 -0
- package/dist/modules/mp3/mp3-writer.d.ts.map +1 -0
- package/dist/modules/mp3/mp3-writer.js +72 -0
- package/dist/modules/muxer.d.ts +32 -0
- package/dist/modules/muxer.d.ts.map +1 -0
- package/dist/modules/muxer.js +50 -0
- package/dist/modules/ogg/ogg-demuxer.d.ts +53 -0
- package/dist/modules/ogg/ogg-demuxer.d.ts.map +1 -0
- package/dist/modules/ogg/ogg-demuxer.js +701 -0
- package/dist/modules/ogg/ogg-misc.d.ts +27 -0
- package/dist/modules/ogg/ogg-misc.d.ts.map +1 -0
- package/dist/modules/ogg/ogg-misc.js +78 -0
- package/dist/modules/ogg/ogg-muxer.d.ts +58 -0
- package/dist/modules/ogg/ogg-muxer.d.ts.map +1 -0
- package/dist/modules/ogg/ogg-muxer.js +345 -0
- package/dist/modules/ogg/ogg-reader.d.ts +37 -0
- package/dist/modules/ogg/ogg-reader.d.ts.map +1 -0
- package/dist/modules/ogg/ogg-reader.js +104 -0
- package/dist/modules/output-format.d.ts +285 -0
- package/dist/modules/output-format.d.ts.map +1 -0
- package/dist/modules/output-format.js +385 -0
- package/dist/modules/output.d.ts +134 -0
- package/dist/modules/output.d.ts.map +1 -0
- package/dist/modules/output.js +269 -0
- package/dist/modules/packet.d.ts +86 -0
- package/dist/modules/packet.d.ts.map +1 -0
- package/dist/modules/packet.js +133 -0
- package/dist/modules/pcm.d.ts +12 -0
- package/dist/modules/pcm.d.ts.map +1 -0
- package/dist/modules/pcm.js +85 -0
- package/dist/modules/reader.d.ts +40 -0
- package/dist/modules/reader.d.ts.map +1 -0
- package/dist/modules/reader.js +153 -0
- package/dist/modules/sample.d.ts +186 -0
- package/dist/modules/sample.d.ts.map +1 -0
- package/dist/modules/sample.js +798 -0
- package/dist/modules/source.d.ts +76 -0
- package/dist/modules/source.d.ts.map +1 -0
- package/dist/modules/source.js +183 -0
- package/dist/modules/subtitles.d.ts +37 -0
- package/dist/modules/subtitles.d.ts.map +1 -0
- package/dist/modules/subtitles.js +89 -0
- package/dist/modules/target.d.ts +58 -0
- package/dist/modules/target.d.ts.map +1 -0
- package/dist/modules/target.js +63 -0
- package/dist/modules/wave/riff-reader.d.ts +19 -0
- package/dist/modules/wave/riff-reader.d.ts.map +1 -0
- package/dist/modules/wave/riff-reader.js +38 -0
- package/dist/modules/wave/riff-writer.d.ts +18 -0
- package/dist/modules/wave/riff-writer.d.ts.map +1 -0
- package/dist/modules/wave/riff-writer.js +25 -0
- package/dist/modules/wave/wave-demuxer.d.ts +42 -0
- package/dist/modules/wave/wave-demuxer.d.ts.map +1 -0
- package/dist/modules/wave/wave-demuxer.js +239 -0
- package/dist/modules/wave/wave-muxer.d.ts +27 -0
- package/dist/modules/wave/wave-muxer.d.ts.map +1 -0
- package/dist/modules/wave/wave-muxer.js +108 -0
- package/dist/modules/writer.d.ts +85 -0
- package/dist/modules/writer.d.ts.map +1 -0
- package/dist/modules/writer.js +347 -0
- package/package.json +14 -10
- package/src/codec-data.ts +1394 -0
- package/src/codec.ts +1392 -0
- package/src/conversion.ts +1199 -0
- package/src/custom-coder.ts +162 -0
- package/src/demuxer.ts +22 -0
- package/src/index.ts +148 -0
- package/src/input-format.ts +386 -0
- package/src/input-track.ts +341 -0
- package/src/input.ts +138 -0
- package/src/isobmff/isobmff-boxes.ts +1365 -0
- package/src/isobmff/isobmff-demuxer.ts +2646 -0
- package/src/isobmff/isobmff-misc.ts +29 -0
- package/src/isobmff/isobmff-muxer.ts +1210 -0
- package/src/isobmff/isobmff-reader.ts +137 -0
- package/src/matroska/ebml.ts +595 -0
- package/src/matroska/matroska-demuxer.ts +1677 -0
- package/src/matroska/matroska-misc.ts +29 -0
- package/src/matroska/matroska-muxer.ts +999 -0
- package/src/media-sink.ts +1577 -0
- package/src/media-source.ts +1406 -0
- package/src/misc.ts +584 -0
- package/src/mp3/mp3-demuxer.ts +228 -0
- package/src/mp3/mp3-misc.ts +154 -0
- package/src/mp3/mp3-muxer.ts +153 -0
- package/src/mp3/mp3-reader.ts +93 -0
- package/src/mp3/mp3-writer.ts +112 -0
- package/src/muxer.ts +88 -0
- package/src/ogg/ogg-demuxer.ts +985 -0
- package/src/ogg/ogg-misc.ts +116 -0
- package/src/ogg/ogg-muxer.ts +480 -0
- package/src/ogg/ogg-reader.ts +143 -0
- package/src/output-format.ts +689 -0
- package/src/output.ts +427 -0
- package/src/packet.ts +180 -0
- package/src/pcm.ts +112 -0
- package/src/reader.ts +205 -0
- package/src/sample.ts +1029 -0
- package/src/source.ts +279 -0
- package/src/subtitles.ts +131 -0
- package/src/target.ts +106 -0
- package/src/tsconfig.json +13 -0
- package/src/wave/riff-reader.ts +48 -0
- package/src/wave/riff-writer.ts +30 -0
- package/src/wave/wave-demuxer.ts +317 -0
- package/src/wave/wave-muxer.ts +145 -0
- package/src/writer.ts +461 -0
- /package/dist/{mediabunny.js → bundles/mediabunny.cjs} +0 -0
- /package/dist/{mediabunny.min.js → bundles/mediabunny.min.cjs} +0 -0
- /package/dist/{mediabunny.min.mjs → bundles/mediabunny.min.mjs} +0 -0
- /package/dist/{mediabunny.mjs → bundles/mediabunny.mjs} +0 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
3
|
+
*
|
|
4
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { MediaCodec } from '../codec';
|
|
10
|
+
import { Reader } from '../reader';
|
|
11
|
+
import { Writer } from '../writer';
|
|
12
|
+
|
|
13
|
+
export interface EBMLElement {
|
|
14
|
+
id: number;
|
|
15
|
+
size?: number;
|
|
16
|
+
data: number | string | Uint8Array | EBMLFloat32 | EBMLFloat64 | EBMLSignedInt | (EBML | null)[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type EBML = EBMLElement | Uint8Array | (EBML | null)[];
|
|
20
|
+
|
|
21
|
+
/** Wrapper around a number to be able to differentiate it in the writer. */
|
|
22
|
+
export class EBMLFloat32 {
|
|
23
|
+
value: number;
|
|
24
|
+
|
|
25
|
+
constructor(value: number) {
|
|
26
|
+
this.value = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Wrapper around a number to be able to differentiate it in the writer. */
|
|
31
|
+
export class EBMLFloat64 {
|
|
32
|
+
value: number;
|
|
33
|
+
|
|
34
|
+
constructor(value: number) {
|
|
35
|
+
this.value = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Wrapper around a number to be able to differentiate it in the writer. */
|
|
40
|
+
export class EBMLSignedInt {
|
|
41
|
+
value: number;
|
|
42
|
+
|
|
43
|
+
constructor(value: number) {
|
|
44
|
+
this.value = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Defines some of the EBML IDs used by Matroska files. */
|
|
49
|
+
export enum EBMLId {
|
|
50
|
+
EBML = 0x1a45dfa3,
|
|
51
|
+
EBMLVersion = 0x4286,
|
|
52
|
+
EBMLReadVersion = 0x42f7,
|
|
53
|
+
EBMLMaxIDLength = 0x42f2,
|
|
54
|
+
EBMLMaxSizeLength = 0x42f3,
|
|
55
|
+
DocType = 0x4282,
|
|
56
|
+
DocTypeVersion = 0x4287,
|
|
57
|
+
DocTypeReadVersion = 0x4285,
|
|
58
|
+
SeekHead = 0x114d9b74,
|
|
59
|
+
Seek = 0x4dbb,
|
|
60
|
+
SeekID = 0x53ab,
|
|
61
|
+
SeekPosition = 0x53ac,
|
|
62
|
+
Duration = 0x4489,
|
|
63
|
+
Info = 0x1549a966,
|
|
64
|
+
TimestampScale = 0x2ad7b1,
|
|
65
|
+
MuxingApp = 0x4d80,
|
|
66
|
+
WritingApp = 0x5741,
|
|
67
|
+
Tracks = 0x1654ae6b,
|
|
68
|
+
TrackEntry = 0xae,
|
|
69
|
+
TrackNumber = 0xd7,
|
|
70
|
+
TrackUID = 0x73c5,
|
|
71
|
+
TrackType = 0x83,
|
|
72
|
+
FlagEnabled = 0xb9,
|
|
73
|
+
FlagDefault = 0x88,
|
|
74
|
+
FlagForced = 0x55aa,
|
|
75
|
+
FlagLacing = 0x9c,
|
|
76
|
+
Language = 0x22b59c,
|
|
77
|
+
CodecID = 0x86,
|
|
78
|
+
CodecPrivate = 0x63a2,
|
|
79
|
+
CodecDelay = 0x56aa,
|
|
80
|
+
SeekPreRoll = 0x56bb,
|
|
81
|
+
DefaultDuration = 0x23e383,
|
|
82
|
+
Video = 0xe0,
|
|
83
|
+
PixelWidth = 0xb0,
|
|
84
|
+
PixelHeight = 0xba,
|
|
85
|
+
Audio = 0xe1,
|
|
86
|
+
SamplingFrequency = 0xb5,
|
|
87
|
+
Channels = 0x9f,
|
|
88
|
+
BitDepth = 0x6264,
|
|
89
|
+
Segment = 0x18538067,
|
|
90
|
+
SimpleBlock = 0xa3,
|
|
91
|
+
BlockGroup = 0xa0,
|
|
92
|
+
Block = 0xa1,
|
|
93
|
+
BlockAdditions = 0x75a1,
|
|
94
|
+
BlockMore = 0xa6,
|
|
95
|
+
BlockAdditional = 0xa5,
|
|
96
|
+
BlockAddID = 0xee,
|
|
97
|
+
BlockDuration = 0x9b,
|
|
98
|
+
ReferenceBlock = 0xfb,
|
|
99
|
+
Cluster = 0x1f43b675,
|
|
100
|
+
Timestamp = 0xe7,
|
|
101
|
+
Cues = 0x1c53bb6b,
|
|
102
|
+
CuePoint = 0xbb,
|
|
103
|
+
CueTime = 0xb3,
|
|
104
|
+
CueTrackPositions = 0xb7,
|
|
105
|
+
CueTrack = 0xf7,
|
|
106
|
+
CueClusterPosition = 0xf1,
|
|
107
|
+
Colour = 0x55b0,
|
|
108
|
+
MatrixCoefficients = 0x55b1,
|
|
109
|
+
TransferCharacteristics = 0x55ba,
|
|
110
|
+
Primaries = 0x55bb,
|
|
111
|
+
Range = 0x55b9,
|
|
112
|
+
Projection = 0x7670,
|
|
113
|
+
ProjectionType = 0x7671,
|
|
114
|
+
ProjectionPoseRoll = 0x7675,
|
|
115
|
+
Attachments = 0x1941a469,
|
|
116
|
+
Chapters = 0x1043a770,
|
|
117
|
+
Tags = 0x1254c367,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const LEVEL_0_EBML_IDS: EBMLId[] = [
|
|
121
|
+
EBMLId.EBML,
|
|
122
|
+
EBMLId.Segment,
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
export const LEVEL_1_EBML_IDS: EBMLId[] = [
|
|
126
|
+
EBMLId.EBMLMaxIDLength,
|
|
127
|
+
EBMLId.EBMLMaxSizeLength,
|
|
128
|
+
EBMLId.SeekHead,
|
|
129
|
+
EBMLId.Info,
|
|
130
|
+
EBMLId.Cluster,
|
|
131
|
+
EBMLId.Tracks,
|
|
132
|
+
EBMLId.Cues,
|
|
133
|
+
EBMLId.Attachments,
|
|
134
|
+
EBMLId.Chapters,
|
|
135
|
+
EBMLId.Tags,
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
export const LEVEL_0_AND_1_EBML_IDS = [
|
|
139
|
+
...LEVEL_0_EBML_IDS,
|
|
140
|
+
...LEVEL_1_EBML_IDS,
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
export const measureUnsignedInt = (value: number) => {
|
|
144
|
+
if (value < (1 << 8)) {
|
|
145
|
+
return 1;
|
|
146
|
+
} else if (value < (1 << 16)) {
|
|
147
|
+
return 2;
|
|
148
|
+
} else if (value < (1 << 24)) {
|
|
149
|
+
return 3;
|
|
150
|
+
} else if (value < 2 ** 32) {
|
|
151
|
+
return 4;
|
|
152
|
+
} else if (value < 2 ** 40) {
|
|
153
|
+
return 5;
|
|
154
|
+
} else {
|
|
155
|
+
return 6;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const measureSignedInt = (value: number) => {
|
|
160
|
+
if (value >= -(1 << 6) && value < (1 << 6)) {
|
|
161
|
+
return 1;
|
|
162
|
+
} else if (value >= -(1 << 13) && value < (1 << 13)) {
|
|
163
|
+
return 2;
|
|
164
|
+
} else if (value >= -(1 << 20) && value < (1 << 20)) {
|
|
165
|
+
return 3;
|
|
166
|
+
} else if (value >= -(1 << 27) && value < (1 << 27)) {
|
|
167
|
+
return 4;
|
|
168
|
+
} else if (value >= -(2 ** 34) && value < 2 ** 34) {
|
|
169
|
+
return 5;
|
|
170
|
+
} else {
|
|
171
|
+
return 6;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const measureVarInt = (value: number) => {
|
|
176
|
+
if (value < (1 << 7) - 1) {
|
|
177
|
+
/** Top bit is set, leaving 7 bits to hold the integer, but we can't store
|
|
178
|
+
* 127 because "all bits set to one" is a reserved value. Same thing for the
|
|
179
|
+
* other cases below:
|
|
180
|
+
*/
|
|
181
|
+
return 1;
|
|
182
|
+
} else if (value < (1 << 14) - 1) {
|
|
183
|
+
return 2;
|
|
184
|
+
} else if (value < (1 << 21) - 1) {
|
|
185
|
+
return 3;
|
|
186
|
+
} else if (value < (1 << 28) - 1) {
|
|
187
|
+
return 4;
|
|
188
|
+
} else if (value < 2 ** 35 - 1) {
|
|
189
|
+
return 5;
|
|
190
|
+
} else if (value < 2 ** 42 - 1) {
|
|
191
|
+
return 6;
|
|
192
|
+
} else {
|
|
193
|
+
throw new Error('EBML varint size not supported ' + value);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export class EBMLWriter {
|
|
198
|
+
helper = new Uint8Array(8);
|
|
199
|
+
helperView = new DataView(this.helper.buffer);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Stores the position from the start of the file to where EBML elements have been written. This is used to
|
|
203
|
+
* rewrite/edit elements that were already added before, and to measure sizes of things.
|
|
204
|
+
*/
|
|
205
|
+
offsets = new WeakMap<EBML, number>();
|
|
206
|
+
/** Same as offsets, but stores position where the element's data starts (after ID and size fields). */
|
|
207
|
+
dataOffsets = new WeakMap<EBML, number>();
|
|
208
|
+
|
|
209
|
+
constructor(private writer: Writer) {}
|
|
210
|
+
|
|
211
|
+
writeByte(value: number) {
|
|
212
|
+
this.helperView.setUint8(0, value);
|
|
213
|
+
this.writer.write(this.helper.subarray(0, 1));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
writeFloat32(value: number) {
|
|
217
|
+
this.helperView.setFloat32(0, value, false);
|
|
218
|
+
this.writer.write(this.helper.subarray(0, 4));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
writeFloat64(value: number) {
|
|
222
|
+
this.helperView.setFloat64(0, value, false);
|
|
223
|
+
this.writer.write(this.helper);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
writeUnsignedInt(value: number, width = measureUnsignedInt(value)) {
|
|
227
|
+
let pos = 0;
|
|
228
|
+
|
|
229
|
+
// Each case falls through:
|
|
230
|
+
switch (width) {
|
|
231
|
+
case 6:
|
|
232
|
+
// Need to use division to access >32 bits of floating point var
|
|
233
|
+
this.helperView.setUint8(pos++, (value / 2 ** 40) | 0);
|
|
234
|
+
// eslint-disable-next-line no-fallthrough
|
|
235
|
+
case 5:
|
|
236
|
+
this.helperView.setUint8(pos++, (value / 2 ** 32) | 0);
|
|
237
|
+
// eslint-disable-next-line no-fallthrough
|
|
238
|
+
case 4:
|
|
239
|
+
this.helperView.setUint8(pos++, value >> 24);
|
|
240
|
+
// eslint-disable-next-line no-fallthrough
|
|
241
|
+
case 3:
|
|
242
|
+
this.helperView.setUint8(pos++, value >> 16);
|
|
243
|
+
// eslint-disable-next-line no-fallthrough
|
|
244
|
+
case 2:
|
|
245
|
+
this.helperView.setUint8(pos++, value >> 8);
|
|
246
|
+
// eslint-disable-next-line no-fallthrough
|
|
247
|
+
case 1:
|
|
248
|
+
this.helperView.setUint8(pos++, value);
|
|
249
|
+
break;
|
|
250
|
+
default:
|
|
251
|
+
throw new Error('Bad unsigned int size ' + width);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.writer.write(this.helper.subarray(0, pos));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
writeSignedInt(value: number, width = measureSignedInt(value)) {
|
|
258
|
+
if (value < 0) {
|
|
259
|
+
// Two's complement stuff
|
|
260
|
+
value += 2 ** (width * 8);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.writeUnsignedInt(value, width);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
writeVarInt(value: number, width = measureVarInt(value)) {
|
|
267
|
+
let pos = 0;
|
|
268
|
+
|
|
269
|
+
switch (width) {
|
|
270
|
+
case 1:
|
|
271
|
+
this.helperView.setUint8(pos++, (1 << 7) | value);
|
|
272
|
+
break;
|
|
273
|
+
case 2:
|
|
274
|
+
this.helperView.setUint8(pos++, (1 << 6) | (value >> 8));
|
|
275
|
+
this.helperView.setUint8(pos++, value);
|
|
276
|
+
break;
|
|
277
|
+
case 3:
|
|
278
|
+
this.helperView.setUint8(pos++, (1 << 5) | (value >> 16));
|
|
279
|
+
this.helperView.setUint8(pos++, value >> 8);
|
|
280
|
+
this.helperView.setUint8(pos++, value);
|
|
281
|
+
break;
|
|
282
|
+
case 4:
|
|
283
|
+
this.helperView.setUint8(pos++, (1 << 4) | (value >> 24));
|
|
284
|
+
this.helperView.setUint8(pos++, value >> 16);
|
|
285
|
+
this.helperView.setUint8(pos++, value >> 8);
|
|
286
|
+
this.helperView.setUint8(pos++, value);
|
|
287
|
+
break;
|
|
288
|
+
case 5:
|
|
289
|
+
/**
|
|
290
|
+
* JavaScript converts its doubles to 32-bit integers for bitwise
|
|
291
|
+
* operations, so we need to do a division by 2^32 instead of a
|
|
292
|
+
* right-shift of 32 to retain those top 3 bits
|
|
293
|
+
*/
|
|
294
|
+
this.helperView.setUint8(pos++, (1 << 3) | ((value / 2 ** 32) & 0x7));
|
|
295
|
+
this.helperView.setUint8(pos++, value >> 24);
|
|
296
|
+
this.helperView.setUint8(pos++, value >> 16);
|
|
297
|
+
this.helperView.setUint8(pos++, value >> 8);
|
|
298
|
+
this.helperView.setUint8(pos++, value);
|
|
299
|
+
break;
|
|
300
|
+
case 6:
|
|
301
|
+
this.helperView.setUint8(pos++, (1 << 2) | ((value / 2 ** 40) & 0x3));
|
|
302
|
+
this.helperView.setUint8(pos++, (value / 2 ** 32) | 0);
|
|
303
|
+
this.helperView.setUint8(pos++, value >> 24);
|
|
304
|
+
this.helperView.setUint8(pos++, value >> 16);
|
|
305
|
+
this.helperView.setUint8(pos++, value >> 8);
|
|
306
|
+
this.helperView.setUint8(pos++, value);
|
|
307
|
+
break;
|
|
308
|
+
default:
|
|
309
|
+
throw new Error('Bad EBML varint size ' + width);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
this.writer.write(this.helper.subarray(0, pos));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Assumes the string is ASCII
|
|
316
|
+
writeString(str: string) {
|
|
317
|
+
this.writer.write(new Uint8Array(str.split('').map(x => x.charCodeAt(0))));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
writeEBML(data: EBML | null) {
|
|
321
|
+
if (data === null) return;
|
|
322
|
+
|
|
323
|
+
if (data instanceof Uint8Array) {
|
|
324
|
+
this.writer.write(data);
|
|
325
|
+
} else if (Array.isArray(data)) {
|
|
326
|
+
for (const elem of data) {
|
|
327
|
+
this.writeEBML(elem);
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
this.offsets.set(data, this.writer.getPos());
|
|
331
|
+
|
|
332
|
+
this.writeUnsignedInt(data.id); // ID field
|
|
333
|
+
|
|
334
|
+
if (Array.isArray(data.data)) {
|
|
335
|
+
const sizePos = this.writer.getPos();
|
|
336
|
+
const sizeSize = data.size === -1 ? 1 : (data.size ?? 4);
|
|
337
|
+
|
|
338
|
+
if (data.size === -1) {
|
|
339
|
+
// Write the reserved all-one-bits marker for unknown/unbounded size.
|
|
340
|
+
this.writeByte(0xff);
|
|
341
|
+
} else {
|
|
342
|
+
this.writer.seek(this.writer.getPos() + sizeSize);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const startPos = this.writer.getPos();
|
|
346
|
+
this.dataOffsets.set(data, startPos);
|
|
347
|
+
this.writeEBML(data.data);
|
|
348
|
+
|
|
349
|
+
if (data.size !== -1) {
|
|
350
|
+
const size = this.writer.getPos() - startPos;
|
|
351
|
+
const endPos = this.writer.getPos();
|
|
352
|
+
this.writer.seek(sizePos);
|
|
353
|
+
this.writeVarInt(size, sizeSize);
|
|
354
|
+
this.writer.seek(endPos);
|
|
355
|
+
}
|
|
356
|
+
} else if (typeof data.data === 'number') {
|
|
357
|
+
const size = data.size ?? measureUnsignedInt(data.data);
|
|
358
|
+
this.writeVarInt(size);
|
|
359
|
+
this.writeUnsignedInt(data.data, size);
|
|
360
|
+
} else if (typeof data.data === 'string') {
|
|
361
|
+
this.writeVarInt(data.data.length);
|
|
362
|
+
this.writeString(data.data);
|
|
363
|
+
} else if (data.data instanceof Uint8Array) {
|
|
364
|
+
this.writeVarInt(data.data.byteLength, data.size);
|
|
365
|
+
this.writer.write(data.data);
|
|
366
|
+
} else if (data.data instanceof EBMLFloat32) {
|
|
367
|
+
this.writeVarInt(4);
|
|
368
|
+
this.writeFloat32(data.data.value);
|
|
369
|
+
} else if (data.data instanceof EBMLFloat64) {
|
|
370
|
+
this.writeVarInt(8);
|
|
371
|
+
this.writeFloat64(data.data.value);
|
|
372
|
+
} else if (data.data instanceof EBMLSignedInt) {
|
|
373
|
+
const size = data.size ?? measureSignedInt(data.data.value);
|
|
374
|
+
this.writeVarInt(size);
|
|
375
|
+
this.writeSignedInt(data.data.value, size);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const MAX_VAR_INT_SIZE = 8;
|
|
382
|
+
export const MIN_HEADER_SIZE = 2; // 1-byte ID and 1-byte size
|
|
383
|
+
export const MAX_HEADER_SIZE = 4 + MAX_VAR_INT_SIZE; // 4-byte ID and 8-byte size
|
|
384
|
+
|
|
385
|
+
export class EBMLReader {
|
|
386
|
+
pos = 0;
|
|
387
|
+
|
|
388
|
+
constructor(public reader: Reader) {}
|
|
389
|
+
|
|
390
|
+
readBytes(length: number) {
|
|
391
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
|
|
392
|
+
this.pos += length;
|
|
393
|
+
|
|
394
|
+
return new Uint8Array(view.buffer, offset, length);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
readU8() {
|
|
398
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1);
|
|
399
|
+
this.pos++;
|
|
400
|
+
|
|
401
|
+
return view.getUint8(offset);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
readS16() {
|
|
405
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 2);
|
|
406
|
+
this.pos += 2;
|
|
407
|
+
|
|
408
|
+
return view.getInt16(offset, false);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
readVarIntSize() {
|
|
412
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1);
|
|
413
|
+
const firstByte = view.getUint8(offset);
|
|
414
|
+
|
|
415
|
+
let width = 1;
|
|
416
|
+
let mask = 0x80;
|
|
417
|
+
while ((firstByte & mask) === 0 && width < 8) {
|
|
418
|
+
width++;
|
|
419
|
+
mask >>= 1;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return width;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
readVarInt() {
|
|
426
|
+
// Read the first byte to determine the width of the variable-length integer
|
|
427
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1);
|
|
428
|
+
const firstByte = view.getUint8(offset);
|
|
429
|
+
|
|
430
|
+
// Find the position of VINT_MARKER, which determines the width
|
|
431
|
+
let width = 1;
|
|
432
|
+
let mask = 1 << 7;
|
|
433
|
+
while ((firstByte & mask) === 0 && width < MAX_VAR_INT_SIZE) {
|
|
434
|
+
width++;
|
|
435
|
+
mask >>= 1;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const { view: fullView, offset: fullOffset } = this.reader.getViewAndOffset(this.pos, this.pos + width);
|
|
439
|
+
|
|
440
|
+
// First byte's value needs the marker bit cleared
|
|
441
|
+
let value = firstByte & (mask - 1);
|
|
442
|
+
|
|
443
|
+
// Read remaining bytes
|
|
444
|
+
for (let i = 1; i < width; i++) {
|
|
445
|
+
value *= 1 << 8;
|
|
446
|
+
value += fullView.getUint8(fullOffset + i);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
this.pos += width;
|
|
450
|
+
return value;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
readUnsignedInt(width: number) {
|
|
454
|
+
if (width < 1 || width > 8) {
|
|
455
|
+
throw new Error('Bad unsigned int size ' + width);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + width);
|
|
459
|
+
let value = 0;
|
|
460
|
+
|
|
461
|
+
// Read bytes from most significant to least significant
|
|
462
|
+
for (let i = 0; i < width; i++) {
|
|
463
|
+
value *= 1 << 8;
|
|
464
|
+
value += view.getUint8(offset + i);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
this.pos += width;
|
|
468
|
+
return value;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
readSignedInt(width: number) {
|
|
472
|
+
let value = this.readUnsignedInt(width);
|
|
473
|
+
|
|
474
|
+
// If the highest bit is set, convert from two's complement
|
|
475
|
+
if (value & (1 << (width * 8 - 1))) {
|
|
476
|
+
value -= 2 ** (width * 8);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
readFloat(width: number) {
|
|
483
|
+
if (width === 0) {
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (width !== 4 && width !== 8) {
|
|
488
|
+
throw new Error('Bad float size ' + width);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + width);
|
|
492
|
+
const value = width === 4 ? view.getFloat32(offset, false) : view.getFloat64(offset, false);
|
|
493
|
+
|
|
494
|
+
this.pos += width;
|
|
495
|
+
return value;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
readString(length: number) {
|
|
499
|
+
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
|
|
500
|
+
this.pos += length;
|
|
501
|
+
|
|
502
|
+
return String.fromCharCode(...new Uint8Array(view.buffer, offset, length));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
readElementId() {
|
|
506
|
+
const size = this.readVarIntSize();
|
|
507
|
+
const id = this.readUnsignedInt(size);
|
|
508
|
+
|
|
509
|
+
return id;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
readElementSize() {
|
|
513
|
+
let size: number | null = this.readU8();
|
|
514
|
+
|
|
515
|
+
if (size === 0xff) {
|
|
516
|
+
size = null;
|
|
517
|
+
} else {
|
|
518
|
+
this.pos--;
|
|
519
|
+
size = this.readVarInt();
|
|
520
|
+
|
|
521
|
+
// In some (livestreamed) files, this is the value of the size field. While this technically is just a very
|
|
522
|
+
// large number, it is intended to behave like the reserved size 0xFF, meaning the size is undefined. We
|
|
523
|
+
// catch the number here. Note that it cannot be perfectly represented as a double, but the comparison works
|
|
524
|
+
// nonetheless.
|
|
525
|
+
// eslint-disable-next-line no-loss-of-precision
|
|
526
|
+
if (size === 0x00ffffffffffffff) {
|
|
527
|
+
size = null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return size;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
readElementHeader() {
|
|
535
|
+
const id = this.readElementId();
|
|
536
|
+
const size = this.readElementSize();
|
|
537
|
+
|
|
538
|
+
return { id, size };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** Returns the byte offset in the file of the next element with a matching ID. */
|
|
542
|
+
async searchForNextElementId(ids: EBMLId[], until: number) {
|
|
543
|
+
const loadChunkSize = 2 ** 20; // 1 MiB
|
|
544
|
+
const idsSet = new Set(ids);
|
|
545
|
+
|
|
546
|
+
while (this.pos < until - MAX_HEADER_SIZE) {
|
|
547
|
+
if (!this.reader.rangeIsLoaded(this.pos, this.pos + MAX_HEADER_SIZE)) {
|
|
548
|
+
await this.reader.loadRange(this.pos, Math.min(this.pos + loadChunkSize, until));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const elementStartPos = this.pos;
|
|
552
|
+
const elementHeader = this.readElementHeader();
|
|
553
|
+
if (idsSet.has(elementHeader.id)) {
|
|
554
|
+
return elementStartPos;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
assertDefinedSize(elementHeader.size);
|
|
558
|
+
|
|
559
|
+
this.pos += elementHeader.size;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export const CODEC_STRING_MAP: Partial<Record<MediaCodec, string>> = {
|
|
567
|
+
'avc': 'V_MPEG4/ISO/AVC',
|
|
568
|
+
'hevc': 'V_MPEGH/ISO/HEVC',
|
|
569
|
+
'vp8': 'V_VP8',
|
|
570
|
+
'vp9': 'V_VP9',
|
|
571
|
+
'av1': 'V_AV1',
|
|
572
|
+
|
|
573
|
+
'aac': 'A_AAC',
|
|
574
|
+
'mp3': 'A_MPEG/L3',
|
|
575
|
+
'opus': 'A_OPUS',
|
|
576
|
+
'vorbis': 'A_VORBIS',
|
|
577
|
+
'flac': 'A_FLAC',
|
|
578
|
+
'pcm-u8': 'A_PCM/INT/LIT',
|
|
579
|
+
'pcm-s16': 'A_PCM/INT/LIT',
|
|
580
|
+
'pcm-s16be': 'A_PCM/INT/BIG',
|
|
581
|
+
'pcm-s24': 'A_PCM/INT/LIT',
|
|
582
|
+
'pcm-s24be': 'A_PCM/INT/BIG',
|
|
583
|
+
'pcm-s32': 'A_PCM/INT/LIT',
|
|
584
|
+
'pcm-s32be': 'A_PCM/INT/BIG',
|
|
585
|
+
'pcm-f32': 'A_PCM/FLOAT/IEEE',
|
|
586
|
+
'pcm-f64': 'A_PCM/FLOAT/IEEE',
|
|
587
|
+
|
|
588
|
+
'webvtt': 'S_TEXT/WEBVTT',
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
export function assertDefinedSize(size: number | null): asserts size is number {
|
|
592
|
+
if (size === null) {
|
|
593
|
+
throw new Error('Undefined element size is used in a place where it is not supported.');
|
|
594
|
+
}
|
|
595
|
+
};
|