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.
Files changed (188) hide show
  1. package/README.md +4 -4
  2. package/dist/mediabunny.d.ts +4 -2
  3. package/dist/modules/codec-data.d.ts +95 -0
  4. package/dist/modules/codec-data.d.ts.map +1 -0
  5. package/dist/modules/codec-data.js +1103 -0
  6. package/dist/modules/codec.d.ts +250 -0
  7. package/dist/modules/codec.d.ts.map +1 -0
  8. package/dist/modules/codec.js +1100 -0
  9. package/dist/modules/conversion.d.ts +158 -0
  10. package/dist/modules/conversion.d.ts.map +1 -0
  11. package/dist/modules/conversion.js +866 -0
  12. package/dist/modules/custom-coder.d.ts +119 -0
  13. package/dist/modules/custom-coder.d.ts.map +1 -0
  14. package/dist/modules/custom-coder.js +91 -0
  15. package/dist/modules/demuxer.d.ts +17 -0
  16. package/dist/modules/demuxer.d.ts.map +1 -0
  17. package/dist/modules/demuxer.js +12 -0
  18. package/dist/modules/index.d.ts +25 -0
  19. package/dist/modules/index.d.ts.map +1 -0
  20. package/dist/modules/index.js +24 -0
  21. package/dist/modules/input-format.d.ts +121 -0
  22. package/dist/modules/input-format.d.ts.map +1 -0
  23. package/dist/modules/input-format.js +333 -0
  24. package/dist/modules/input-track.d.ts +138 -0
  25. package/dist/modules/input-track.d.ts.map +1 -0
  26. package/dist/modules/input-track.js +230 -0
  27. package/dist/modules/input.d.ts +55 -0
  28. package/dist/modules/input.d.ts.map +1 -0
  29. package/dist/modules/input.js +104 -0
  30. package/dist/modules/isobmff/isobmff-boxes.d.ts +202 -0
  31. package/dist/modules/isobmff/isobmff-boxes.d.ts.map +1 -0
  32. package/dist/modules/isobmff/isobmff-boxes.js +1111 -0
  33. package/dist/modules/isobmff/isobmff-demuxer.d.ts +163 -0
  34. package/dist/modules/isobmff/isobmff-demuxer.d.ts.map +1 -0
  35. package/dist/modules/isobmff/isobmff-demuxer.js +2080 -0
  36. package/dist/modules/isobmff/isobmff-misc.d.ts +14 -0
  37. package/dist/modules/isobmff/isobmff-misc.d.ts.map +1 -0
  38. package/dist/modules/isobmff/isobmff-misc.js +20 -0
  39. package/dist/modules/isobmff/isobmff-muxer.d.ts +142 -0
  40. package/dist/modules/isobmff/isobmff-muxer.d.ts.map +1 -0
  41. package/dist/modules/isobmff/isobmff-muxer.js +858 -0
  42. package/dist/modules/isobmff/isobmff-reader.d.ts +36 -0
  43. package/dist/modules/isobmff/isobmff-reader.d.ts.map +1 -0
  44. package/dist/modules/isobmff/isobmff-reader.js +105 -0
  45. package/dist/modules/matroska/ebml.d.ts +156 -0
  46. package/dist/modules/matroska/ebml.d.ts.map +1 -0
  47. package/dist/modules/matroska/ebml.js +529 -0
  48. package/dist/modules/matroska/matroska-demuxer.d.ts +119 -0
  49. package/dist/modules/matroska/matroska-demuxer.d.ts.map +1 -0
  50. package/dist/modules/matroska/matroska-demuxer.js +1330 -0
  51. package/dist/modules/matroska/matroska-misc.d.ts +14 -0
  52. package/dist/modules/matroska/matroska-misc.d.ts.map +1 -0
  53. package/dist/modules/matroska/matroska-misc.js +20 -0
  54. package/dist/modules/matroska/matroska-muxer.d.ts +71 -0
  55. package/dist/modules/matroska/matroska-muxer.d.ts.map +1 -0
  56. package/dist/modules/matroska/matroska-muxer.js +729 -0
  57. package/dist/modules/media-sink.d.ts +272 -0
  58. package/dist/modules/media-sink.d.ts.map +1 -0
  59. package/dist/modules/media-sink.js +1264 -0
  60. package/dist/modules/media-source.d.ts +230 -0
  61. package/dist/modules/media-source.d.ts.map +1 -0
  62. package/dist/modules/media-source.js +1095 -0
  63. package/dist/modules/misc.d.ts +127 -0
  64. package/dist/modules/misc.d.ts.map +1 -0
  65. package/dist/modules/misc.js +462 -0
  66. package/dist/modules/mp3/mp3-demuxer.d.ts +32 -0
  67. package/dist/modules/mp3/mp3-demuxer.d.ts.map +1 -0
  68. package/dist/modules/mp3/mp3-demuxer.js +160 -0
  69. package/dist/modules/mp3/mp3-misc.d.ts +37 -0
  70. package/dist/modules/mp3/mp3-misc.d.ts.map +1 -0
  71. package/dist/modules/mp3/mp3-misc.js +122 -0
  72. package/dist/modules/mp3/mp3-muxer.d.ts +27 -0
  73. package/dist/modules/mp3/mp3-muxer.d.ts.map +1 -0
  74. package/dist/modules/mp3/mp3-muxer.js +112 -0
  75. package/dist/modules/mp3/mp3-reader.d.ts +25 -0
  76. package/dist/modules/mp3/mp3-reader.d.ts.map +1 -0
  77. package/dist/modules/mp3/mp3-reader.js +73 -0
  78. package/dist/modules/mp3/mp3-writer.d.ts +30 -0
  79. package/dist/modules/mp3/mp3-writer.d.ts.map +1 -0
  80. package/dist/modules/mp3/mp3-writer.js +72 -0
  81. package/dist/modules/muxer.d.ts +32 -0
  82. package/dist/modules/muxer.d.ts.map +1 -0
  83. package/dist/modules/muxer.js +50 -0
  84. package/dist/modules/ogg/ogg-demuxer.d.ts +53 -0
  85. package/dist/modules/ogg/ogg-demuxer.d.ts.map +1 -0
  86. package/dist/modules/ogg/ogg-demuxer.js +701 -0
  87. package/dist/modules/ogg/ogg-misc.d.ts +27 -0
  88. package/dist/modules/ogg/ogg-misc.d.ts.map +1 -0
  89. package/dist/modules/ogg/ogg-misc.js +78 -0
  90. package/dist/modules/ogg/ogg-muxer.d.ts +58 -0
  91. package/dist/modules/ogg/ogg-muxer.d.ts.map +1 -0
  92. package/dist/modules/ogg/ogg-muxer.js +345 -0
  93. package/dist/modules/ogg/ogg-reader.d.ts +37 -0
  94. package/dist/modules/ogg/ogg-reader.d.ts.map +1 -0
  95. package/dist/modules/ogg/ogg-reader.js +104 -0
  96. package/dist/modules/output-format.d.ts +285 -0
  97. package/dist/modules/output-format.d.ts.map +1 -0
  98. package/dist/modules/output-format.js +385 -0
  99. package/dist/modules/output.d.ts +134 -0
  100. package/dist/modules/output.d.ts.map +1 -0
  101. package/dist/modules/output.js +269 -0
  102. package/dist/modules/packet.d.ts +86 -0
  103. package/dist/modules/packet.d.ts.map +1 -0
  104. package/dist/modules/packet.js +133 -0
  105. package/dist/modules/pcm.d.ts +12 -0
  106. package/dist/modules/pcm.d.ts.map +1 -0
  107. package/dist/modules/pcm.js +85 -0
  108. package/dist/modules/reader.d.ts +40 -0
  109. package/dist/modules/reader.d.ts.map +1 -0
  110. package/dist/modules/reader.js +153 -0
  111. package/dist/modules/sample.d.ts +186 -0
  112. package/dist/modules/sample.d.ts.map +1 -0
  113. package/dist/modules/sample.js +798 -0
  114. package/dist/modules/source.d.ts +76 -0
  115. package/dist/modules/source.d.ts.map +1 -0
  116. package/dist/modules/source.js +183 -0
  117. package/dist/modules/subtitles.d.ts +37 -0
  118. package/dist/modules/subtitles.d.ts.map +1 -0
  119. package/dist/modules/subtitles.js +89 -0
  120. package/dist/modules/target.d.ts +58 -0
  121. package/dist/modules/target.d.ts.map +1 -0
  122. package/dist/modules/target.js +63 -0
  123. package/dist/modules/wave/riff-reader.d.ts +19 -0
  124. package/dist/modules/wave/riff-reader.d.ts.map +1 -0
  125. package/dist/modules/wave/riff-reader.js +38 -0
  126. package/dist/modules/wave/riff-writer.d.ts +18 -0
  127. package/dist/modules/wave/riff-writer.d.ts.map +1 -0
  128. package/dist/modules/wave/riff-writer.js +25 -0
  129. package/dist/modules/wave/wave-demuxer.d.ts +42 -0
  130. package/dist/modules/wave/wave-demuxer.d.ts.map +1 -0
  131. package/dist/modules/wave/wave-demuxer.js +239 -0
  132. package/dist/modules/wave/wave-muxer.d.ts +27 -0
  133. package/dist/modules/wave/wave-muxer.d.ts.map +1 -0
  134. package/dist/modules/wave/wave-muxer.js +108 -0
  135. package/dist/modules/writer.d.ts +85 -0
  136. package/dist/modules/writer.d.ts.map +1 -0
  137. package/dist/modules/writer.js +347 -0
  138. package/package.json +14 -10
  139. package/src/codec-data.ts +1394 -0
  140. package/src/codec.ts +1392 -0
  141. package/src/conversion.ts +1199 -0
  142. package/src/custom-coder.ts +162 -0
  143. package/src/demuxer.ts +22 -0
  144. package/src/index.ts +148 -0
  145. package/src/input-format.ts +386 -0
  146. package/src/input-track.ts +341 -0
  147. package/src/input.ts +138 -0
  148. package/src/isobmff/isobmff-boxes.ts +1365 -0
  149. package/src/isobmff/isobmff-demuxer.ts +2646 -0
  150. package/src/isobmff/isobmff-misc.ts +29 -0
  151. package/src/isobmff/isobmff-muxer.ts +1210 -0
  152. package/src/isobmff/isobmff-reader.ts +137 -0
  153. package/src/matroska/ebml.ts +595 -0
  154. package/src/matroska/matroska-demuxer.ts +1677 -0
  155. package/src/matroska/matroska-misc.ts +29 -0
  156. package/src/matroska/matroska-muxer.ts +999 -0
  157. package/src/media-sink.ts +1577 -0
  158. package/src/media-source.ts +1406 -0
  159. package/src/misc.ts +584 -0
  160. package/src/mp3/mp3-demuxer.ts +228 -0
  161. package/src/mp3/mp3-misc.ts +154 -0
  162. package/src/mp3/mp3-muxer.ts +153 -0
  163. package/src/mp3/mp3-reader.ts +93 -0
  164. package/src/mp3/mp3-writer.ts +112 -0
  165. package/src/muxer.ts +88 -0
  166. package/src/ogg/ogg-demuxer.ts +985 -0
  167. package/src/ogg/ogg-misc.ts +116 -0
  168. package/src/ogg/ogg-muxer.ts +480 -0
  169. package/src/ogg/ogg-reader.ts +143 -0
  170. package/src/output-format.ts +689 -0
  171. package/src/output.ts +427 -0
  172. package/src/packet.ts +180 -0
  173. package/src/pcm.ts +112 -0
  174. package/src/reader.ts +205 -0
  175. package/src/sample.ts +1029 -0
  176. package/src/source.ts +279 -0
  177. package/src/subtitles.ts +131 -0
  178. package/src/target.ts +106 -0
  179. package/src/tsconfig.json +13 -0
  180. package/src/wave/riff-reader.ts +48 -0
  181. package/src/wave/riff-writer.ts +30 -0
  182. package/src/wave/wave-demuxer.ts +317 -0
  183. package/src/wave/wave-muxer.ts +145 -0
  184. package/src/writer.ts +461 -0
  185. /package/dist/{mediabunny.js → bundles/mediabunny.cjs} +0 -0
  186. /package/dist/{mediabunny.min.js → bundles/mediabunny.min.cjs} +0 -0
  187. /package/dist/{mediabunny.min.mjs → bundles/mediabunny.min.mjs} +0 -0
  188. /package/dist/{mediabunny.mjs → bundles/mediabunny.mjs} +0 -0
