@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,730 +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 { FlacBlockType, readVorbisComments } from '../codec-data';
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 {
15
- assert,
16
- AsyncMutex,
17
- binarySearchLessOrEqual,
18
- Bitstream,
19
- textDecoder,
20
- UNDETERMINED_LANGUAGE,
21
- } from '../misc';
22
- import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
23
- import {
24
- FileSlice,
25
- readBytes,
26
- Reader,
27
- readU24Be,
28
- readU32Be,
29
- readU8,
30
- } from '../reader';
31
- import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
32
- import {
33
- calculateCrc8,
34
- readBlockSize,
35
- getBlockSizeOrUncommon,
36
- readCodedNumber,
37
- readSampleRate,
38
- getSampleRateOrUncommon,
39
- } from './flac-misc';
40
-
41
- type FlacAudioInfo = {
42
- numberOfChannels: number;
43
- sampleRate: number;
44
- totalSamples: number;
45
- minimumBlockSize: number;
46
- maximumBlockSize: number;
47
- minimumFrameSize: number;
48
- maximumFrameSize: number;
49
- description: Uint8Array;
50
- };
51
-
52
- type Sample = {
53
- blockOffset: number;
54
- blockSize: number;
55
- byteOffset: number;
56
- byteSize: number;
57
- };
58
-
59
- type NextFlacFrameResult = {
60
- num: number;
61
- blockSize: number;
62
- sampleRate: number;
63
- size: number;
64
- isLastFrame: boolean;
65
- };
66
-
67
- export class FlacDemuxer extends Demuxer {
68
- reader: Reader;
69
-
70
- loadedSamples: Sample[] = []; // All samples from the start of the file to lastLoadedPos
71
-
72
- metadataPromise: Promise<void> | null = null;
73
- track: InputAudioTrack | null = null;
74
- metadataTags: MetadataTags = {};
75
-
76
- audioInfo: FlacAudioInfo | null = null;
77
- lastLoadedPos: number | null = null;
78
- blockingBit: number | null = null;
79
-
80
- readingMutex = new AsyncMutex();
81
- lastSampleLoaded = false;
82
-
83
- constructor(input: Input) {
84
- super(input);
85
-
86
- this.reader = input._reader;
87
- }
88
-
89
- override async computeDuration(): Promise<number> {
90
- await this.readMetadata();
91
- assert(this.track);
92
- return this.track.computeDuration();
93
- }
94
-
95
- override async getMetadataTags(): Promise<MetadataTags> {
96
- await this.readMetadata();
97
- return this.metadataTags;
98
- }
99
-
100
- async getTracks() {
101
- await this.readMetadata();
102
- assert(this.track);
103
- return [this.track];
104
- }
105
-
106
- async getMimeType() {
107
- return 'audio/flac';
108
- }
109
-
110
- async readMetadata() {
111
- let currentPos = 4; // Skip 'fLaC'
112
-
113
- return (this.metadataPromise ??= (async () => {
114
- while (
115
- this.reader.fileSize === null
116
- || currentPos < this.reader.fileSize
117
- ) {
118
- let sizeSlice = this.reader.requestSlice(currentPos, 4);
119
- if (sizeSlice instanceof Promise) sizeSlice = await sizeSlice;
120
- currentPos += 4;
121
-
122
- if (sizeSlice === null) {
123
- throw new Error(
124
- `Metadata block at position ${currentPos} is too small! Corrupted file.`,
125
- );
126
- }
127
-
128
- assert(sizeSlice);
129
-
130
- const byte = readU8(sizeSlice); // first bit: isLastMetadata, remaining 7 bits: metaBlockType
131
- const size = readU24Be(sizeSlice);
132
- const isLastMetadata = (byte & 0x80) !== 0;
133
- const metaBlockType = byte & 0x7f;
134
-
135
- switch (metaBlockType) {
136
- case FlacBlockType.STREAMINFO: {
137
- // Parse streaminfo block
138
- // https://www.rfc-editor.org/rfc/rfc9639.html#section-8.2
139
- let streamInfoBlock = this.reader.requestSlice(
140
- currentPos,
141
- size,
142
- );
143
- if (streamInfoBlock instanceof Promise) streamInfoBlock = await streamInfoBlock;
144
-
145
- assert(streamInfoBlock);
146
- if (streamInfoBlock === null) {
147
- throw new Error(
148
- `StreamInfo block at position ${currentPos} is too small! Corrupted file.`,
149
- );
150
- }
151
-
152
- const streamInfoBytes = readBytes(streamInfoBlock, 34);
153
- const bitstream = new Bitstream(streamInfoBytes);
154
-
155
- const minimumBlockSize = bitstream.readBits(16);
156
- const maximumBlockSize = bitstream.readBits(16);
157
- const minimumFrameSize = bitstream.readBits(24);
158
- const maximumFrameSize = bitstream.readBits(24);
159
-
160
- const sampleRate = bitstream.readBits(20);
161
- const numberOfChannels = bitstream.readBits(3) + 1;
162
- bitstream.readBits(5); // bitsPerSample - 1
163
- const totalSamples = bitstream.readBits(36);
164
-
165
- // https://www.w3.org/TR/webcodecs-flac-codec-registration/#audiodecoderconfig-description
166
- // description is required, and has to be the following:
167
- // 1. The bytes 0x66 0x4C 0x61 0x43 ("fLaC" in ASCII)
168
- // 2. A metadata block (called the STREAMINFO block) as described in section 7 of [FLAC]
169
- // 3. Optionaly (sic) other metadata blocks, that are not used by the specification
170
-
171
- bitstream.skipBits(16 * 8); // md5 hash
172
-
173
- const description = new Uint8Array(42);
174
- // 1. "fLaC"
175
- description.set(new Uint8Array([0x66, 0x4c, 0x61, 0x43]), 0);
176
- // 2. STREAMINFO block
177
- description.set(new Uint8Array([128, 0, 0, 34]), 4);
178
- // 3. Other metadata blocks
179
- description.set(streamInfoBytes, 8);
180
-
181
- this.audioInfo = {
182
- numberOfChannels,
183
- sampleRate,
184
- totalSamples,
185
- minimumBlockSize,
186
- maximumBlockSize,
187
- minimumFrameSize,
188
- maximumFrameSize,
189
- description,
190
- };
191
-
192
- this.track = new InputAudioTrack(this.input, new FlacAudioTrackBacking(this));
193
- break;
194
- }
195
- case FlacBlockType.VORBIS_COMMENT: {
196
- // Parse vorbis comment block
197
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-vorbis-comment
198
- let vorbisCommentBlock = this.reader.requestSlice(
199
- currentPos,
200
- size,
201
- );
202
- if (vorbisCommentBlock instanceof Promise) vorbisCommentBlock = await vorbisCommentBlock;
203
-
204
- assert(vorbisCommentBlock);
205
-
206
- readVorbisComments(
207
- readBytes(vorbisCommentBlock, size),
208
- this.metadataTags,
209
- );
210
-
211
- break;
212
- }
213
- case FlacBlockType.PICTURE: {
214
- // Parse picture block
215
- // https://www.rfc-editor.org/rfc/rfc9639.html#name-picture
216
- let pictureBlock = this.reader.requestSlice(
217
- currentPos,
218
- size,
219
- );
220
- if (pictureBlock instanceof Promise) pictureBlock = await pictureBlock;
221
-
222
- assert(pictureBlock);
223
- const pictureType = readU32Be(pictureBlock);
224
- const mediaTypeLength = readU32Be(pictureBlock);
225
- const mediaType = textDecoder.decode(
226
- readBytes(pictureBlock, mediaTypeLength),
227
- );
228
- const descriptionLength = readU32Be(pictureBlock);
229
- const description = textDecoder.decode(
230
- readBytes(pictureBlock, descriptionLength),
231
- );
232
- pictureBlock.skip(4 + 4 + 4 + 4); // Skip width, height, color depth, number of indexed colors
233
- const dataLength = readU32Be(pictureBlock);
234
- const data = readBytes(pictureBlock, dataLength);
235
-
236
- this.metadataTags.images ??= [];
237
- this.metadataTags.images.push({
238
- data,
239
- mimeType: mediaType,
240
- // https://www.rfc-editor.org/rfc/rfc9639.html#table13
241
- kind:
242
- pictureType === 3
243
- ? 'coverFront'
244
- : pictureType === 4
245
- ? 'coverBack'
246
- : 'unknown',
247
- description,
248
- });
249
- break;
250
- }
251
- default:
252
- break;
253
- }
254
- currentPos += size;
255
-
256
- if (isLastMetadata) {
257
- this.lastLoadedPos = currentPos;
258
- break;
259
- }
260
- }
261
- })());
262
- }
263
-
264
- async readNextFlacFrame({
265
- startPos,
266
- isFirstPacket,
267
- }: {
268
- startPos: number;
269
- isFirstPacket: boolean;
270
- }): Promise<NextFlacFrameResult | null> {
271
- assert(this.audioInfo);
272
- // we expect that there are at least `minimumFrameSize` bytes left in the file
273
-
274
- // Ideally we also want to validate the next header is valid
275
- // to throw out an accidential sync word
276
-
277
- // The shortest valid FLAC header I can think of, based off the code
278
- // of readFlacFrameHeader:
279
- // 4 bytes used for bitstream from syncword to bit depth
280
- // 1 byte coded number
281
- // (uncommon values, no bytes read)
282
- // 1 byte crc
283
- // --> 6 bytes
284
- const minimumHeaderLength = 6;
285
- // If we read everything in readFlacFrameHeader, we read 16 bytes
286
- const maximumHeaderSize = 16;
287
- const maximumSliceLength
288
- = this.audioInfo.maximumFrameSize + maximumHeaderSize;
289
-
290
- const slice = await this.reader.requestSliceRange(
291
- startPos,
292
- this.audioInfo.minimumFrameSize,
293
- maximumSliceLength,
294
- );
295
-
296
- if (!slice) {
297
- return null;
298
- }
299
-
300
- const frameHeader = this.readFlacFrameHeader({
301
- slice,
302
- isFirstPacket: isFirstPacket,
303
- });
304
-
305
- if (!frameHeader) {
306
- return null;
307
- }
308
-
309
- // We don't know exactly how long the packet is, we only know the `minimumFrameSize` and `maximumFrameSize`
310
- // The packet is over if the next 2 bytes are the sync word followed by a valid header
311
- // or the end of the file is reached
312
-
313
- // The next sync word is expected at earliest when `minimumFrameSize` is reached,
314
- // we can skip over anything before that
315
- slice.filePos = startPos + this.audioInfo.minimumFrameSize;
316
-
317
- while (true) {
318
- // Reached end of the file, packet is over
319
- if (slice.filePos > slice.end - minimumHeaderLength) {
320
- return {
321
- num: frameHeader.num,
322
- blockSize: frameHeader.blockSize,
323
- sampleRate: frameHeader.sampleRate,
324
- size: slice.end - startPos,
325
- isLastFrame: true,
326
- };
327
- }
328
-
329
- const nextByte = readU8(slice);
330
- if (nextByte === 0xff) {
331
- const positionBeforeReading = slice.filePos;
332
-
333
- const byteAfterNextByte = readU8(slice);
334
-
335
- const expected = this.blockingBit === 1 ? 0b1111_1001 : 0b1111_1000;
336
- if (byteAfterNextByte !== expected) {
337
- slice.filePos = positionBeforeReading;
338
- continue;
339
- }
340
-
341
- slice.skip(-2);
342
- const lengthIfNextFlacFrameHeaderIsLegit = slice.filePos - startPos;
343
-
344
- const nextFrameHeader = this.readFlacFrameHeader({
345
- slice,
346
- isFirstPacket: false,
347
- });
348
-
349
- if (!nextFrameHeader) {
350
- slice.filePos = positionBeforeReading;
351
- continue;
352
- }
353
-
354
- // Ensure the frameOrSampleNum is consecutive.
355
- // https://github.com/Vanilagy/mediabunny/issues/194
356
-
357
- if (this.blockingBit === 0) {
358
- // Case A: If the stream is fixed block size, this is the frame number, which increments by 1
359
- if (nextFrameHeader.num - frameHeader.num !== 1) {
360
- slice.filePos = positionBeforeReading;
361
- continue;
362
- }
363
- } else {
364
- // Case B: If the stream is variable block size, this is the sample number, which increments by
365
- // amount of samples in a frame.
366
- if (nextFrameHeader.num - frameHeader.num !== frameHeader.blockSize) {
367
- slice.filePos = positionBeforeReading;
368
- continue;
369
- }
370
- }
371
-
372
- return {
373
- num: frameHeader.num,
374
- blockSize: frameHeader.blockSize,
375
- sampleRate: frameHeader.sampleRate,
376
- size: lengthIfNextFlacFrameHeaderIsLegit,
377
- isLastFrame: false,
378
- };
379
- }
380
- }
381
- }
382
-
383
- readFlacFrameHeader({
384
- slice,
385
- isFirstPacket,
386
- }: {
387
- slice: FileSlice;
388
- isFirstPacket: boolean;
389
- }) {
390
- // In this function, generally it is not safe to throw errors.
391
- // We might end up here because we stumbled upon a syncword,
392
- // but the data might not actually be a FLAC frame, it might be random bitstream
393
- // data, in that case we should return null and continue.
394
-
395
- const startOffset = slice.filePos;
396
-
397
- // https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1
398
- // Each frame MUST start on a byte boundary and start with the 15-bit frame
399
- // sync code 0b111111111111100. Following the sync code is the blocking strategy
400
- // bit, which MUST NOT change during the audio stream.
401
- const bytes = readBytes(slice, 4);
402
- const bitstream = new Bitstream(bytes);
403
-
404
- const bits = bitstream.readBits(15);
405
- if (bits !== 0b111111111111100) {
406
- // This cannot be a valid FLAC frame, must start with the syncword
407
- return null;
408
- }
409
-
410
- if (this.blockingBit === null) {
411
- assert(isFirstPacket);
412
- const newBlockingBit = bitstream.readBits(1);
413
- this.blockingBit = newBlockingBit;
414
- } else if (this.blockingBit === 1) {
415
- assert(!isFirstPacket);
416
- const newBlockingBit = bitstream.readBits(1);
417
- if (newBlockingBit !== 1) {
418
- // This cannot be a valid FLAC frame, expected 1 but got 0
419
- return null;
420
- }
421
- } else if (this.blockingBit === 0) {
422
- assert(!isFirstPacket);
423
- const newBlockingBit = bitstream.readBits(1);
424
- if (newBlockingBit !== 0) {
425
- // This cannot be a valid FLAC frame, expected 0 but got 1
426
- return null;
427
- }
428
- } else {
429
- throw new Error('Invalid blocking bit');
430
- }
431
-
432
- const blockSizeOrUncommon = getBlockSizeOrUncommon(bitstream.readBits(4));
433
- if (!blockSizeOrUncommon) {
434
- // This cannot be a valid FLAC frame, the syncword was just coincidental
435
- return null;
436
- }
437
- assert(this.audioInfo);
438
- const sampleRateOrUncommon = getSampleRateOrUncommon(
439
- bitstream.readBits(4),
440
- this.audioInfo.sampleRate,
441
- );
442
- if (!sampleRateOrUncommon) {
443
- // This cannot be a valid FLAC frame, the syncword was just coincidental
444
- return null;
445
- }
446
-
447
- bitstream.readBits(4); // channel count
448
- bitstream.readBits(3); // bit depth
449
- const reservedZero = bitstream.readBits(1); // reserved zero
450
-
451
- if (reservedZero !== 0) {
452
- // This cannot be a valid FLAC frame, the syncword was just coincidental
453
- return null;
454
- }
455
-
456
- const num = readCodedNumber(slice);
457
- const blockSize = readBlockSize(slice, blockSizeOrUncommon);
458
-
459
- const sampleRate = readSampleRate(slice, sampleRateOrUncommon);
460
- if (sampleRate === null) {
461
- // This cannot be a valid FLAC frame, the syncword was just coincidental
462
- return null;
463
- }
464
-
465
- if (sampleRate !== this.audioInfo.sampleRate) {
466
- // This cannot be a valid FLAC frame, the sample rate is not the same as in the stream info
467
- return null;
468
- }
469
-
470
- const size = slice.filePos - startOffset;
471
- const crc = readU8(slice);
472
-
473
- slice.skip(-size);
474
- slice.skip(-1);
475
- const crcCalculated = calculateCrc8(readBytes(slice, size));
476
-
477
- if (crc !== crcCalculated) {
478
- // Maybe this wasn't a FLAC frame at all, the syncword was just coincidentally
479
- // in the bitstream
480
- return null;
481
- }
482
-
483
- return { num, blockSize, sampleRate };
484
- }
485
-
486
- async advanceReader() {
487
- await this.readMetadata();
488
- assert(this.lastLoadedPos !== null);
489
- assert(this.audioInfo);
490
- const startPos = this.lastLoadedPos;
491
- const frame = await this.readNextFlacFrame({
492
- startPos,
493
- isFirstPacket: this.loadedSamples.length === 0,
494
- });
495
-
496
- if (!frame) {
497
- // Unexpected case, failed to read next FLAC frame
498
- // handling gracefully
499
- this.lastSampleLoaded = true;
500
- return;
501
- }
502
-
503
- const lastSample = this.loadedSamples[this.loadedSamples.length - 1];
504
- const blockOffset = lastSample
505
- ? lastSample.blockOffset + lastSample.blockSize
506
- : 0;
507
-
508
- const sample: Sample = {
509
- blockOffset,
510
- blockSize: frame.blockSize,
511
- byteOffset: startPos,
512
- byteSize: frame.size,
513
- };
514
-
515
- this.lastLoadedPos = this.lastLoadedPos + frame.size;
516
- this.loadedSamples.push(sample);
517
-
518
- if (frame.isLastFrame) {
519
- this.lastSampleLoaded = true;
520
- return;
521
- }
522
- }
523
- }
524
-
525
- class FlacAudioTrackBacking implements InputAudioTrackBacking {
526
- constructor(public demuxer: FlacDemuxer) {}
527
-
528
- getId() {
529
- return 1;
530
- }
531
-
532
- getCodec() {
533
- return 'flac' as const;
534
- }
535
-
536
- getInternalCodecId(): string | number | Uint8Array | null {
537
- return null;
538
- }
539
-
540
- getNumberOfChannels() {
541
- assert(this.demuxer.audioInfo);
542
- return this.demuxer.audioInfo.numberOfChannels;
543
- }
544
-
545
- async computeDuration() {
546
- const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
547
- return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
548
- }
549
-
550
- getSampleRate() {
551
- assert(this.demuxer.audioInfo);
552
- return this.demuxer.audioInfo.sampleRate;
553
- }
554
-
555
- getName(): string | null {
556
- return null;
557
- }
558
-
559
- getLanguageCode() {
560
- return UNDETERMINED_LANGUAGE;
561
- }
562
-
563
- getTimeResolution() {
564
- assert(this.demuxer.audioInfo);
565
- return this.demuxer.audioInfo.sampleRate;
566
- }
567
-
568
- getDisposition() {
569
- return {
570
- ...DEFAULT_TRACK_DISPOSITION,
571
- };
572
- }
573
-
574
- async getFirstTimestamp() {
575
- return 0;
576
- }
577
-
578
- async getDecoderConfig(): Promise<AudioDecoderConfig | null> {
579
- assert(this.demuxer.audioInfo);
580
-
581
- return {
582
- codec: 'flac' as const,
583
- numberOfChannels: this.demuxer.audioInfo.numberOfChannels,
584
- sampleRate: this.demuxer.audioInfo.sampleRate,
585
- description: this.demuxer.audioInfo.description,
586
- };
587
- }
588
-
589
- async getPacket(
590
- timestamp: number,
591
- options: PacketRetrievalOptions,
592
- ): Promise<EncodedPacket | null> {
593
- assert(this.demuxer.audioInfo);
594
- if (timestamp < 0) {
595
- throw new Error('Timestamp cannot be negative');
596
- }
597
-
598
- const release = await this.demuxer.readingMutex.acquire();
599
-
600
- try {
601
- while (true) {
602
- const packetIndex = binarySearchLessOrEqual(
603
- this.demuxer.loadedSamples,
604
- timestamp,
605
- x => x.blockOffset / this.demuxer.audioInfo!.sampleRate,
606
- );
607
- if (packetIndex === -1) {
608
- await this.demuxer.advanceReader();
609
- continue;
610
- }
611
-
612
- const packet = this.demuxer.loadedSamples[packetIndex]!;
613
- const sampleTimestamp
614
- = packet.blockOffset / this.demuxer.audioInfo.sampleRate;
615
- const sampleDuration
616
- = packet.blockSize / this.demuxer.audioInfo.sampleRate;
617
-
618
- if (sampleTimestamp + sampleDuration <= timestamp) {
619
- if (this.demuxer.lastSampleLoaded) {
620
- return this.getPacketAtIndex(
621
- this.demuxer.loadedSamples.length - 1,
622
- options,
623
- );
624
- }
625
-
626
- await this.demuxer.advanceReader();
627
- continue;
628
- }
629
-
630
- return this.getPacketAtIndex(packetIndex, options);
631
- }
632
- } finally {
633
- release();
634
- }
635
- }
636
-
637
- async getNextPacket(
638
- packet: EncodedPacket,
639
- options: PacketRetrievalOptions,
640
- ): Promise<EncodedPacket | null> {
641
- const release = await this.demuxer.readingMutex.acquire();
642
- try {
643
- const nextIndex = packet.sequenceNumber + 1;
644
- if (
645
- this.demuxer.lastSampleLoaded
646
- && nextIndex >= this.demuxer.loadedSamples.length
647
- ) {
648
- return null;
649
- }
650
-
651
- // Ensure the next sample exists
652
- while (
653
- nextIndex >= this.demuxer.loadedSamples.length
654
- && !this.demuxer.lastSampleLoaded
655
- ) {
656
- await this.demuxer.advanceReader();
657
- }
658
- return this.getPacketAtIndex(nextIndex, options);
659
- } finally {
660
- release();
661
- }
662
- }
663
-
664
- getKeyPacket(
665
- timestamp: number,
666
- options: PacketRetrievalOptions,
667
- ): Promise<EncodedPacket | null> {
668
- return this.getPacket(timestamp, options);
669
- }
670
-
671
- getNextKeyPacket(
672
- packet: EncodedPacket,
673
- options: PacketRetrievalOptions,
674
- ): Promise<EncodedPacket | null> {
675
- return this.getNextPacket(packet, options);
676
- }
677
-
678
- async getPacketAtIndex(
679
- sampleIndex: number,
680
- options: PacketRetrievalOptions,
681
- ): Promise<EncodedPacket | null> {
682
- const rawSample = this.demuxer.loadedSamples[sampleIndex];
683
- if (!rawSample) {
684
- return null;
685
- }
686
-
687
- let data: Uint8Array;
688
- if (options.metadataOnly) {
689
- data = PLACEHOLDER_DATA;
690
- } else {
691
- let slice = this.demuxer.reader.requestSlice(
692
- rawSample.byteOffset,
693
- rawSample.byteSize,
694
- );
695
- if (slice instanceof Promise) slice = await slice;
696
-
697
- if (!slice) {
698
- return null; // Data didn't fit into the rest of the file
699
- }
700
-
701
- data = readBytes(slice, rawSample.byteSize);
702
- }
703
-
704
- assert(this.demuxer.audioInfo);
705
- const timestamp = rawSample.blockOffset / this.demuxer.audioInfo.sampleRate;
706
- const duration = rawSample.blockSize / this.demuxer.audioInfo.sampleRate;
707
- return new EncodedPacket(
708
- data,
709
- 'key',
710
- timestamp,
711
- duration,
712
- sampleIndex,
713
- rawSample.byteSize,
714
- );
715
- }
716
-
717
- async getFirstPacket(
718
- options: PacketRetrievalOptions,
719
- ): Promise<EncodedPacket | null> {
720
- // Ensure the next sample exists
721
- while (
722
- this.demuxer.loadedSamples.length === 0
723
- && !this.demuxer.lastSampleLoaded
724
- ) {
725
- await this.demuxer.advanceReader();
726
- }
727
-
728
- return this.getPacketAtIndex(0, options);
729
- }
730
- }