@kenzuya/mediabunny 1.26.0 → 1.28.5

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 (237) hide show
  1. package/README.md +1 -1
  2. package/dist/bundles/{mediabunny.mjs → mediabunny.js} +21963 -21388
  3. package/dist/bundles/mediabunny.min.js +490 -0
  4. package/dist/modules/shared/mp3-misc.d.ts.map +1 -1
  5. package/dist/modules/src/adts/adts-demuxer.d.ts +6 -6
  6. package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
  7. package/dist/modules/src/adts/adts-muxer.d.ts +4 -4
  8. package/dist/modules/src/adts/adts-muxer.d.ts.map +1 -1
  9. package/dist/modules/src/adts/adts-reader.d.ts +1 -1
  10. package/dist/modules/src/adts/adts-reader.d.ts.map +1 -1
  11. package/dist/modules/src/avi/avi-demuxer.d.ts +44 -0
  12. package/dist/modules/src/avi/avi-demuxer.d.ts.map +1 -0
  13. package/dist/modules/src/avi/avi-misc.d.ts +88 -0
  14. package/dist/modules/src/avi/avi-misc.d.ts.map +1 -0
  15. package/dist/modules/src/avi/avi-muxer.d.ts +45 -0
  16. package/dist/modules/src/avi/avi-muxer.d.ts.map +1 -0
  17. package/dist/modules/src/avi/riff-writer.d.ts +26 -0
  18. package/dist/modules/src/avi/riff-writer.d.ts.map +1 -0
  19. package/dist/modules/src/codec-data.d.ts +8 -3
  20. package/dist/modules/src/codec-data.d.ts.map +1 -1
  21. package/dist/modules/src/codec.d.ts +10 -10
  22. package/dist/modules/src/codec.d.ts.map +1 -1
  23. package/dist/modules/src/conversion.d.ts +33 -16
  24. package/dist/modules/src/conversion.d.ts.map +1 -1
  25. package/dist/modules/src/custom-coder.d.ts +8 -8
  26. package/dist/modules/src/custom-coder.d.ts.map +1 -1
  27. package/dist/modules/src/demuxer.d.ts +3 -3
  28. package/dist/modules/src/demuxer.d.ts.map +1 -1
  29. package/dist/modules/src/encode.d.ts +8 -8
  30. package/dist/modules/src/encode.d.ts.map +1 -1
  31. package/dist/modules/src/flac/flac-demuxer.d.ts +7 -7
  32. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  33. package/dist/modules/src/flac/flac-misc.d.ts +3 -3
  34. package/dist/modules/src/flac/flac-misc.d.ts.map +1 -1
  35. package/dist/modules/src/flac/flac-muxer.d.ts +5 -5
  36. package/dist/modules/src/flac/flac-muxer.d.ts.map +1 -1
  37. package/dist/modules/src/id3.d.ts +3 -3
  38. package/dist/modules/src/id3.d.ts.map +1 -1
  39. package/dist/modules/src/index.d.ts +20 -20
  40. package/dist/modules/src/index.d.ts.map +1 -1
  41. package/dist/modules/src/input-format.d.ts +22 -0
  42. package/dist/modules/src/input-format.d.ts.map +1 -1
  43. package/dist/modules/src/input-track.d.ts +8 -8
  44. package/dist/modules/src/input-track.d.ts.map +1 -1
  45. package/dist/modules/src/input.d.ts +12 -12
  46. package/dist/modules/src/isobmff/isobmff-boxes.d.ts +2 -2
  47. package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
  48. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts +12 -12
  49. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
  50. package/dist/modules/src/isobmff/isobmff-misc.d.ts.map +1 -1
  51. package/dist/modules/src/isobmff/isobmff-muxer.d.ts +11 -11
  52. package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
  53. package/dist/modules/src/isobmff/isobmff-reader.d.ts +2 -2
  54. package/dist/modules/src/isobmff/isobmff-reader.d.ts.map +1 -1
  55. package/dist/modules/src/matroska/ebml.d.ts +3 -3
  56. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  57. package/dist/modules/src/matroska/matroska-demuxer.d.ts +13 -13
  58. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  59. package/dist/modules/src/matroska/matroska-input.d.ts +33 -0
  60. package/dist/modules/src/matroska/matroska-input.d.ts.map +1 -0
  61. package/dist/modules/src/matroska/matroska-misc.d.ts.map +1 -1
  62. package/dist/modules/src/matroska/matroska-muxer.d.ts +5 -5
  63. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  64. package/dist/modules/src/media-sink.d.ts +5 -5
  65. package/dist/modules/src/media-sink.d.ts.map +1 -1
  66. package/dist/modules/src/media-source.d.ts +22 -4
  67. package/dist/modules/src/media-source.d.ts.map +1 -1
  68. package/dist/modules/src/metadata.d.ts +2 -2
  69. package/dist/modules/src/metadata.d.ts.map +1 -1
  70. package/dist/modules/src/misc.d.ts +5 -4
  71. package/dist/modules/src/misc.d.ts.map +1 -1
  72. package/dist/modules/src/mp3/mp3-demuxer.d.ts +7 -7
  73. package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
  74. package/dist/modules/src/mp3/mp3-muxer.d.ts +4 -4
  75. package/dist/modules/src/mp3/mp3-muxer.d.ts.map +1 -1
  76. package/dist/modules/src/mp3/mp3-reader.d.ts +2 -2
  77. package/dist/modules/src/mp3/mp3-reader.d.ts.map +1 -1
  78. package/dist/modules/src/mp3/mp3-writer.d.ts +1 -1
  79. package/dist/modules/src/mp3/mp3-writer.d.ts.map +1 -1
  80. package/dist/modules/src/muxer.d.ts +4 -4
  81. package/dist/modules/src/muxer.d.ts.map +1 -1
  82. package/dist/modules/src/node.d.ts +1 -1
  83. package/dist/modules/src/ogg/ogg-demuxer.d.ts +7 -7
  84. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  85. package/dist/modules/src/ogg/ogg-misc.d.ts +1 -1
  86. package/dist/modules/src/ogg/ogg-misc.d.ts.map +1 -1
  87. package/dist/modules/src/ogg/ogg-muxer.d.ts +5 -5
  88. package/dist/modules/src/ogg/ogg-muxer.d.ts.map +1 -1
  89. package/dist/modules/src/ogg/ogg-reader.d.ts +1 -1
  90. package/dist/modules/src/ogg/ogg-reader.d.ts.map +1 -1
  91. package/dist/modules/src/output-format.d.ts +51 -6
  92. package/dist/modules/src/output-format.d.ts.map +1 -1
  93. package/dist/modules/src/output.d.ts +13 -13
  94. package/dist/modules/src/output.d.ts.map +1 -1
  95. package/dist/modules/src/packet.d.ts +1 -1
  96. package/dist/modules/src/packet.d.ts.map +1 -1
  97. package/dist/modules/src/pcm.d.ts.map +1 -1
  98. package/dist/modules/src/reader.d.ts +2 -2
  99. package/dist/modules/src/reader.d.ts.map +1 -1
  100. package/dist/modules/src/sample.d.ts +57 -15
  101. package/dist/modules/src/sample.d.ts.map +1 -1
  102. package/dist/modules/src/source.d.ts +3 -3
  103. package/dist/modules/src/source.d.ts.map +1 -1
  104. package/dist/modules/src/subtitles.d.ts +1 -1
  105. package/dist/modules/src/subtitles.d.ts.map +1 -1
  106. package/dist/modules/src/target.d.ts +2 -2
  107. package/dist/modules/src/target.d.ts.map +1 -1
  108. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  109. package/dist/modules/src/wave/riff-writer.d.ts +1 -1
  110. package/dist/modules/src/wave/riff-writer.d.ts.map +1 -1
  111. package/dist/modules/src/wave/wave-demuxer.d.ts +6 -6
  112. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  113. package/dist/modules/src/wave/wave-muxer.d.ts +4 -4
  114. package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
  115. package/dist/modules/src/writer.d.ts +1 -1
  116. package/dist/modules/src/writer.d.ts.map +1 -1
  117. package/dist/packages/eac3/eac3.wasm +0 -0
  118. package/dist/packages/eac3/mediabunny-eac3.js +1058 -0
  119. package/dist/packages/eac3/mediabunny-eac3.min.js +44 -0
  120. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.js +694 -0
  121. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.min.js +58 -0
  122. package/dist/packages/mpeg4/mediabunny-mpeg4.js +1198 -0
  123. package/dist/packages/mpeg4/mediabunny-mpeg4.min.js +44 -0
  124. package/dist/packages/mpeg4/xvid.wasm +0 -0
  125. package/package.json +18 -57
  126. package/dist/bundles/mediabunny.cjs +0 -26140
  127. package/dist/bundles/mediabunny.min.cjs +0 -147
  128. package/dist/bundles/mediabunny.min.mjs +0 -146
  129. package/dist/mediabunny.d.ts +0 -3319
  130. package/dist/modules/shared/mp3-misc.js +0 -147
  131. package/dist/modules/src/adts/adts-demuxer.js +0 -239
  132. package/dist/modules/src/adts/adts-muxer.js +0 -80
  133. package/dist/modules/src/adts/adts-reader.js +0 -63
  134. package/dist/modules/src/codec-data.js +0 -1730
  135. package/dist/modules/src/codec.js +0 -869
  136. package/dist/modules/src/conversion.js +0 -1459
  137. package/dist/modules/src/custom-coder.js +0 -117
  138. package/dist/modules/src/demuxer.js +0 -12
  139. package/dist/modules/src/encode.js +0 -442
  140. package/dist/modules/src/flac/flac-demuxer.js +0 -504
  141. package/dist/modules/src/flac/flac-misc.js +0 -135
  142. package/dist/modules/src/flac/flac-muxer.js +0 -222
  143. package/dist/modules/src/id3.js +0 -848
  144. package/dist/modules/src/index.js +0 -28
  145. package/dist/modules/src/input-format.js +0 -480
  146. package/dist/modules/src/input-track.js +0 -372
  147. package/dist/modules/src/input.js +0 -188
  148. package/dist/modules/src/isobmff/isobmff-boxes.js +0 -1480
  149. package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -2618
  150. package/dist/modules/src/isobmff/isobmff-misc.js +0 -20
  151. package/dist/modules/src/isobmff/isobmff-muxer.js +0 -966
  152. package/dist/modules/src/isobmff/isobmff-reader.js +0 -72
  153. package/dist/modules/src/matroska/ebml.js +0 -653
  154. package/dist/modules/src/matroska/matroska-demuxer.js +0 -2133
  155. package/dist/modules/src/matroska/matroska-misc.js +0 -20
  156. package/dist/modules/src/matroska/matroska-muxer.js +0 -1017
  157. package/dist/modules/src/media-sink.js +0 -1736
  158. package/dist/modules/src/media-source.js +0 -1825
  159. package/dist/modules/src/metadata.js +0 -193
  160. package/dist/modules/src/misc.js +0 -623
  161. package/dist/modules/src/mp3/mp3-demuxer.js +0 -285
  162. package/dist/modules/src/mp3/mp3-muxer.js +0 -123
  163. package/dist/modules/src/mp3/mp3-reader.js +0 -26
  164. package/dist/modules/src/mp3/mp3-writer.js +0 -78
  165. package/dist/modules/src/muxer.js +0 -50
  166. package/dist/modules/src/node.js +0 -9
  167. package/dist/modules/src/ogg/ogg-demuxer.js +0 -763
  168. package/dist/modules/src/ogg/ogg-misc.js +0 -78
  169. package/dist/modules/src/ogg/ogg-muxer.js +0 -353
  170. package/dist/modules/src/ogg/ogg-reader.js +0 -65
  171. package/dist/modules/src/output-format.js +0 -527
  172. package/dist/modules/src/output.js +0 -300
  173. package/dist/modules/src/packet.js +0 -182
  174. package/dist/modules/src/pcm.js +0 -85
  175. package/dist/modules/src/reader.js +0 -236
  176. package/dist/modules/src/sample.js +0 -1056
  177. package/dist/modules/src/source.js +0 -1182
  178. package/dist/modules/src/subtitles.js +0 -575
  179. package/dist/modules/src/target.js +0 -140
  180. package/dist/modules/src/wave/riff-writer.js +0 -30
  181. package/dist/modules/src/wave/wave-demuxer.js +0 -447
  182. package/dist/modules/src/wave/wave-muxer.js +0 -318
  183. package/dist/modules/src/writer.js +0 -370
  184. package/src/adts/adts-demuxer.ts +0 -331
  185. package/src/adts/adts-muxer.ts +0 -111
  186. package/src/adts/adts-reader.ts +0 -85
  187. package/src/codec-data.ts +0 -2078
  188. package/src/codec.ts +0 -1092
  189. package/src/conversion.ts +0 -2112
  190. package/src/custom-coder.ts +0 -197
  191. package/src/demuxer.ts +0 -24
  192. package/src/encode.ts +0 -739
  193. package/src/flac/flac-demuxer.ts +0 -730
  194. package/src/flac/flac-misc.ts +0 -164
  195. package/src/flac/flac-muxer.ts +0 -320
  196. package/src/id3.ts +0 -925
  197. package/src/index.ts +0 -221
  198. package/src/input-format.ts +0 -541
  199. package/src/input-track.ts +0 -529
  200. package/src/input.ts +0 -235
  201. package/src/isobmff/isobmff-boxes.ts +0 -1719
  202. package/src/isobmff/isobmff-demuxer.ts +0 -3190
  203. package/src/isobmff/isobmff-misc.ts +0 -29
  204. package/src/isobmff/isobmff-muxer.ts +0 -1348
  205. package/src/isobmff/isobmff-reader.ts +0 -91
  206. package/src/matroska/ebml.ts +0 -730
  207. package/src/matroska/matroska-demuxer.ts +0 -2481
  208. package/src/matroska/matroska-misc.ts +0 -29
  209. package/src/matroska/matroska-muxer.ts +0 -1276
  210. package/src/media-sink.ts +0 -2179
  211. package/src/media-source.ts +0 -2243
  212. package/src/metadata.ts +0 -320
  213. package/src/misc.ts +0 -798
  214. package/src/mp3/mp3-demuxer.ts +0 -383
  215. package/src/mp3/mp3-muxer.ts +0 -166
  216. package/src/mp3/mp3-reader.ts +0 -34
  217. package/src/mp3/mp3-writer.ts +0 -120
  218. package/src/muxer.ts +0 -88
  219. package/src/node.ts +0 -11
  220. package/src/ogg/ogg-demuxer.ts +0 -1053
  221. package/src/ogg/ogg-misc.ts +0 -116
  222. package/src/ogg/ogg-muxer.ts +0 -497
  223. package/src/ogg/ogg-reader.ts +0 -93
  224. package/src/output-format.ts +0 -945
  225. package/src/output.ts +0 -488
  226. package/src/packet.ts +0 -263
  227. package/src/pcm.ts +0 -112
  228. package/src/reader.ts +0 -323
  229. package/src/sample.ts +0 -1461
  230. package/src/source.ts +0 -1688
  231. package/src/subtitles.ts +0 -711
  232. package/src/target.ts +0 -204
  233. package/src/tsconfig.json +0 -16
  234. package/src/wave/riff-writer.ts +0 -36
  235. package/src/wave/wave-demuxer.ts +0 -529
  236. package/src/wave/wave-muxer.ts +0 -371
  237. package/src/writer.ts +0 -490