@@ -0,0 +1,341 @@
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 { AudioCodec, MediaCodec, VideoCodec } from './codec';
10
+ import { customAudioDecoders, customVideoDecoders } from './custom-coder';
11
+ import { EncodedPacketSink, PacketRetrievalOptions } from './media-sink';
12
+ import { assert, Rotation } from './misc';
13
+ import { TrackType } from './output';
14
+ import { EncodedPacket } from './packet';
15
+
16
+ /**
17
+ * Contains aggregate statistics about the encoded packets of a track.
18
+ * @public
19
+ */
20
+ export type PacketStats = {
21
+ /** The total number of packets. */
22
+ packetCount: number;
23
+ /** The average number of packets per second. For video tracks, this will equal the average frame rate (FPS). */
24
+ averagePacketRate: number;
25
+ /** The average number of bits per second. */
26
+ averageBitrate: number;
27
+ };
28
+
29
+ export interface InputTrackBacking {
30
+ getId(): number;
31
+ getCodec(): MediaCodec | null;
32
+ getLanguageCode(): string;
33
+ getTimeResolution(): number;
34
+ getFirstTimestamp(): Promise<number>;
35
+ computeDuration(): Promise<number>;
36
+
37
+ getFirstPacket(options: PacketRetrievalOptions): Promise<EncodedPacket | null>;
38
+ getPacket(timestamp: number, options: PacketRetrievalOptions): Promise<EncodedPacket | null>;
39
+ getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions): Promise<EncodedPacket | null>;
40
+ getKeyPacket(timestamp: number, options: PacketRetrievalOptions): Promise<EncodedPacket | null>;
41
+ getNextKeyPacket(packet: EncodedPacket, options: PacketRetrievalOptions): Promise<EncodedPacket | null>;
42
+ }
43
+
44
+ /**
45
+ * Represents a media track in an input file.
46
+ * @public
47
+ */
48
+ export abstract class InputTrack {
49
+ /** @internal */
50
+ _backing: InputTrackBacking;
51
+
52
+ /** @internal */
53
+ constructor(backing: InputTrackBacking) {
54
+ this._backing = backing;
55
+ }
56
+
57
+ /** The type of the track. */
58
+ abstract get type(): TrackType;
59
+ /** The codec of the track's packets. */
60
+ abstract get codec(): MediaCodec | null;
61
+ /** Returns the full codec parameter string for this track. */
62
+ abstract getCodecParameterString(): Promise<string | null>;
63
+ /** Checks if this track's packets can be decoded by the browser. */
64
+ abstract canDecode(): Promise<boolean>;
65
+
66
+ /** Returns true iff this track is a video track. */
67
+ isVideoTrack(): this is InputVideoTrack {
68
+ return this instanceof InputVideoTrack;
69
+ }
70
+
71
+ /** Returns true iff this track is an audio track. */
72
+ isAudioTrack(): this is InputAudioTrack {
73
+ return this instanceof InputAudioTrack;
74
+ }
75
+
76
+ /** The unique ID of this track in the input file. */
77
+ get id() {
78
+ return this._backing.getId();
79
+ }
80
+
81
+ /** The ISO 639-2/T language code for this track. If the language is unknown, this field is 'und' (undetermined). */
82
+ get languageCode() {
83
+ return this._backing.getLanguageCode();
84
+ }
85
+
86
+ /**
87
+ * A positive number x such that all timestamps and durations of all packets of this track are
88
+ * integer multiples of 1/x.
89
+ */
90
+ get timeResolution() {
91
+ return this._backing.getTimeResolution();
92
+ }
93
+
94
+ /**
95
+ * Returns the start timestamp of the first packet of this track, in seconds. While often near zero, this value
96
+ * may be positive or even negative. A negative starting timestamp means the track's timing has been offset. Samples
97
+ * with a negative timestamp should not be presented.
98
+ */
99
+ getFirstTimestamp() {
100
+ return this._backing.getFirstTimestamp();
101
+ }
102
+
103
+ /** Returns the end timestamp of the last packet of this track, in seconds. */
104
+ computeDuration() {
105
+ return this._backing.computeDuration();
106
+ }
107
+
108
+ /**
109
+ * Computes aggregate packet statistics for this track, such as average packet rate or bitrate.
110
+ *
111
+ * @param targetPacketCount - This optional parameter sets a target for how many packets this method must have
112
+ * looked at before it can return early; this means, you can use it to aggregate only a subset (prefix) of all
113
+ * packets. This is very useful for getting a great estimate of video frame rate without having to scan through the
114
+ * entire file.
115
+ */
116
+ async computePacketStats(targetPacketCount = Infinity): Promise<PacketStats> {
117
+ const sink = new EncodedPacketSink(this);
118
+
119
+ let startTimestamp = Infinity;
120
+ let endTimestamp = -Infinity;
121
+ let packetCount = 0;
122
+ let totalPacketBytes = 0;
123
+
124
+ for await (const packet of sink.packets(undefined, undefined, { metadataOnly: true })) {
125
+ if (
126
+ packetCount >= targetPacketCount
127
+ // This additional condition is needed to produce correct results with out-of-presentation-order packets
128
+ && packet.timestamp >= endTimestamp
129
+ ) {
130
+ break;
131
+ }
132
+
133
+ startTimestamp = Math.min(startTimestamp, packet.timestamp);
134
+ endTimestamp = Math.max(endTimestamp, packet.timestamp + packet.duration);
135
+
136
+ packetCount++;
137
+ totalPacketBytes += packet.byteLength;
138
+ }
139
+
140
+ return {
141
+ packetCount,
142
+ averagePacketRate: packetCount
143
+ ? Number((packetCount / (endTimestamp - startTimestamp)).toPrecision(16))
144
+ : 0,
145
+ averageBitrate: packetCount
146
+ ? Number((8 * totalPacketBytes / (endTimestamp - startTimestamp)).toPrecision(16))
147
+ : 0,
148
+ };
149
+ }
150
+ }
151
+
152
+ export interface InputVideoTrackBacking extends InputTrackBacking {
153
+ getCodec(): VideoCodec | null;
154
+ getCodedWidth(): number;
155
+ getCodedHeight(): number;
156
+ getRotation(): Rotation;
157
+ getColorSpace(): Promise<VideoColorSpaceInit>;
158
+ getDecoderConfig(): Promise<VideoDecoderConfig | null>;
159
+ }
160
+
161
+ /**
162
+ * Represents a video track in an input file.
163
+ * @public
164
+ */
165
+ export class InputVideoTrack extends InputTrack {
166
+ /** @internal */
167
+ override _backing: InputVideoTrackBacking;
168
+
169
+ /** @internal */
170
+ constructor(backing: InputVideoTrackBacking) {
171
+ super(backing);
172
+
173
+ this._backing = backing;
174
+ }
175
+
176
+ get type(): TrackType {
177
+ return 'video';
178
+ }
179
+
180
+ get codec() {
181
+ return this._backing.getCodec();
182
+ }
183
+
184
+ /** The width in pixels of the track's coded samples, before any transformations or rotations. */
185
+ get codedWidth() {
186
+ return this._backing.getCodedWidth();
187
+ }
188
+
189
+ /** The height in pixels of the track's coded samples, before any transformations or rotations. */
190
+ get codedHeight() {
191
+ return this._backing.getCodedHeight();
192
+ }
193
+
194
+ /** The angle in degrees by which the track's frames should be rotated (clockwise). */
195
+ get rotation() {
196
+ return this._backing.getRotation();
197
+ }
198
+
199
+ /** The width in pixels of the track's frames after rotation. */
200
+ get displayWidth() {
201
+ const rotation = this._backing.getRotation();
202
+ return rotation % 180 === 0 ? this._backing.getCodedWidth() : this._backing.getCodedHeight();
203
+ }
204
+
205
+ /** The height in pixels of the track's frames after rotation. */
206
+ get displayHeight() {
207
+ const rotation = this._backing.getRotation();
208
+ return rotation % 180 === 0 ? this._backing.getCodedHeight() : this._backing.getCodedWidth();
209
+ }
210
+
211
+ /** Returns the color space of the track's samples. */
212
+ getColorSpace() {
213
+ return this._backing.getColorSpace();
214
+ }
215
+
216
+ /** If this method returns true, the track's samples use a high dynamic range (HDR). */
217
+ async hasHighDynamicRange() {
218
+ const colorSpace = await this._backing.getColorSpace();
219
+
220
+ return (colorSpace.primaries as string) === 'bt2020' || (colorSpace.primaries as string) === 'smpte432'
221
+ || (colorSpace.transfer as string) === 'pg' || (colorSpace.transfer as string) === 'hlg'
222
+ || (colorSpace.matrix as string) === 'bt2020-ncl';
223
+ }
224
+
225
+ /** Returns the decoder configuration for decoding the track's packets using a VideoDecoder. */
226
+ getDecoderConfig() {
227
+ return this._backing.getDecoderConfig();
228
+ }
229
+
230
+ async getCodecParameterString() {
231
+ const decoderConfig = await this._backing.getDecoderConfig();
232
+ return decoderConfig?.codec ?? null;
233
+ }
234
+
235
+ async canDecode() {
236
+ try {
237
+ const decoderConfig = await this._backing.getDecoderConfig();
238
+ if (!decoderConfig) {
239
+ return false;
240
+ }
241
+
242
+ const codec = this._backing.getCodec();
243
+ assert(codec !== null);
244
+
245
+ if (customVideoDecoders.some(x => x.supports(codec, decoderConfig))) {
246
+ return true;
247
+ }
248
+
249
+ if (typeof VideoDecoder === 'undefined') {
250
+ return false;
251
+ }
252
+
253
+ const support = await VideoDecoder.isConfigSupported(decoderConfig);
254
+ return support.supported === true;
255
+ } catch (error) {
256
+ console.error('Error during decodability check:', error);
257
+ return false;
258
+ }
259
+ }
260
+ }
261
+
262
+ export interface InputAudioTrackBacking extends InputTrackBacking {
263
+ getCodec(): AudioCodec | null;
264
+ getNumberOfChannels(): number;
265
+ getSampleRate(): number;
266
+ getDecoderConfig(): Promise<AudioDecoderConfig | null>;
267
+ }
268
+
269
+ /**
270
+ * Represents an audio track in an input file.
271
+ * @public
272
+ */
273
+ export class InputAudioTrack extends InputTrack {
274
+ /** @internal */
275
+ override _backing: InputAudioTrackBacking;
276
+
277
+ /** @internal */
278
+ constructor(backing: InputAudioTrackBacking) {
279
+ super(backing);
280
+
281
+ this._backing = backing;
282
+ }
283
+
284
+ get type(): TrackType {
285
+ return 'audio';
286
+ }
287
+
288
+ get codec(): AudioCodec | null {
289
+ return this._backing.getCodec();
290
+ }
291
+
292
+ /** The number of audio channels in the track. */
293
+ get numberOfChannels() {
294
+ return this._backing.getNumberOfChannels();
295
+ }
296
+
297
+ /** The track's audio sample rate in hertz. */
298
+ get sampleRate() {
299
+ return this._backing.getSampleRate();
300
+ }
301
+
302
+ /** Returns the decoder configuration for decoding the track's packets using an AudioDecoder. */
303
+ getDecoderConfig() {
304
+ return this._backing.getDecoderConfig();
305
+ }
306
+
307
+ async getCodecParameterString() {
308
+ const decoderConfig = await this._backing.getDecoderConfig();
309
+ return decoderConfig?.codec ?? null;
310
+ }
311
+
312
+ async canDecode() {
313
+ try {
314
+ const decoderConfig = await this._backing.getDecoderConfig();
315
+ if (!decoderConfig) {
316
+ return false;
317
+ }
318
+
319
+ const codec = this._backing.getCodec();
320
+ assert(codec !== null);
321
+
322
+ if (customAudioDecoders.some(x => x.supports(codec, decoderConfig))) {
323
+ return true;
324
+ }
325
+
326
+ if (decoderConfig.codec.startsWith('pcm-')) {
327
+ return true; // Since we decode it ourselves
328
+ } else {
329
+ if (typeof AudioDecoder === 'undefined') {
330
+ return false;
331
+ }
332
+
333
+ const support = await AudioDecoder.isConfigSupported(decoderConfig);
334
+ return support.supported === true;
335
+ }
336
+ } catch (error) {
337
+ console.error('Error during decodability check:', error);
338
+ return false;
339
+ }
340
+ }
341
+ }
package/src/input.ts ADDED
@@ -0,0 +1,138 @@
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 { Demuxer } from './demuxer';
10
+ import { InputFormat } from './input-format';
11
+ import { assert } from './misc';
12
+ import { Reader } from './reader';
13
+ import { Source } from './source';
14
+
15
+ /**
16
+ * The options for creating an Input object.
17
+ * @public
18
+ */
19
+ export type InputOptions<S extends Source = Source> = {
20
+ /** A list of supported formats. If the source file is not of one of these formats, then it cannot be read. */
21
+ formats: InputFormat[];
22
+ /** The source from which data will be read. */
23
+ source: S;
24
+ };
25
+
26
+ /**
27
+ * Represents an input media file. This is the root object from which all media read operations start.
28
+ * @public
29
+ */
30
+ export class Input<S extends Source = Source> {
31
+ /** @internal */
32
+ _source: S;
33
+ /** @internal */
34
+ _formats: InputFormat[];
35
+ /** @internal */
36
+ _mainReader: Reader;
37
+ /** @internal */
38
+ _demuxerPromise: Promise<Demuxer> | null = null;
39
+ /** @internal */
40
+ _format: InputFormat | null = null;
41
+
42
+ constructor(options: InputOptions<S>) {
43
+ if (!options || typeof options !== 'object') {
44
+ throw new TypeError('options must be an object.');
45
+ }
46
+ if (!Array.isArray(options.formats) || options.formats.some(x => !(x instanceof InputFormat))) {
47
+ throw new TypeError('options.formats must be an array of InputFormat.');
48
+ }
49
+ if (!(options.source instanceof Source)) {
50
+ throw new TypeError('options.source must be a Source.');
51
+ }
52
+
53
+ this._formats = options.formats;
54
+ this._source = options.source;
55
+ this._mainReader = new Reader(options.source);
56
+ }
57
+
58
+ /** @internal */
59
+ _getDemuxer() {
60
+ return this._demuxerPromise ??= (async () => {
61
+ await this._mainReader.loadRange(0, 4096); // Load the first 4 kiB so we can determine the format
62
+
63
+ for (const format of this._formats) {
64
+ const canRead = await format._canReadInput(this);
65
+ if (canRead) {
66
+ this._format = format;
67
+ return format._createDemuxer(this);
68
+ }
69
+ }
70
+
71
+ throw new Error('Input has an unsupported or unrecognizable format.');
72
+ })();
73
+ }
74
+
75
+ /**
76
+ * Returns the source from which this input file reads its data. This is the same source that was passed to the
77
+ * constructor.
78
+ */
79
+ get source() {
80
+ return this._source;
81
+ }
82
+
83
+ /**
84
+ * Returns the format of the input file. You can compare this result directly to the InputFormat singletons or use
85
+ * `instanceof` checks for subset-aware logic (for example, `format instanceof MatroskaInputFormat` is true for
86
+ * both MKV and WebM).
87
+ */
88
+ async getFormat() {
89
+ await this._getDemuxer();
90
+ assert(this._format!);
91
+ return this._format;
92
+ }
93
+
94
+ /**
95
+ * Computes the duration of the input file, in seconds. More precisely, returns the largest end timestamp among
96
+ * all tracks.
97
+ */
98
+ async computeDuration() {
99
+ const demuxer = await this._getDemuxer();
100
+ return demuxer.computeDuration();
101
+ }
102
+
103
+ /** Returns the list of all tracks of this input file. */
104
+ async getTracks() {
105
+ const demuxer = await this._getDemuxer();
106
+ return demuxer.getTracks();
107
+ }
108
+
109
+ /** Returns the list of all video tracks of this input file. */
110
+ async getVideoTracks() {
111
+ const tracks = await this.getTracks();
112
+ return tracks.filter(x => x.isVideoTrack());
113
+ }
114
+
115
+ /** Returns the primary video track of this input file, or null if there are no video tracks. */
116
+ async getPrimaryVideoTrack() {
117
+ const tracks = await this.getTracks();
118
+ return tracks.find(x => x.isVideoTrack()) ?? null;
119
+ }
120
+
121
+ /** Returns the list of all audio tracks of this input file. */
122
+ async getAudioTracks() {
123
+ const tracks = await this.getTracks();
124
+ return tracks.filter(x => x.isAudioTrack());
125
+ }
126
+
127
+ /** Returns the primary audio track of this input file, or null if there are no audio tracks. */
128
+ async getPrimaryAudioTrack() {
129
+ const tracks = await this.getTracks();
130
+ return tracks.find(x => x.isAudioTrack()) ?? null;
131
+ }
132
+
133
+ /** Returns the full MIME type of this input file, including track codecs. */
134
+ async getMimeType() {
135
+ const demuxer = await this._getDemuxer();
136
+ return demuxer.getMimeType();
137
+ }
138
+ }