@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,78 +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
- import { parseOpusTocByte } from '../codec-data.js';
9
- import { assert, ilog, toDataView } from '../misc.js';
10
- export const OGGS = 0x5367674f; // 'OggS'
11
- const OGG_CRC_POLYNOMIAL = 0x04c11db7;
12
- const OGG_CRC_TABLE = new Uint32Array(256);
13
- for (let n = 0; n < 256; n++) {
14
- let crc = n << 24;
15
- for (let k = 0; k < 8; k++) {
16
- crc = (crc & 0x80000000)
17
- ? ((crc << 1) ^ OGG_CRC_POLYNOMIAL)
18
- : (crc << 1);
19
- }
20
- OGG_CRC_TABLE[n] = (crc >>> 0) & 0xffffffff;
21
- }
22
- export const computeOggPageCrc = (bytes) => {
23
- const view = toDataView(bytes);
24
- const originalChecksum = view.getUint32(22, true);
25
- view.setUint32(22, 0, true); // Zero out checksum field
26
- let crc = 0;
27
- for (let i = 0; i < bytes.length; i++) {
28
- const byte = bytes[i];
29
- crc = ((crc << 8) ^ OGG_CRC_TABLE[(crc >>> 24) ^ byte]) >>> 0;
30
- }
31
- view.setUint32(22, originalChecksum, true); // Restore checksum field
32
- return crc;
33
- };
34
- export const extractSampleMetadata = (data, codecInfo, vorbisLastBlocksize) => {
35
- let durationInSamples = 0;
36
- let currentBlocksize = null;
37
- if (data.length > 0) {
38
- // To know sample duration, we'll need to peak inside the packet
39
- if (codecInfo.codec === 'vorbis') {
40
- assert(codecInfo.vorbisInfo);
41
- const vorbisModeCount = codecInfo.vorbisInfo.modeBlockflags.length;
42
- const bitCount = ilog(vorbisModeCount - 1);
43
- const modeMask = ((1 << bitCount) - 1) << 1;
44
- const modeNumber = (data[0] & modeMask) >> 1;
45
- if (modeNumber >= codecInfo.vorbisInfo.modeBlockflags.length) {
46
- throw new Error('Invalid mode number.');
47
- }
48
- // In Vorbis, packet duration also depends on the blocksize of the previous packet
49
- let prevBlocksize = vorbisLastBlocksize;
50
- const blockflag = codecInfo.vorbisInfo.modeBlockflags[modeNumber];
51
- currentBlocksize = codecInfo.vorbisInfo.blocksizes[blockflag];
52
- if (blockflag === 1) {
53
- const prevMask = (modeMask | 0x1) + 1;
54
- const flag = data[0] & prevMask ? 1 : 0;
55
- prevBlocksize = codecInfo.vorbisInfo.blocksizes[flag];
56
- }
57
- durationInSamples = prevBlocksize !== null
58
- ? (prevBlocksize + currentBlocksize) >> 2
59
- : 0; // The first sample outputs no audio data and therefore has a duration of 0
60
- }
61
- else if (codecInfo.codec === 'opus') {
62
- const toc = parseOpusTocByte(data);
63
- durationInSamples = toc.durationInSamples;
64
- }
65
- }
66
- return {
67
- durationInSamples,
68
- vorbisBlockSize: currentBlocksize,
69
- };
70
- };
71
- export const buildOggMimeType = (info) => {
72
- let string = 'audio/ogg';
73
- if (info.codecStrings) {
74
- const uniqueCodecMimeTypes = [...new Set(info.codecStrings)];
75
- string += `; codecs="${uniqueCodecMimeTypes.join(', ')}"`;
76
- }
77
- return string;
78
- };
@@ -1,353 +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
- import { OPUS_SAMPLE_RATE, validateAudioChunkMetadata } from '../codec.js';
9
- import { createVorbisComments, parseModesFromVorbisSetupPacket, parseOpusIdentificationHeader } from '../codec-data.js';
10
- import { assert, promiseWithResolvers, setInt64, toDataView, toUint8Array, } from '../misc.js';
11
- import { Muxer } from '../muxer.js';
12
- import { buildOggMimeType, computeOggPageCrc, extractSampleMetadata, OGGS, } from './ogg-misc.js';
13
- import { MAX_PAGE_SIZE } from './ogg-reader.js';
14
- const PAGE_SIZE_TARGET = 8192;
15
- export class OggMuxer extends Muxer {
16
- constructor(output, format) {
17
- super(output);
18
- this.trackDatas = [];
19
- this.bosPagesWritten = false;
20
- this.allTracksKnown = promiseWithResolvers();
21
- this.pageBytes = new Uint8Array(MAX_PAGE_SIZE);
22
- this.pageView = new DataView(this.pageBytes.buffer);
23
- this.format = format;
24
- this.writer = output._writer;
25
- this.writer.ensureMonotonicity = true; // Ogg is always monotonically written!
26
- }
27
- async start() {
28
- // Nothin'
29
- }
30
- async getMimeType() {
31
- await this.allTracksKnown.promise;
32
- return buildOggMimeType({
33
- codecStrings: this.trackDatas.map(x => x.codecInfo.codec),
34
- });
35
- }
36
- addEncodedVideoPacket() {
37
- throw new Error('Video tracks are not supported.');
38
- }
39
- getTrackData(track, meta) {
40
- const existingTrackData = this.trackDatas.find(td => td.track === track);
41
- if (existingTrackData) {
42
- return existingTrackData;
43
- }
44
- // Give the track a unique random serial number
45
- let serialNumber;
46
- do {
47
- serialNumber = Math.floor(2 ** 32 * Math.random());
48
- } while (this.trackDatas.some(td => td.serialNumber === serialNumber));
49
- assert(track.source._codec === 'vorbis' || track.source._codec === 'opus');
50
- validateAudioChunkMetadata(meta);
51
- assert(meta);
52
- assert(meta.decoderConfig);
53
- const newTrackData = {
54
- track,
55
- serialNumber,
56
- internalSampleRate: track.source._codec === 'opus'
57
- ? OPUS_SAMPLE_RATE
58
- : meta.decoderConfig.sampleRate,
59
- codecInfo: {
60
- codec: track.source._codec,
61
- vorbisInfo: null,
62
- opusInfo: null,
63
- },
64
- vorbisLastBlocksize: null,
65
- packetQueue: [],
66
- currentTimestampInSamples: 0,
67
- pagesWritten: 0,
68
- currentGranulePosition: 0,
69
- currentLacingValues: [],
70
- currentPageData: [],
71
- currentPageSize: 27,
72
- currentPageStartsWithFreshPacket: true,
73
- };
74
- this.queueHeaderPackets(newTrackData, meta);
75
- this.trackDatas.push(newTrackData);
76
- if (this.allTracksAreKnown()) {
77
- this.allTracksKnown.resolve();
78
- }
79
- return newTrackData;
80
- }
81
- queueHeaderPackets(trackData, meta) {
82
- assert(meta.decoderConfig);
83
- if (trackData.track.source._codec === 'vorbis') {
84
- assert(meta.decoderConfig.description);
85
- const bytes = toUint8Array(meta.decoderConfig.description);
86
- if (bytes[0] !== 2) {
87
- throw new TypeError('First byte of Vorbis decoder description must be 2.');
88
- }
89
- let pos = 1;
90
- const readPacketLength = () => {
91
- let length = 0;
92
- while (true) {
93
- const value = bytes[pos++];
94
- if (value === undefined) {
95
- throw new TypeError('Vorbis decoder description is too short.');
96
- }
97
- length += value;
98
- if (value < 255) {
99
- return length;
100
- }
101
- }
102
- };
103
- const identificationHeaderLength = readPacketLength();
104
- const commentHeaderLength = readPacketLength();
105
- const setupHeaderLength = bytes.length - pos; // Setup header fills the remaining bytes
106
- if (setupHeaderLength <= 0) {
107
- throw new TypeError('Vorbis decoder description is too short.');
108
- }
109
- const identificationHeader = bytes.subarray(pos, pos += identificationHeaderLength);
110
- pos += commentHeaderLength; // Skip the comment header, we'll build our own
111
- const setupHeader = bytes.subarray(pos);
112
- const commentHeaderHeader = new Uint8Array(7);
113
- commentHeaderHeader[0] = 3; // Packet type
114
- commentHeaderHeader[1] = 0x76; // 'v'
115
- commentHeaderHeader[2] = 0x6f; // 'o'
116
- commentHeaderHeader[3] = 0x72; // 'r'
117
- commentHeaderHeader[4] = 0x62; // 'b'
118
- commentHeaderHeader[5] = 0x69; // 'i'
119
- commentHeaderHeader[6] = 0x73; // 's'
120
- const commentHeader = createVorbisComments(commentHeaderHeader, this.output._metadataTags, true);
121
- trackData.packetQueue.push({
122
- data: identificationHeader,
123
- endGranulePosition: 0,
124
- timestamp: 0,
125
- forcePageFlush: true,
126
- }, {
127
- data: commentHeader,
128
- endGranulePosition: 0,
129
- timestamp: 0,
130
- forcePageFlush: false,
131
- }, {
132
- data: setupHeader,
133
- endGranulePosition: 0,
134
- timestamp: 0,
135
- forcePageFlush: true, // The last header packet must flush the page
136
- });
137
- const view = toDataView(identificationHeader);
138
- const blockSizeByte = view.getUint8(28);
139
- trackData.codecInfo.vorbisInfo = {
140
- blocksizes: [
141
- 1 << (blockSizeByte & 0xf),
142
- 1 << (blockSizeByte >> 4),
143
- ],
144
- modeBlockflags: parseModesFromVorbisSetupPacket(setupHeader).modeBlockflags,
145
- };
146
- }
147
- else if (trackData.track.source._codec === 'opus') {
148
- if (!meta.decoderConfig.description) {
149
- throw new TypeError('For Ogg, Opus decoder description is required.');
150
- }
151
- const identificationHeader = toUint8Array(meta.decoderConfig.description);
152
- const commentHeaderHeader = new Uint8Array(8);
153
- const commentHeaderHeaderView = toDataView(commentHeaderHeader);
154
- commentHeaderHeaderView.setUint32(0, 0x4f707573, false); // 'Opus'
155
- commentHeaderHeaderView.setUint32(4, 0x54616773, false); // 'Tags'
156
- const commentHeader = createVorbisComments(commentHeaderHeader, this.output._metadataTags, true);
157
- trackData.packetQueue.push({
158
- data: identificationHeader,
159
- endGranulePosition: 0,
160
- timestamp: 0,
161
- forcePageFlush: true,
162
- }, {
163
- data: commentHeader,
164
- endGranulePosition: 0,
165
- timestamp: 0,
166
- forcePageFlush: true, // The last header packet must flush the page
167
- });
168
- trackData.codecInfo.opusInfo = {
169
- preSkip: parseOpusIdentificationHeader(identificationHeader).preSkip,
170
- };
171
- }
172
- }
173
- async addEncodedAudioPacket(track, packet, meta) {
174
- const release = await this.mutex.acquire();
175
- try {
176
- const trackData = this.getTrackData(track, meta);
177
- this.validateAndNormalizeTimestamp(trackData.track, packet.timestamp, packet.type === 'key');
178
- const currentTimestampInSamples = trackData.currentTimestampInSamples;
179
- const { durationInSamples, vorbisBlockSize } = extractSampleMetadata(packet.data, trackData.codecInfo, trackData.vorbisLastBlocksize);
180
- trackData.currentTimestampInSamples += durationInSamples;
181
- trackData.vorbisLastBlocksize = vorbisBlockSize;
182
- trackData.packetQueue.push({
183
- data: packet.data,
184
- endGranulePosition: trackData.currentTimestampInSamples,
185
- timestamp: currentTimestampInSamples / trackData.internalSampleRate,
186
- forcePageFlush: false,
187
- });
188
- await this.interleavePages();
189
- }
190
- finally {
191
- release();
192
- }
193
- }
194
- addSubtitleCue() {
195
- throw new Error('Subtitle tracks are not supported.');
196
- }
197
- allTracksAreKnown() {
198
- for (const track of this.output._tracks) {
199
- if (!track.source._closed && !this.trackDatas.some(x => x.track === track)) {
200
- return false; // We haven't seen a sample from this open track yet
201
- }
202
- }
203
- return true;
204
- }
205
- async interleavePages(isFinalCall = false) {
206
- if (!this.bosPagesWritten) {
207
- if (!this.allTracksAreKnown()) {
208
- return; // We can't interleave yet as we don't yet know how many tracks we'll truly have
209
- }
210
- // Write the header page for all bitstreams
211
- for (const trackData of this.trackDatas) {
212
- while (trackData.packetQueue.length > 0) {
213
- const packet = trackData.packetQueue.shift();
214
- this.writePacket(trackData, packet, false);
215
- if (packet.forcePageFlush) {
216
- // We say the header page ends once the first packet is encountered that forces a page flush
217
- break;
218
- }
219
- }
220
- }
221
- this.bosPagesWritten = true;
222
- }
223
- outer: while (true) {
224
- let trackWithMinTimestamp = null;
225
- let minTimestamp = Infinity;
226
- for (const trackData of this.trackDatas) {
227
- if (!isFinalCall
228
- && trackData.packetQueue.length <= 1 // Limit is 1, not 0, for correct EOS flag logic
229
- && !trackData.track.source._closed) {
230
- break outer;
231
- }
232
- if (trackData.packetQueue.length > 0
233
- && trackData.packetQueue[0].timestamp < minTimestamp) {
234
- trackWithMinTimestamp = trackData;
235
- minTimestamp = trackData.packetQueue[0].timestamp;
236
- }
237
- }
238
- if (!trackWithMinTimestamp) {
239
- break;
240
- }
241
- const packet = trackWithMinTimestamp.packetQueue.shift();
242
- const isFinalPacket = trackWithMinTimestamp.packetQueue.length === 0;
243
- this.writePacket(trackWithMinTimestamp, packet, isFinalPacket);
244
- }
245
- if (!isFinalCall) {
246
- await this.writer.flush();
247
- }
248
- }
249
- writePacket(trackData, packet, isFinalPacket) {
250
- let remainingLength = packet.data.length;
251
- let dataStartOffset = 0;
252
- let dataOffset = 0;
253
- while (true) {
254
- if (trackData.currentLacingValues.length === 0 && dataStartOffset > 0) {
255
- // This is a packet spanning multiple pages
256
- trackData.currentPageStartsWithFreshPacket = false;
257
- }
258
- const segmentSize = Math.min(255, remainingLength);
259
- trackData.currentLacingValues.push(segmentSize);
260
- trackData.currentPageSize++;
261
- dataOffset += segmentSize;
262
- const segmentIsLastOfPacket = remainingLength < 255;
263
- if (trackData.currentLacingValues.length === 255) {
264
- // The page is full, we need to add part of the packet data and then flush the page
265
- const slice = packet.data.subarray(dataStartOffset, dataOffset);
266
- dataStartOffset = dataOffset;
267
- trackData.currentPageData.push(slice);
268
- trackData.currentPageSize += slice.length;
269
- this.writePage(trackData, isFinalPacket && segmentIsLastOfPacket);
270
- if (segmentIsLastOfPacket) {
271
- return;
272
- }
273
- }
274
- if (segmentIsLastOfPacket) {
275
- break;
276
- }
277
- remainingLength -= 255;
278
- }
279
- const slice = packet.data.subarray(dataStartOffset);
280
- trackData.currentPageData.push(slice);
281
- trackData.currentPageSize += slice.length;
282
- trackData.currentGranulePosition = packet.endGranulePosition;
283
- if (trackData.currentPageSize >= PAGE_SIZE_TARGET || packet.forcePageFlush) {
284
- this.writePage(trackData, isFinalPacket);
285
- }
286
- }
287
- writePage(trackData, isEos) {
288
- this.pageView.setUint32(0, OGGS, true); // Capture pattern
289
- this.pageView.setUint8(4, 0); // Version
290
- let headerType = 0;
291
- if (!trackData.currentPageStartsWithFreshPacket) {
292
- headerType |= 1;
293
- }
294
- if (trackData.pagesWritten === 0) {
295
- headerType |= 2; // Beginning of stream
296
- }
297
- if (isEos) {
298
- headerType |= 4; // End of stream
299
- }
300
- this.pageView.setUint8(5, headerType); // Header type
301
- const granulePosition = trackData.currentLacingValues.every(x => x === 255)
302
- ? -1 // No packets end on this page
303
- : trackData.currentGranulePosition;
304
- setInt64(this.pageView, 6, granulePosition, true); // Granule position
305
- this.pageView.setUint32(14, trackData.serialNumber, true); // Serial number
306
- this.pageView.setUint32(18, trackData.pagesWritten, true); // Page sequence number
307
- this.pageView.setUint32(22, 0, true); // Checksum placeholder
308
- this.pageView.setUint8(26, trackData.currentLacingValues.length); // Number of page segments
309
- this.pageBytes.set(trackData.currentLacingValues, 27);
310
- let pos = 27 + trackData.currentLacingValues.length;
311
- for (const data of trackData.currentPageData) {
312
- this.pageBytes.set(data, pos);
313
- pos += data.length;
314
- }
315
- const slice = this.pageBytes.subarray(0, pos);
316
- const crc = computeOggPageCrc(slice);
317
- this.pageView.setUint32(22, crc, true); // Checksum
318
- trackData.pagesWritten++;
319
- trackData.currentLacingValues.length = 0;
320
- trackData.currentPageData.length = 0;
321
- trackData.currentPageSize = 27;
322
- trackData.currentPageStartsWithFreshPacket = true;
323
- if (this.format._options.onPage) {
324
- this.writer.startTrackingWrites();
325
- }
326
- this.writer.write(slice);
327
- if (this.format._options.onPage) {
328
- const { data, start } = this.writer.stopTrackingWrites();
329
- this.format._options.onPage(data, start, trackData.track.source);
330
- }
331
- }
332
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
333
- async onTrackClose() {
334
- const release = await this.mutex.acquire();
335
- if (this.allTracksAreKnown()) {
336
- this.allTracksKnown.resolve();
337
- }
338
- // Since a track is now closed, we may be able to write out chunks that were previously waiting
339
- await this.interleavePages();
340
- release();
341
- }
342
- async finalize() {
343
- const release = await this.mutex.acquire();
344
- this.allTracksKnown.resolve();
345
- await this.interleavePages(true);
346
- for (const trackData of this.trackDatas) {
347
- if (trackData.currentLacingValues.length > 0) {
348
- this.writePage(trackData, true);
349
- }
350
- }
351
- release();
352
- }
353
- }
@@ -1,65 +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
- import { readI64Le, readU32Le, readU8 } from '../reader.js';
9
- import { OGGS } from './ogg-misc.js';
10
- export const MIN_PAGE_HEADER_SIZE = 27;
11
- export const MAX_PAGE_HEADER_SIZE = 27 + 255;
12
- export const MAX_PAGE_SIZE = /* #__PURE__ */ MAX_PAGE_HEADER_SIZE + 255 * 255;
13
- export const readPageHeader = (slice) => {
14
- const startPos = slice.filePos;
15
- const capturePattern = readU32Le(slice);
16
- if (capturePattern !== OGGS) {
17
- return null;
18
- }
19
- slice.skip(1); // Version
20
- const headerType = readU8(slice);
21
- const granulePosition = readI64Le(slice);
22
- const serialNumber = readU32Le(slice);
23
- const sequenceNumber = readU32Le(slice);
24
- const checksum = readU32Le(slice);
25
- const numberPageSegments = readU8(slice);
26
- const lacingValues = new Uint8Array(numberPageSegments);
27
- for (let i = 0; i < numberPageSegments; i++) {
28
- lacingValues[i] = readU8(slice);
29
- }
30
- const headerSize = 27 + numberPageSegments;
31
- const dataSize = lacingValues.reduce((a, b) => a + b, 0);
32
- const totalSize = headerSize + dataSize;
33
- return {
34
- headerStartPos: startPos,
35
- totalSize,
36
- dataStartPos: startPos + headerSize,
37
- dataSize,
38
- headerType,
39
- granulePosition,
40
- serialNumber,
41
- sequenceNumber,
42
- checksum,
43
- lacingValues,
44
- };
45
- };
46
- export const findNextPageHeader = (slice, until) => {
47
- while (slice.filePos < until - (4 - 1)) { // Size of word minus 1
48
- const word = readU32Le(slice);
49
- const firstByte = word & 0xff;
50
- const secondByte = (word >>> 8) & 0xff;
51
- const thirdByte = (word >>> 16) & 0xff;
52
- const fourthByte = (word >>> 24) & 0xff;
53
- const O = 0x4f; // 'O'
54
- if (firstByte !== O && secondByte !== O && thirdByte !== O && fourthByte !== O) {
55
- continue;
56
- }
57
- slice.skip(-4);
58
- if (word === OGGS) {
59
- // We have found the capture pattern
60
- return true;
61
- }
62
- slice.skip(1);
63
- }
64
- return false;
65
- };