@kenzuya/mediabunny 1.26.0 → 1.28.6

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 (238) hide show
  1. package/README.md +1 -1
  2. package/dist/bundles/{mediabunny.mjs → mediabunny.js} +21963 -21390
  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/ogg/ogg-demuxer.d.ts +7 -7
  83. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  84. package/dist/modules/src/ogg/ogg-misc.d.ts +1 -1
  85. package/dist/modules/src/ogg/ogg-misc.d.ts.map +1 -1
  86. package/dist/modules/src/ogg/ogg-muxer.d.ts +5 -5
  87. package/dist/modules/src/ogg/ogg-muxer.d.ts.map +1 -1
  88. package/dist/modules/src/ogg/ogg-reader.d.ts +1 -1
  89. package/dist/modules/src/ogg/ogg-reader.d.ts.map +1 -1
  90. package/dist/modules/src/output-format.d.ts +51 -6
  91. package/dist/modules/src/output-format.d.ts.map +1 -1
  92. package/dist/modules/src/output.d.ts +13 -13
  93. package/dist/modules/src/output.d.ts.map +1 -1
  94. package/dist/modules/src/packet.d.ts +1 -1
  95. package/dist/modules/src/packet.d.ts.map +1 -1
  96. package/dist/modules/src/pcm.d.ts.map +1 -1
  97. package/dist/modules/src/reader.d.ts +2 -2
  98. package/dist/modules/src/reader.d.ts.map +1 -1
  99. package/dist/modules/src/sample.d.ts +57 -15
  100. package/dist/modules/src/sample.d.ts.map +1 -1
  101. package/dist/modules/src/source.d.ts +3 -3
  102. package/dist/modules/src/source.d.ts.map +1 -1
  103. package/dist/modules/src/subtitles.d.ts +1 -1
  104. package/dist/modules/src/subtitles.d.ts.map +1 -1
  105. package/dist/modules/src/target.d.ts +2 -2
  106. package/dist/modules/src/target.d.ts.map +1 -1
  107. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  108. package/dist/modules/src/wave/riff-writer.d.ts +1 -1
  109. package/dist/modules/src/wave/riff-writer.d.ts.map +1 -1
  110. package/dist/modules/src/wave/wave-demuxer.d.ts +6 -6
  111. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  112. package/dist/modules/src/wave/wave-muxer.d.ts +4 -4
  113. package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
  114. package/dist/modules/src/writer.d.ts +1 -1
  115. package/dist/modules/src/writer.d.ts.map +1 -1
  116. package/dist/packages/eac3/eac3.wasm +0 -0
  117. package/dist/packages/eac3/mediabunny-eac3.js +1058 -0
  118. package/dist/packages/eac3/mediabunny-eac3.min.js +44 -0
  119. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.js +694 -0
  120. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.min.js +58 -0
  121. package/dist/packages/mpeg4/mediabunny-mpeg4.js +1198 -0
  122. package/dist/packages/mpeg4/mediabunny-mpeg4.min.js +44 -0
  123. package/dist/packages/mpeg4/xvid.wasm +0 -0
  124. package/package.json +18 -57
  125. package/dist/bundles/mediabunny.cjs +0 -26140
  126. package/dist/bundles/mediabunny.min.cjs +0 -147
  127. package/dist/bundles/mediabunny.min.mjs +0 -146
  128. package/dist/mediabunny.d.ts +0 -3319
  129. package/dist/modules/shared/mp3-misc.js +0 -147
  130. package/dist/modules/src/adts/adts-demuxer.js +0 -239
  131. package/dist/modules/src/adts/adts-muxer.js +0 -80
  132. package/dist/modules/src/adts/adts-reader.js +0 -63
  133. package/dist/modules/src/codec-data.js +0 -1730
  134. package/dist/modules/src/codec.js +0 -869
  135. package/dist/modules/src/conversion.js +0 -1459
  136. package/dist/modules/src/custom-coder.js +0 -117
  137. package/dist/modules/src/demuxer.js +0 -12
  138. package/dist/modules/src/encode.js +0 -442
  139. package/dist/modules/src/flac/flac-demuxer.js +0 -504
  140. package/dist/modules/src/flac/flac-misc.js +0 -135
  141. package/dist/modules/src/flac/flac-muxer.js +0 -222
  142. package/dist/modules/src/id3.js +0 -848
  143. package/dist/modules/src/index.js +0 -28
  144. package/dist/modules/src/input-format.js +0 -480
  145. package/dist/modules/src/input-track.js +0 -372
  146. package/dist/modules/src/input.js +0 -188
  147. package/dist/modules/src/isobmff/isobmff-boxes.js +0 -1480
  148. package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -2618
  149. package/dist/modules/src/isobmff/isobmff-misc.js +0 -20
  150. package/dist/modules/src/isobmff/isobmff-muxer.js +0 -966
  151. package/dist/modules/src/isobmff/isobmff-reader.js +0 -72
  152. package/dist/modules/src/matroska/ebml.js +0 -653
  153. package/dist/modules/src/matroska/matroska-demuxer.js +0 -2133
  154. package/dist/modules/src/matroska/matroska-misc.js +0 -20
  155. package/dist/modules/src/matroska/matroska-muxer.js +0 -1017
  156. package/dist/modules/src/media-sink.js +0 -1736
  157. package/dist/modules/src/media-source.js +0 -1825
  158. package/dist/modules/src/metadata.js +0 -193
  159. package/dist/modules/src/misc.js +0 -623
  160. package/dist/modules/src/mp3/mp3-demuxer.js +0 -285
  161. package/dist/modules/src/mp3/mp3-muxer.js +0 -123
  162. package/dist/modules/src/mp3/mp3-reader.js +0 -26
  163. package/dist/modules/src/mp3/mp3-writer.js +0 -78
  164. package/dist/modules/src/muxer.js +0 -50
  165. package/dist/modules/src/node.d.ts +0 -9
  166. package/dist/modules/src/node.d.ts.map +0 -1
  167. package/dist/modules/src/node.js +0 -9
  168. package/dist/modules/src/ogg/ogg-demuxer.js +0 -763
  169. package/dist/modules/src/ogg/ogg-misc.js +0 -78
  170. package/dist/modules/src/ogg/ogg-muxer.js +0 -353
  171. package/dist/modules/src/ogg/ogg-reader.js +0 -65
  172. package/dist/modules/src/output-format.js +0 -527
  173. package/dist/modules/src/output.js +0 -300
  174. package/dist/modules/src/packet.js +0 -182
  175. package/dist/modules/src/pcm.js +0 -85
  176. package/dist/modules/src/reader.js +0 -236
  177. package/dist/modules/src/sample.js +0 -1056
  178. package/dist/modules/src/source.js +0 -1182
  179. package/dist/modules/src/subtitles.js +0 -575
  180. package/dist/modules/src/target.js +0 -140
  181. package/dist/modules/src/wave/riff-writer.js +0 -30
  182. package/dist/modules/src/wave/wave-demuxer.js +0 -447
  183. package/dist/modules/src/wave/wave-muxer.js +0 -318
  184. package/dist/modules/src/writer.js +0 -370
  185. package/src/adts/adts-demuxer.ts +0 -331
  186. package/src/adts/adts-muxer.ts +0 -111
  187. package/src/adts/adts-reader.ts +0 -85
  188. package/src/codec-data.ts +0 -2078
  189. package/src/codec.ts +0 -1092
  190. package/src/conversion.ts +0 -2112
  191. package/src/custom-coder.ts +0 -197
  192. package/src/demuxer.ts +0 -24
  193. package/src/encode.ts +0 -739
  194. package/src/flac/flac-demuxer.ts +0 -730
  195. package/src/flac/flac-misc.ts +0 -164
  196. package/src/flac/flac-muxer.ts +0 -320
  197. package/src/id3.ts +0 -925
  198. package/src/index.ts +0 -221
  199. package/src/input-format.ts +0 -541
  200. package/src/input-track.ts +0 -529
  201. package/src/input.ts +0 -235
  202. package/src/isobmff/isobmff-boxes.ts +0 -1719
  203. package/src/isobmff/isobmff-demuxer.ts +0 -3190
  204. package/src/isobmff/isobmff-misc.ts +0 -29
  205. package/src/isobmff/isobmff-muxer.ts +0 -1348
  206. package/src/isobmff/isobmff-reader.ts +0 -91
  207. package/src/matroska/ebml.ts +0 -730
  208. package/src/matroska/matroska-demuxer.ts +0 -2481
  209. package/src/matroska/matroska-misc.ts +0 -29
  210. package/src/matroska/matroska-muxer.ts +0 -1276
  211. package/src/media-sink.ts +0 -2179
  212. package/src/media-source.ts +0 -2243
  213. package/src/metadata.ts +0 -320
  214. package/src/misc.ts +0 -798
  215. package/src/mp3/mp3-demuxer.ts +0 -383
  216. package/src/mp3/mp3-muxer.ts +0 -166
  217. package/src/mp3/mp3-reader.ts +0 -34
  218. package/src/mp3/mp3-writer.ts +0 -120
  219. package/src/muxer.ts +0 -88
  220. package/src/node.ts +0 -11
  221. package/src/ogg/ogg-demuxer.ts +0 -1053
  222. package/src/ogg/ogg-misc.ts +0 -116
  223. package/src/ogg/ogg-muxer.ts +0 -497
  224. package/src/ogg/ogg-reader.ts +0 -93
  225. package/src/output-format.ts +0 -945
  226. package/src/output.ts +0 -488
  227. package/src/packet.ts +0 -263
  228. package/src/pcm.ts +0 -112
  229. package/src/reader.ts +0 -323
  230. package/src/sample.ts +0 -1461
  231. package/src/source.ts +0 -1688
  232. package/src/subtitles.ts +0 -711
  233. package/src/target.ts +0 -204
  234. package/src/tsconfig.json +0 -16
  235. package/src/wave/riff-writer.ts +0 -36
  236. package/src/wave/wave-demuxer.ts +0 -529
  237. package/src/wave/wave-muxer.ts +0 -371
  238. package/src/writer.ts +0 -490