@@ -1,529 +0,0 @@
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 } from '../codec';
10
- import { Demuxer } from '../demuxer';
11
- import { Input } from '../input';
12
- import { InputAudioTrack, InputAudioTrackBacking } from '../input-track';
13
- import { PacketRetrievalOptions } from '../media-sink';
14
- import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
15
- import { assert, UNDETERMINED_LANGUAGE } from '../misc';
16
- import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
17
- import { readAscii, readBytes, Reader, readU16, readU32, readU64 } from '../reader';
18
- import { parseId3V2Tag, readId3V2Header } from '../id3';
19
-
20
- export enum WaveFormat {
21
- PCM = 0x0001,
22
- IEEE_FLOAT = 0x0003,
23
- ALAW = 0x0006,
24
- MULAW = 0x0007,
25
- EXTENSIBLE = 0xFFFE,
26
- }
27
-
28
- export class WaveDemuxer extends Demuxer {
29
- reader: Reader;
30
-
31
- metadataPromise: Promise<void> | null = null;
32
- dataStart = -1;
33
- dataSize = -1;
34
- audioInfo: {
35
- format: number;
36
- numberOfChannels: number;
37
- sampleRate: number;
38
- sampleSizeInBytes: number;
39
- blockSizeInBytes: number;
40
- } | null = null;
41
-
42
- tracks: InputAudioTrack[] = [];
43
- lastKnownPacketIndex = 0;
44
- metadataTags: MetadataTags = {};
45
-
46
- constructor(input: Input) {
47
- super(input);
48
-
49
- this.reader = input._reader;
50
- }
51
-
52
- async readMetadata() {
53
- return this.metadataPromise ??= (async () => {
54
- let slice = this.reader.requestSlice(0, 12);
55
- if (slice instanceof Promise) slice = await slice;
56
- assert(slice);
57
-
58
- const riffType = readAscii(slice, 4);
59
- const littleEndian = riffType !== 'RIFX';
60
-
61
- const isRf64 = riffType === 'RF64';
62
-
63
- const outerChunkSize = readU32(slice, littleEndian);
64
-
65
- let totalFileSize = isRf64
66
- ? this.reader.fileSize
67
- : Math.min(outerChunkSize + 8, this.reader.fileSize ?? Infinity);
68
- const format = readAscii(slice, 4);
69
-
70
- if (format !== 'WAVE') {
71
- throw new Error('Invalid WAVE file - wrong format');
72
- }
73
-
74
- let chunksRead = 0;
75
- let dataChunkSize: number | null = null;
76
- let currentPos = slice.filePos;
77
-
78
- while (totalFileSize === null || currentPos < totalFileSize) {
79
- let slice = this.reader.requestSlice(currentPos, 8);
80
- if (slice instanceof Promise) slice = await slice;
81
- if (!slice) break;
82
-
83
- const chunkId = readAscii(slice, 4);
84
- const chunkSize = readU32(slice, littleEndian);
85
- const startPos = slice.filePos;
86
-
87
- if (isRf64 && chunksRead === 0 && chunkId !== 'ds64') {
88
- throw new Error('Invalid RF64 file: First chunk must be "ds64".');
89
- }
90
-
91
- if (chunkId === 'fmt ') {
92
- await this.parseFmtChunk(startPos, chunkSize, littleEndian);
93
- } else if (chunkId === 'data') {
94
- dataChunkSize ??= chunkSize;
95
-
96
- this.dataStart = slice.filePos;
97
- this.dataSize = Math.min(dataChunkSize, (totalFileSize ?? Infinity) - this.dataStart);
98
-
99
- if (this.reader.fileSize === null) {
100
- break; // Stop once we hit the data chunk
101
- }
102
- } else if (chunkId === 'ds64') {
103
- // File and data chunk sizes are defined in here instead
104
-
105
- let ds64Slice = this.reader.requestSlice(startPos, chunkSize);
106
- if (ds64Slice instanceof Promise) ds64Slice = await ds64Slice;
107
- if (!ds64Slice) break;
108
-
109
- const riffChunkSize = readU64(ds64Slice, littleEndian);
110
- dataChunkSize = readU64(ds64Slice, littleEndian);
111
-
112
- totalFileSize = Math.min(riffChunkSize + 8, this.reader.fileSize ?? Infinity);
113
- } else if (chunkId === 'LIST') {
114
- await this.parseListChunk(startPos, chunkSize, littleEndian);
115
- } else if (chunkId === 'ID3 ' || chunkId === 'id3 ') {
116
- await this.parseId3Chunk(startPos, chunkSize);
117
- }
118
-
119
- currentPos = startPos + chunkSize + (chunkSize & 1); // Handle padding
120
- chunksRead++;
121
- }
122
-
123
- if (!this.audioInfo) {
124
- throw new Error('Invalid WAVE file - missing "fmt " chunk');
125
- }
126
- if (this.dataStart === -1) {
127
- throw new Error('Invalid WAVE file - missing "data" chunk');
128
- }
129
-
130
- const blockSize = this.audioInfo.blockSizeInBytes;
131
- this.dataSize = Math.floor(this.dataSize / blockSize) * blockSize;
132
-
133
- this.tracks.push(new InputAudioTrack(this.input, new WaveAudioTrackBacking(this)));
134
- })();
135
- }
136
-
137
- private async parseFmtChunk(startPos: number, size: number, littleEndian: boolean) {
138
- let slice = this.reader.requestSlice(startPos, size);
139
- if (slice instanceof Promise) slice = await slice;
140
- if (!slice) return; // File too short
141
-
142
- let formatTag = readU16(slice, littleEndian);
143
- const numChannels = readU16(slice, littleEndian);
144
- const sampleRate = readU32(slice, littleEndian);
145
- slice.skip(4); // Bytes per second
146
- const blockAlign = readU16(slice, littleEndian);
147
-
148
- let bitsPerSample: number;
149
-
150
- if (size === 14) { // Plain WAVEFORMAT
151
- bitsPerSample = 8;
152
- } else {
153
- bitsPerSample = readU16(slice, littleEndian);
154
- }
155
-
156
- // Handle WAVEFORMATEXTENSIBLE
157
- if (size >= 18 && formatTag !== 0x0165) {
158
- const cbSize = readU16(slice, littleEndian);
159
- const remainingSize = size - 18;
160
- const extensionSize = Math.min(remainingSize, cbSize);
161
-
162
- if (extensionSize >= 22 && formatTag === WaveFormat.EXTENSIBLE) {
163
- // Parse WAVEFORMATEXTENSIBLE
164
- slice.skip(2 + 4);
165
- const subFormat = readBytes(slice, 16);
166
-
167
- // Get actual format from subFormat GUID
168
- formatTag = subFormat[0]! | (subFormat[1]! << 8);
169
- }
170
- }
171
-
172
- if (formatTag === WaveFormat.MULAW || formatTag === WaveFormat.ALAW) {
173
- bitsPerSample = 8;
174
- }
175
-
176
- this.audioInfo = {
177
- format: formatTag,
178
- numberOfChannels: numChannels,
179
- sampleRate,
180
- sampleSizeInBytes: Math.ceil(bitsPerSample / 8),
181
- blockSizeInBytes: blockAlign,
182
- };
183
- }
184
-
185
- private async parseListChunk(startPos: number, size: number, littleEndian: boolean) {
186
- let slice = this.reader.requestSlice(startPos, size);
187
- if (slice instanceof Promise) slice = await slice;
188
- if (!slice) return; // File too short
189
-
190
- const infoType = readAscii(slice, 4);
191
- if (infoType !== 'INFO' && infoType !== 'INF0') { // exiftool.org claims INF0 can happen
192
- return; // Not an INFO chunk
193
- }
194
-
195
- let currentPos = slice.filePos;
196
- while (currentPos <= startPos + size - 8) {
197
- slice.filePos = currentPos;
198
-
199
- const chunkName = readAscii(slice, 4);
200
- const chunkSize = readU32(slice, littleEndian);
201
- const bytes = readBytes(slice, chunkSize);
202
-
203
- let stringLength = 0;
204
- for (let i = 0; i < bytes.length; i++) {
205
- if (bytes[i] === 0) {
206
- break;
207
- }
208
-
209
- stringLength++;
210
- }
211
-
212
- const value = String.fromCharCode(...bytes.subarray(0, stringLength));
213
-
214
- this.metadataTags.raw ??= {};
215
- this.metadataTags.raw[chunkName] = value;
216
-
217
- switch (chunkName) {
218
- case 'INAM':
219
- case 'TITL': {
220
- this.metadataTags.title ??= value;
221
- }; break;
222
-
223
- case 'TIT3': {
224
- this.metadataTags.description ??= value;
225
- }; break;
226
-
227
- case 'IART': {
228
- this.metadataTags.artist ??= value;
229
- }; break;
230
-
231
- case 'IPRD': {
232
- this.metadataTags.album ??= value;
233
- }; break;
234
-
235
- case 'IPRT':
236
- case 'ITRK':
237
- case 'TRCK': {
238
- const parts = value.split('/');
239
- const trackNum = Number.parseInt(parts[0]!, 10);
240
- const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
241
-
242
- if (Number.isInteger(trackNum) && trackNum > 0) {
243
- this.metadataTags.trackNumber ??= trackNum;
244
- }
245
- if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
246
- this.metadataTags.tracksTotal ??= tracksTotal;
247
- }
248
- }; break;
249
-
250
- case 'ICRD':
251
- case 'IDIT': {
252
- const date = new Date(value);
253
- if (!Number.isNaN(date.getTime())) {
254
- this.metadataTags.date ??= date;
255
- }
256
- }; break;
257
-
258
- case 'YEAR': {
259
- const year = Number.parseInt(value, 10);
260
- if (Number.isInteger(year) && year > 0) {
261
- this.metadataTags.date ??= new Date(year, 0, 1);
262
- }
263
- }; break;
264
-
265
- case 'IGNR':
266
- case 'GENR': {
267
- this.metadataTags.genre ??= value;
268
- }; break;
269
-
270
- case 'ICMT':
271
- case 'CMNT':
272
- case 'COMM': {
273
- this.metadataTags.comment ??= value;
274
- }; break;
275
- }
276
-
277
- currentPos += 8 + chunkSize + (chunkSize & 1); // Handle padding
278
- }
279
- }
280
-
281
- private async parseId3Chunk(startPos: number, size: number) {
282
- // Parse ID3 tag embedded in WAV file (non-default, but used a lot in practice anyway)
283
- let slice = this.reader.requestSlice(startPos, size);
284
- if (slice instanceof Promise) slice = await slice;
285
- if (!slice) return; // File too short
286
-
287
- const id3V2Header = readId3V2Header(slice);
288
- if (id3V2Header) {
289
- // Extract the content portion (skip the 10-byte header)
290
- const contentSlice = slice.slice(startPos + 10, id3V2Header.size);
291
-
292
- parseId3V2Tag(contentSlice, id3V2Header, this.metadataTags);
293
- }
294
- }
295
-
296
- getCodec(): AudioCodec | null {
297
- assert(this.audioInfo);
298
-
299
- if (this.audioInfo.format === WaveFormat.MULAW) {
300
- return 'ulaw';
301
- }
302
- if (this.audioInfo.format === WaveFormat.ALAW) {
303
- return 'alaw';
304
- }
305
- if (this.audioInfo.format === WaveFormat.PCM) {
306
- // All formats are little-endian
307
- if (this.audioInfo.sampleSizeInBytes === 1) {
308
- return 'pcm-u8';
309
- } else if (this.audioInfo.sampleSizeInBytes === 2) {
310
- return 'pcm-s16';
311
- } else if (this.audioInfo.sampleSizeInBytes === 3) {
312
- return 'pcm-s24';
313
- } else if (this.audioInfo.sampleSizeInBytes === 4) {
314
- return 'pcm-s32';
315
- }
316
- }
317
- if (this.audioInfo.format === WaveFormat.IEEE_FLOAT) {
318
- if (this.audioInfo.sampleSizeInBytes === 4) {
319
- return 'pcm-f32';
320
- }
321
- }
322
-
323
- return null;
324
- }
325
-
326
- async getMimeType() {
327
- return 'audio/wav';
328
- }
329
-
330
- async computeDuration() {
331
- await this.readMetadata();
332
-
333
- const track = this.tracks[0];
334
- assert(track);
335
-
336
- return track.computeDuration();
337
- }
338
-
339
- async getTracks() {
340
- await this.readMetadata();
341
- return this.tracks;
342
- }
343
-
344
- async getMetadataTags() {
345
- await this.readMetadata();
346
- return this.metadataTags;
347
- }
348
- }
349
-
350
- const PACKET_SIZE_IN_FRAMES = 2048;
351
-
352
- class WaveAudioTrackBacking implements InputAudioTrackBacking {
353
- constructor(public demuxer: WaveDemuxer) {}
354
-
355
- getId() {
356
- return 1;
357
- }
358
-
359
- getCodec() {
360
- return this.demuxer.getCodec();
361
- }
362
-
363
- getInternalCodecId() {
364
- assert(this.demuxer.audioInfo);
365
- return this.demuxer.audioInfo.format;
366
- }
367
-
368
- async getDecoderConfig(): Promise<AudioDecoderConfig | null> {
369
- const codec = this.demuxer.getCodec();
370
- if (!codec) {
371
- return null;
372
- }
373
-
374
- assert(this.demuxer.audioInfo);
375
- return {
376
- codec,
377
- numberOfChannels: this.demuxer.audioInfo.numberOfChannels,
378
- sampleRate: this.demuxer.audioInfo.sampleRate,
379
- };
380
- }
381
-
382
- async computeDuration() {
383
- const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
384
- return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
385
- }
386
-
387
- getNumberOfChannels() {
388
- assert(this.demuxer.audioInfo);
389
- return this.demuxer.audioInfo.numberOfChannels;
390
- }
391
-
392
- getSampleRate() {
393
- assert(this.demuxer.audioInfo);
394
- return this.demuxer.audioInfo.sampleRate;
395
- }
396
-
397
- getTimeResolution() {
398
- assert(this.demuxer.audioInfo);
399
- return this.demuxer.audioInfo.sampleRate;
400
- }
401
-
402
- getName() {
403
- return null;
404
- }
405
-
406
- getLanguageCode() {
407
- return UNDETERMINED_LANGUAGE;
408
- }
409
-
410
- getDisposition() {
411
- return {
412
- ...DEFAULT_TRACK_DISPOSITION,
413
- };
414
- }
415
-
416
- async getFirstTimestamp() {
417
- return 0;
418
- }
419
-
420
- private async getPacketAtIndex(
421
- packetIndex: number,
422
- options: PacketRetrievalOptions,
423
- ): Promise<EncodedPacket | null> {
424
- assert(this.demuxer.audioInfo);
425
- const startOffset = packetIndex * PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes;
426
- if (startOffset >= this.demuxer.dataSize) {
427
- return null;
428
- }
429
-
430
- const sizeInBytes = Math.min(
431
- PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes,
432
- this.demuxer.dataSize - startOffset,
433
- );
434
-
435
- if (this.demuxer.reader.fileSize === null) {
436
- // If the file size is unknown, we weren't able to cap the dataSize in the init logic and we instead have to
437
- // rely on the headers telling us how large the file is. But, these might be wrong, so let's check if the
438
- // requested slice actually exists.
439
-
440
- let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes);
441
- if (slice instanceof Promise) slice = await slice;
442
-
443
- if (!slice) {
444
- return null;
445
- }
446
- }
447
-
448
- let data: Uint8Array;
449
- if (options.metadataOnly) {
450
- data = PLACEHOLDER_DATA;
451
- } else {
452
- let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes);
453
- if (slice instanceof Promise) slice = await slice;
454
- assert(slice);
455
-
456
- data = readBytes(slice, sizeInBytes);
457
- }
458
-
459
- const timestamp = packetIndex * PACKET_SIZE_IN_FRAMES / this.demuxer.audioInfo.sampleRate;
460
- const duration = sizeInBytes / this.demuxer.audioInfo.blockSizeInBytes / this.demuxer.audioInfo.sampleRate;
461
-
462
- this.demuxer.lastKnownPacketIndex = Math.max(
463
- packetIndex,
464
- timestamp,
465
- );
466
-
467
- return new EncodedPacket(
468
- data,
469
- 'key',
470
- timestamp,
471
- duration,
472
- packetIndex,
473
- sizeInBytes,
474
- );
475
- }
476
-
477
- getFirstPacket(options: PacketRetrievalOptions) {
478
- return this.getPacketAtIndex(0, options);
479
- }
480
-
481
- async getPacket(timestamp: number, options: PacketRetrievalOptions) {
482
- assert(this.demuxer.audioInfo);
483
-
484
- const packetIndex = Math.floor(Math.min(
485
- timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES,
486
- (this.demuxer.dataSize - 1) / (PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes),
487
- ));
488
-
489
- const packet = await this.getPacketAtIndex(packetIndex, options);
490
- if (packet) {
491
- return packet;
492
- }
493
-
494
- if (packetIndex === 0) {
495
- return null; // Empty data chunk
496
- }
497
-
498
- assert(this.demuxer.reader.fileSize === null);
499
-
500
- // The file is shorter than we thought, meaning the packet we were looking for doesn't exist. So, let's find
501
- // the last packet by doing a sequential scan, instead.
502
- let currentPacket = await this.getPacketAtIndex(this.demuxer.lastKnownPacketIndex, options);
503
- while (currentPacket) {
504
- const nextPacket = await this.getNextPacket(currentPacket, options);
505
- if (!nextPacket) {
506
- break;
507
- }
508
-
509
- currentPacket = nextPacket;
510
- }
511
-
512
- return currentPacket;
513
- }
514
-
515
- getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
516
- assert(this.demuxer.audioInfo);
517
- const packetIndex = Math.round(packet.timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES);
518
-
519
- return this.getPacketAtIndex(packetIndex + 1, options);
520
- }
521
-
522
- getKeyPacket(timestamp: number, options: PacketRetrievalOptions) {
523
- return this.getPacket(timestamp, options);
524
- }
525
-
526
- getNextKeyPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
527
- return this.getNextPacket(packet, options);
528
- }
529
- }