@@ -1,504 +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 { FlacBlockType, readVorbisComments } from '../codec-data.js';
9
- import { Demuxer } from '../demuxer.js';
10
- import { InputAudioTrack } from '../input-track.js';
11
- import { assert, AsyncMutex, binarySearchLessOrEqual, Bitstream, textDecoder, UNDETERMINED_LANGUAGE, } from '../misc.js';
12
- import { EncodedPacket, PLACEHOLDER_DATA } from '../packet.js';
13
- import { readBytes, readU24Be, readU32Be, readU8, } from '../reader.js';
14
- import { DEFAULT_TRACK_DISPOSITION } from '../metadata.js';
15
- import { calculateCrc8, readBlockSize, getBlockSizeOrUncommon, readCodedNumber, readSampleRate, getSampleRateOrUncommon, } from './flac-misc.js';
16
- export class FlacDemuxer extends Demuxer {
17
- constructor(input) {
18
- super(input);
19
- this.loadedSamples = []; // All samples from the start of the file to lastLoadedPos
20
- this.metadataPromise = null;
21
- this.track = null;
22
- this.metadataTags = {};
23
- this.audioInfo = null;
24
- this.lastLoadedPos = null;
25
- this.blockingBit = null;
26
- this.readingMutex = new AsyncMutex();
27
- this.lastSampleLoaded = false;
28
- this.reader = input._reader;
29
- }
30
- async computeDuration() {
31
- await this.readMetadata();
32
- assert(this.track);
33
- return this.track.computeDuration();
34
- }
35
- async getMetadataTags() {
36
- await this.readMetadata();
37
- return this.metadataTags;
38
- }
39
- async getTracks() {
40
- await this.readMetadata();
41
- assert(this.track);
42
- return [this.track];
43
- }
44
- async getMimeType() {
45
- return 'audio/flac';
46
- }
47
- async readMetadata() {
48
- let currentPos = 4; // Skip 'fLaC'
49
- return (this.metadataPromise ??= (async () => {
50
- while (this.reader.fileSize === null
51
- || currentPos < this.reader.fileSize) {
52
- let sizeSlice = this.reader.requestSlice(currentPos, 4);
53
- if (sizeSlice instanceof Promise)
54
- sizeSlice = await sizeSlice;
55
- currentPos += 4;
56
- if (sizeSlice === null) {
57
- throw new Error(`Metadata block at position ${currentPos} is too small! Corrupted file.`);
58
- }
59
- assert(sizeSlice);
60
- const byte = readU8(sizeSlice); // first bit: isLastMetadata, remaining 7 bits: metaBlockType
61
- const size = readU24Be(sizeSlice);
62
- const isLastMetadata = (byte & 0x80) !== 0;
63
- const metaBlockType = byte & 0x7f;
64
- switch (metaBlockType) {
65
- case FlacBlockType.STREAMINFO: {
66
- // Parse streaminfo block
67
- // https://www.rfc-editor.org/rfc/rfc9639.html#section-8.2
68
- let streamInfoBlock = this.reader.requestSlice(currentPos, size);
69
- if (streamInfoBlock instanceof Promise)
70
- streamInfoBlock = await streamInfoBlock;
71
- assert(streamInfoBlock);
72
- if (streamInfoBlock === null) {
73
- throw new Error(`StreamInfo block at position ${currentPos} is too small! Corrupted file.`);
74
- }
75
- const streamInfoBytes = readBytes(streamInfoBlock, 34);
76
- const bitstream = new Bitstream(streamInfoBytes);
77
- const minimumBlockSize = bitstream.readBits(16);
78
- const maximumBlockSize = bitstream.readBits(16);
79
- const minimumFrameSize = bitstream.readBits(24);
80
- const maximumFrameSize = bitstream.readBits(24);
81
- const sampleRate = bitstream.readBits(20);
82
- const numberOfChannels = bitstream.readBits(3) + 1;
83
- bitstream.readBits(5); // bitsPerSample - 1
84
- const totalSamples = bitstream.readBits(36);
85
- // https://www.w3.org/TR/webcodecs-flac-codec-registration/#audiodecoderconfig-description
86
- // description is required, and has to be the following:
87
- // 1. The bytes 0x66 0x4C 0x61 0x43 ("fLaC" in ASCII)
88
- // 2. A metadata block (called the STREAMINFO block) as described in section 7 of [FLAC]
89
- // 3. Optionaly (sic) other metadata blocks, that are not used by the specification
90
- bitstream.skipBits(16 * 8); // md5 hash
91
- const description = new Uint8Array(42);
92
- // 1. "fLaC"
93
- description.set(new Uint8Array([0x66, 0x4c, 0x61, 0x43]), 0);
94
- // 2. STREAMINFO block
95
- description.set(new Uint8Array([128, 0, 0, 34]), 4);
96
- // 3. Other metadata blocks
97
- description.set(streamInfoBytes, 8);
98
- this.audioInfo = {
99
- numberOfChannels,
100
- sampleRate,
101
- totalSamples,
102
- minimumBlockSize,
103
- maximumBlockSize,
104
- minimumFrameSize,
105
- maximumFrameSize,
106
- description,
107
- };
108
- this.track = new InputAudioTrack(this.input, new FlacAudioTrackBacking(this));
109
- break;
110
- }
111
- case FlacBlockType.VORBIS_COMMENT: {
112
- // Parse vorbis comment block
113
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-vorbis-comment
114
- let vorbisCommentBlock = this.reader.requestSlice(currentPos, size);
115
- if (vorbisCommentBlock instanceof Promise)
116
- vorbisCommentBlock = await vorbisCommentBlock;
117
- assert(vorbisCommentBlock);
118
- readVorbisComments(readBytes(vorbisCommentBlock, size), this.metadataTags);
119
- break;
120
- }
121
- case FlacBlockType.PICTURE: {
122
- // Parse picture block
123
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-picture
124
- let pictureBlock = this.reader.requestSlice(currentPos, size);
125
- if (pictureBlock instanceof Promise)
126
- pictureBlock = await pictureBlock;
127
- assert(pictureBlock);
128
- const pictureType = readU32Be(pictureBlock);
129
- const mediaTypeLength = readU32Be(pictureBlock);
130
- const mediaType = textDecoder.decode(readBytes(pictureBlock, mediaTypeLength));
131
- const descriptionLength = readU32Be(pictureBlock);
132
- const description = textDecoder.decode(readBytes(pictureBlock, descriptionLength));
133
- pictureBlock.skip(4 + 4 + 4 + 4); // Skip width, height, color depth, number of indexed colors
134
- const dataLength = readU32Be(pictureBlock);
135
- const data = readBytes(pictureBlock, dataLength);
136
- this.metadataTags.images ??= [];
137
- this.metadataTags.images.push({
138
- data,
139
- mimeType: mediaType,
140
- // https://www.rfc-editor.org/rfc/rfc9639.html#table13
141
- kind: pictureType === 3
142
- ? 'coverFront'
143
- : pictureType === 4
144
- ? 'coverBack'
145
- : 'unknown',
146
- description,
147
- });
148
- break;
149
- }
150
- default:
151
- break;
152
- }
153
- currentPos += size;
154
- if (isLastMetadata) {
155
- this.lastLoadedPos = currentPos;
156
- break;
157
- }
158
- }
159
- })());
160
- }
161
- async readNextFlacFrame({ startPos, isFirstPacket, }) {
162
- assert(this.audioInfo);
163
- // we expect that there are at least `minimumFrameSize` bytes left in the file
164
- // Ideally we also want to validate the next header is valid
165
- // to throw out an accidential sync word
166
- // The shortest valid FLAC header I can think of, based off the code
167
- // of readFlacFrameHeader:
168
- // 4 bytes used for bitstream from syncword to bit depth
169
- // 1 byte coded number
170
- // (uncommon values, no bytes read)
171
- // 1 byte crc
172
- // --> 6 bytes
173
- const minimumHeaderLength = 6;
174
- // If we read everything in readFlacFrameHeader, we read 16 bytes
175
- const maximumHeaderSize = 16;
176
- const maximumSliceLength = this.audioInfo.maximumFrameSize + maximumHeaderSize;
177
- const slice = await this.reader.requestSliceRange(startPos, this.audioInfo.minimumFrameSize, maximumSliceLength);
178
- if (!slice) {
179
- return null;
180
- }
181
- const frameHeader = this.readFlacFrameHeader({
182
- slice,
183
- isFirstPacket: isFirstPacket,
184
- });
185
- if (!frameHeader) {
186
- return null;
187
- }
188
- // We don't know exactly how long the packet is, we only know the `minimumFrameSize` and `maximumFrameSize`
189
- // The packet is over if the next 2 bytes are the sync word followed by a valid header
190
- // or the end of the file is reached
191
- // The next sync word is expected at earliest when `minimumFrameSize` is reached,
192
- // we can skip over anything before that
193
- slice.filePos = startPos + this.audioInfo.minimumFrameSize;
194
- while (true) {
195
- // Reached end of the file, packet is over
196
- if (slice.filePos > slice.end - minimumHeaderLength) {
197
- return {
198
- num: frameHeader.num,
199
- blockSize: frameHeader.blockSize,
200
- sampleRate: frameHeader.sampleRate,
201
- size: slice.end - startPos,
202
- isLastFrame: true,
203
- };
204
- }
205
- const nextByte = readU8(slice);
206
- if (nextByte === 0xff) {
207
- const positionBeforeReading = slice.filePos;
208
- const byteAfterNextByte = readU8(slice);
209
- const expected = this.blockingBit === 1 ? 0b1111_1001 : 0b1111_1000;
210
- if (byteAfterNextByte !== expected) {
211
- slice.filePos = positionBeforeReading;
212
- continue;
213
- }
214
- slice.skip(-2);
215
- const lengthIfNextFlacFrameHeaderIsLegit = slice.filePos - startPos;
216
- const nextFrameHeader = this.readFlacFrameHeader({
217
- slice,
218
- isFirstPacket: false,
219
- });
220
- if (!nextFrameHeader) {
221
- slice.filePos = positionBeforeReading;
222
- continue;
223
- }
224
- // Ensure the frameOrSampleNum is consecutive.
225
- // https://github.com/Vanilagy/mediabunny/issues/194
226
- if (this.blockingBit === 0) {
227
- // Case A: If the stream is fixed block size, this is the frame number, which increments by 1
228
- if (nextFrameHeader.num - frameHeader.num !== 1) {
229
- slice.filePos = positionBeforeReading;
230
- continue;
231
- }
232
- }
233
- else {
234
- // Case B: If the stream is variable block size, this is the sample number, which increments by
235
- // amount of samples in a frame.
236
- if (nextFrameHeader.num - frameHeader.num !== frameHeader.blockSize) {
237
- slice.filePos = positionBeforeReading;
238
- continue;
239
- }
240
- }
241
- return {
242
- num: frameHeader.num,
243
- blockSize: frameHeader.blockSize,
244
- sampleRate: frameHeader.sampleRate,
245
- size: lengthIfNextFlacFrameHeaderIsLegit,
246
- isLastFrame: false,
247
- };
248
- }
249
- }
250
- }
251
- readFlacFrameHeader({ slice, isFirstPacket, }) {
252
- // In this function, generally it is not safe to throw errors.
253
- // We might end up here because we stumbled upon a syncword,
254
- // but the data might not actually be a FLAC frame, it might be random bitstream
255
- // data, in that case we should return null and continue.
256
- const startOffset = slice.filePos;
257
- // https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1
258
- // Each frame MUST start on a byte boundary and start with the 15-bit frame
259
- // sync code 0b111111111111100. Following the sync code is the blocking strategy
260
- // bit, which MUST NOT change during the audio stream.
261
- const bytes = readBytes(slice, 4);
262
- const bitstream = new Bitstream(bytes);
263
- const bits = bitstream.readBits(15);
264
- if (bits !== 0b111111111111100) {
265
- // This cannot be a valid FLAC frame, must start with the syncword
266
- return null;
267
- }
268
- if (this.blockingBit === null) {
269
- assert(isFirstPacket);
270
- const newBlockingBit = bitstream.readBits(1);
271
- this.blockingBit = newBlockingBit;
272
- }
273
- else if (this.blockingBit === 1) {
274
- assert(!isFirstPacket);
275
- const newBlockingBit = bitstream.readBits(1);
276
- if (newBlockingBit !== 1) {
277
- // This cannot be a valid FLAC frame, expected 1 but got 0
278
- return null;
279
- }
280
- }
281
- else if (this.blockingBit === 0) {
282
- assert(!isFirstPacket);
283
- const newBlockingBit = bitstream.readBits(1);
284
- if (newBlockingBit !== 0) {
285
- // This cannot be a valid FLAC frame, expected 0 but got 1
286
- return null;
287
- }
288
- }
289
- else {
290
- throw new Error('Invalid blocking bit');
291
- }
292
- const blockSizeOrUncommon = getBlockSizeOrUncommon(bitstream.readBits(4));
293
- if (!blockSizeOrUncommon) {
294
- // This cannot be a valid FLAC frame, the syncword was just coincidental
295
- return null;
296
- }
297
- assert(this.audioInfo);
298
- const sampleRateOrUncommon = getSampleRateOrUncommon(bitstream.readBits(4), this.audioInfo.sampleRate);
299
- if (!sampleRateOrUncommon) {
300
- // This cannot be a valid FLAC frame, the syncword was just coincidental
301
- return null;
302
- }
303
- bitstream.readBits(4); // channel count
304
- bitstream.readBits(3); // bit depth
305
- const reservedZero = bitstream.readBits(1); // reserved zero
306
- if (reservedZero !== 0) {
307
- // This cannot be a valid FLAC frame, the syncword was just coincidental
308
- return null;
309
- }
310
- const num = readCodedNumber(slice);
311
- const blockSize = readBlockSize(slice, blockSizeOrUncommon);
312
- const sampleRate = readSampleRate(slice, sampleRateOrUncommon);
313
- if (sampleRate === null) {
314
- // This cannot be a valid FLAC frame, the syncword was just coincidental
315
- return null;
316
- }
317
- if (sampleRate !== this.audioInfo.sampleRate) {
318
- // This cannot be a valid FLAC frame, the sample rate is not the same as in the stream info
319
- return null;
320
- }
321
- const size = slice.filePos - startOffset;
322
- const crc = readU8(slice);
323
- slice.skip(-size);
324
- slice.skip(-1);
325
- const crcCalculated = calculateCrc8(readBytes(slice, size));
326
- if (crc !== crcCalculated) {
327
- // Maybe this wasn't a FLAC frame at all, the syncword was just coincidentally
328
- // in the bitstream
329
- return null;
330
- }
331
- return { num, blockSize, sampleRate };
332
- }
333
- async advanceReader() {
334
- await this.readMetadata();
335
- assert(this.lastLoadedPos !== null);
336
- assert(this.audioInfo);
337
- const startPos = this.lastLoadedPos;
338
- const frame = await this.readNextFlacFrame({
339
- startPos,
340
- isFirstPacket: this.loadedSamples.length === 0,
341
- });
342
- if (!frame) {
343
- // Unexpected case, failed to read next FLAC frame
344
- // handling gracefully
345
- this.lastSampleLoaded = true;
346
- return;
347
- }
348
- const lastSample = this.loadedSamples[this.loadedSamples.length - 1];
349
- const blockOffset = lastSample
350
- ? lastSample.blockOffset + lastSample.blockSize
351
- : 0;
352
- const sample = {
353
- blockOffset,
354
- blockSize: frame.blockSize,
355
- byteOffset: startPos,
356
- byteSize: frame.size,
357
- };
358
- this.lastLoadedPos = this.lastLoadedPos + frame.size;
359
- this.loadedSamples.push(sample);
360
- if (frame.isLastFrame) {
361
- this.lastSampleLoaded = true;
362
- return;
363
- }
364
- }
365
- }
366
- class FlacAudioTrackBacking {
367
- constructor(demuxer) {
368
- this.demuxer = demuxer;
369
- }
370
- getId() {
371
- return 1;
372
- }
373
- getCodec() {
374
- return 'flac';
375
- }
376
- getInternalCodecId() {
377
- return null;
378
- }
379
- getNumberOfChannels() {
380
- assert(this.demuxer.audioInfo);
381
- return this.demuxer.audioInfo.numberOfChannels;
382
- }
383
- async computeDuration() {
384
- const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
385
- return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
386
- }
387
- getSampleRate() {
388
- assert(this.demuxer.audioInfo);
389
- return this.demuxer.audioInfo.sampleRate;
390
- }
391
- getName() {
392
- return null;
393
- }
394
- getLanguageCode() {
395
- return UNDETERMINED_LANGUAGE;
396
- }
397
- getTimeResolution() {
398
- assert(this.demuxer.audioInfo);
399
- return this.demuxer.audioInfo.sampleRate;
400
- }
401
- getDisposition() {
402
- return {
403
- ...DEFAULT_TRACK_DISPOSITION,
404
- };
405
- }
406
- async getFirstTimestamp() {
407
- return 0;
408
- }
409
- async getDecoderConfig() {
410
- assert(this.demuxer.audioInfo);
411
- return {
412
- codec: 'flac',
413
- numberOfChannels: this.demuxer.audioInfo.numberOfChannels,
414
- sampleRate: this.demuxer.audioInfo.sampleRate,
415
- description: this.demuxer.audioInfo.description,
416
- };
417
- }
418
- async getPacket(timestamp, options) {
419
- assert(this.demuxer.audioInfo);
420
- if (timestamp < 0) {
421
- throw new Error('Timestamp cannot be negative');
422
- }
423
- const release = await this.demuxer.readingMutex.acquire();
424
- try {
425
- while (true) {
426
- const packetIndex = binarySearchLessOrEqual(this.demuxer.loadedSamples, timestamp, x => x.blockOffset / this.demuxer.audioInfo.sampleRate);
427
- if (packetIndex === -1) {
428
- await this.demuxer.advanceReader();
429
- continue;
430
- }
431
- const packet = this.demuxer.loadedSamples[packetIndex];
432
- const sampleTimestamp = packet.blockOffset / this.demuxer.audioInfo.sampleRate;
433
- const sampleDuration = packet.blockSize / this.demuxer.audioInfo.sampleRate;
434
- if (sampleTimestamp + sampleDuration <= timestamp) {
435
- if (this.demuxer.lastSampleLoaded) {
436
- return this.getPacketAtIndex(this.demuxer.loadedSamples.length - 1, options);
437
- }
438
- await this.demuxer.advanceReader();
439
- continue;
440
- }
441
- return this.getPacketAtIndex(packetIndex, options);
442
- }
443
- }
444
- finally {
445
- release();
446
- }
447
- }
448
- async getNextPacket(packet, options) {
449
- const release = await this.demuxer.readingMutex.acquire();
450
- try {
451
- const nextIndex = packet.sequenceNumber + 1;
452
- if (this.demuxer.lastSampleLoaded
453
- && nextIndex >= this.demuxer.loadedSamples.length) {
454
- return null;
455
- }
456
- // Ensure the next sample exists
457
- while (nextIndex >= this.demuxer.loadedSamples.length
458
- && !this.demuxer.lastSampleLoaded) {
459
- await this.demuxer.advanceReader();
460
- }
461
- return this.getPacketAtIndex(nextIndex, options);
462
- }
463
- finally {
464
- release();
465
- }
466
- }
467
- getKeyPacket(timestamp, options) {
468
- return this.getPacket(timestamp, options);
469
- }
470
- getNextKeyPacket(packet, options) {
471
- return this.getNextPacket(packet, options);
472
- }
473
- async getPacketAtIndex(sampleIndex, options) {
474
- const rawSample = this.demuxer.loadedSamples[sampleIndex];
475
- if (!rawSample) {
476
- return null;
477
- }
478
- let data;
479
- if (options.metadataOnly) {
480
- data = PLACEHOLDER_DATA;
481
- }
482
- else {
483
- let slice = this.demuxer.reader.requestSlice(rawSample.byteOffset, rawSample.byteSize);
484
- if (slice instanceof Promise)
485
- slice = await slice;
486
- if (!slice) {
487
- return null; // Data didn't fit into the rest of the file
488
- }
489
- data = readBytes(slice, rawSample.byteSize);
490
- }
491
- assert(this.demuxer.audioInfo);
492
- const timestamp = rawSample.blockOffset / this.demuxer.audioInfo.sampleRate;
493
- const duration = rawSample.blockSize / this.demuxer.audioInfo.sampleRate;
494
- return new EncodedPacket(data, 'key', timestamp, duration, sampleIndex, rawSample.byteSize);
495
- }
496
- async getFirstPacket(options) {
497
- // Ensure the next sample exists
498
- while (this.demuxer.loadedSamples.length === 0
499
- && !this.demuxer.lastSampleLoaded) {
500
- await this.demuxer.advanceReader();
501
- }
502
- return this.getPacketAtIndex(0, options);
503
- }
504
- }
@@ -1,135 +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 { assert, assertNever, Bitstream } from '../misc.js';
9
- import { readBytes, readU16Be, readU8 } from '../reader.js';
10
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-block-size-bits
11
- export const getBlockSizeOrUncommon = (bits) => {
12
- if (bits === 0b0000) {
13
- return null;
14
- }
15
- else if (bits === 0b0001) {
16
- return 192;
17
- }
18
- else if (bits >= 0b0010 && bits <= 0b0101) {
19
- return 144 * 2 ** bits;
20
- }
21
- else if (bits === 0b0110) {
22
- return 'uncommon-u8';
23
- }
24
- else if (bits === 0b0111) {
25
- return 'uncommon-u16';
26
- }
27
- else if (bits >= 0b1000 && bits <= 0b1111) {
28
- return 2 ** bits;
29
- }
30
- else {
31
- return null;
32
- }
33
- };
34
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-sample-rate-bits
35
- export const getSampleRateOrUncommon = (sampleRateBits, streamInfoSampleRate) => {
36
- switch (sampleRateBits) {
37
- case 0b0000: return streamInfoSampleRate;
38
- case 0b0001: return 88200;
39
- case 0b0010: return 176400;
40
- case 0b0011: return 192000;
41
- case 0b0100: return 8000;
42
- case 0b0101: return 16000;
43
- case 0b0110: return 22050;
44
- case 0b0111: return 24000;
45
- case 0b1000: return 32000;
46
- case 0b1001: return 44100;
47
- case 0b1010: return 48000;
48
- case 0b1011: return 96000;
49
- case 0b1100: return 'uncommon-u8';
50
- case 0b1101: return 'uncommon-u16';
51
- case 0b1110: return 'uncommon-u16-10';
52
- default: return null;
53
- }
54
- };
55
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-coded-number
56
- export const readCodedNumber = (fileSlice) => {
57
- let ones = 0;
58
- const bitstream1 = new Bitstream(readBytes(fileSlice, 1));
59
- while (bitstream1.readBits(1) === 1) {
60
- ones++;
61
- }
62
- if (ones === 0) {
63
- return bitstream1.readBits(7);
64
- }
65
- const bitArray = [];
66
- const extraBytes = ones - 1;
67
- const bitstream2 = new Bitstream(readBytes(fileSlice, extraBytes));
68
- const firstByteBits = 8 - ones - 1;
69
- for (let i = 0; i < firstByteBits; i++) {
70
- bitArray.unshift(bitstream1.readBits(1));
71
- }
72
- for (let i = 0; i < extraBytes; i++) {
73
- for (let j = 0; j < 8; j++) {
74
- const val = bitstream2.readBits(1);
75
- if (j < 2) {
76
- continue;
77
- }
78
- bitArray.unshift(val);
79
- }
80
- }
81
- const encoded = bitArray.reduce((acc, bit, index) => {
82
- return acc | (bit << index);
83
- }, 0);
84
- return encoded;
85
- };
86
- export const readBlockSize = (slice, blockSizeBits) => {
87
- if (blockSizeBits === 'uncommon-u16') {
88
- return readU16Be(slice) + 1;
89
- }
90
- else if (blockSizeBits === 'uncommon-u8') {
91
- return readU8(slice) + 1;
92
- }
93
- else if (typeof blockSizeBits === 'number') {
94
- return blockSizeBits;
95
- }
96
- else {
97
- assertNever(blockSizeBits);
98
- assert(false);
99
- }
100
- };
101
- export const readSampleRate = (slice, sampleRateOrUncommon) => {
102
- if (sampleRateOrUncommon === 'uncommon-u16') {
103
- return readU16Be(slice);
104
- }
105
- if (sampleRateOrUncommon === 'uncommon-u16-10') {
106
- return readU16Be(slice) * 10;
107
- }
108
- if (sampleRateOrUncommon === 'uncommon-u8') {
109
- return readU8(slice);
110
- }
111
- if (typeof sampleRateOrUncommon === 'number') {
112
- return sampleRateOrUncommon;
113
- }
114
- return null;
115
- };
116
- // https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1.1
117
- export const calculateCrc8 = (data) => {
118
- const polynomial = 0x07; // x^8 + x^2 + x^1 + x^0
119
- let crc = 0x00; // Initialize CRC to 0
120
- for (const byte of data) {
121
- crc ^= byte; // XOR byte into least significant byte of crc
122
- for (let i = 0; i < 8; i++) {
123
- // For each bit in the byte
124
- if ((crc & 0x80) !== 0) {
125
- // If the leftmost bit (MSB) is set
126
- crc = (crc << 1) ^ polynomial; // Shift left and XOR with polynomial
127
- }
128
- else {
129
- crc <<= 1; // Just shift left
130
- }
131
- crc &= 0xff; // Ensure CRC remains 8-bit
132
- }
133
- }
134
- return crc;
135
- };