@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
@@ -0,0 +1,1058 @@
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
+ // src/misc.ts
10
+ /*!
11
+ * Copyright (c) 2025-present, Vanilagy and contributors
12
+ *
13
+ * This Source Code Form is subject to the terms of the Mozilla Public
14
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
15
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
16
+ */
17
+ var toDataView = (source) => {
18
+ if (source.constructor === DataView) {
19
+ return source;
20
+ }
21
+ if (ArrayBuffer.isView(source)) {
22
+ return new DataView(source.buffer, source.byteOffset, source.byteLength);
23
+ }
24
+ if (typeof SharedArrayBuffer !== "undefined" && source instanceof SharedArrayBuffer) {
25
+ return new DataView(source);
26
+ }
27
+ return new DataView(source);
28
+ };
29
+ var textDecoder = new TextDecoder;
30
+ var textEncoder = new TextEncoder;
31
+ var invertObject = (object) => {
32
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [value, key]));
33
+ };
34
+ var COLOR_PRIMARIES_MAP = {
35
+ bt709: 1,
36
+ bt470bg: 5,
37
+ smpte170m: 6,
38
+ bt2020: 9,
39
+ smpte432: 12
40
+ };
41
+ var COLOR_PRIMARIES_MAP_INVERSE = invertObject(COLOR_PRIMARIES_MAP);
42
+ var TRANSFER_CHARACTERISTICS_MAP = {
43
+ bt709: 1,
44
+ smpte170m: 6,
45
+ linear: 8,
46
+ "iec61966-2-1": 13,
47
+ pq: 16,
48
+ hlg: 18
49
+ };
50
+ var TRANSFER_CHARACTERISTICS_MAP_INVERSE = invertObject(TRANSFER_CHARACTERISTICS_MAP);
51
+ var MATRIX_COEFFICIENTS_MAP = {
52
+ rgb: 0,
53
+ bt709: 1,
54
+ bt470bg: 5,
55
+ smpte170m: 6,
56
+ "bt2020-ncl": 9
57
+ };
58
+ var MATRIX_COEFFICIENTS_MAP_INVERSE = invertObject(MATRIX_COEFFICIENTS_MAP);
59
+ var isAllowSharedBufferSource = (x) => {
60
+ return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x);
61
+ };
62
+
63
+ class AsyncMutex {
64
+ currentPromise = Promise.resolve();
65
+ async acquire() {
66
+ let resolver;
67
+ const nextPromise = new Promise((resolve) => {
68
+ resolver = resolve;
69
+ });
70
+ const currentPromiseAlias = this.currentPromise;
71
+ this.currentPromise = nextPromise;
72
+ await currentPromiseAlias;
73
+ return resolver;
74
+ }
75
+ }
76
+ var clamp = (value, min, max) => {
77
+ return Math.max(min, Math.min(max, value));
78
+ };
79
+ var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);
80
+ class CallSerializer {
81
+ currentPromise = Promise.resolve();
82
+ call(fn) {
83
+ return this.currentPromise = this.currentPromise.then(fn);
84
+ }
85
+ }
86
+ var polyfillSymbolDispose = () => {
87
+ Symbol.dispose ??= Symbol("Symbol.dispose");
88
+ };
89
+
90
+ // src/custom-coder.ts
91
+ /*!
92
+ * Copyright (c) 2025-present, Vanilagy and contributors
93
+ *
94
+ * This Source Code Form is subject to the terms of the Mozilla Public
95
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
96
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
97
+ */
98
+
99
+ class CustomVideoDecoder {
100
+ codec;
101
+ config;
102
+ onSample;
103
+ static supports(codec, config) {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ class CustomAudioDecoder {
109
+ codec;
110
+ config;
111
+ onSample;
112
+ static supports(codec, config) {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ class CustomVideoEncoder {
118
+ codec;
119
+ config;
120
+ onPacket;
121
+ static supports(codec, config) {
122
+ return false;
123
+ }
124
+ }
125
+
126
+ class CustomAudioEncoder {
127
+ codec;
128
+ config;
129
+ onPacket;
130
+ static supports(codec, config) {
131
+ return false;
132
+ }
133
+ }
134
+ var customVideoDecoders = [];
135
+ var customAudioDecoders = [];
136
+ var customVideoEncoders = [];
137
+ var customAudioEncoders = [];
138
+ var registerDecoder = (decoder) => {
139
+ if (decoder.prototype instanceof CustomVideoDecoder) {
140
+ const casted = decoder;
141
+ if (customVideoDecoders.includes(casted)) {
142
+ console.warn("Video decoder already registered.");
143
+ return;
144
+ }
145
+ customVideoDecoders.push(casted);
146
+ } else if (decoder.prototype instanceof CustomAudioDecoder) {
147
+ const casted = decoder;
148
+ if (customAudioDecoders.includes(casted)) {
149
+ console.warn("Audio decoder already registered.");
150
+ return;
151
+ }
152
+ customAudioDecoders.push(casted);
153
+ } else {
154
+ throw new TypeError("Decoder must be a CustomVideoDecoder or CustomAudioDecoder.");
155
+ }
156
+ };
157
+ var registerEncoder = (encoder) => {
158
+ if (encoder.prototype instanceof CustomVideoEncoder) {
159
+ const casted = encoder;
160
+ if (customVideoEncoders.includes(casted)) {
161
+ console.warn("Video encoder already registered.");
162
+ return;
163
+ }
164
+ customVideoEncoders.push(casted);
165
+ } else if (encoder.prototype instanceof CustomAudioEncoder) {
166
+ const casted = encoder;
167
+ if (customAudioEncoders.includes(casted)) {
168
+ console.warn("Audio encoder already registered.");
169
+ return;
170
+ }
171
+ customAudioEncoders.push(casted);
172
+ } else {
173
+ throw new TypeError("Encoder must be a CustomVideoEncoder or CustomAudioEncoder.");
174
+ }
175
+ };
176
+
177
+ // src/packet.ts
178
+ /*!
179
+ * Copyright (c) 2025-present, Vanilagy and contributors
180
+ *
181
+ * This Source Code Form is subject to the terms of the Mozilla Public
182
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
183
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
184
+ */
185
+ var PLACEHOLDER_DATA = new Uint8Array(0);
186
+
187
+ class EncodedPacket {
188
+ data;
189
+ type;
190
+ timestamp;
191
+ duration;
192
+ sequenceNumber;
193
+ byteLength;
194
+ sideData;
195
+ constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) {
196
+ this.data = data;
197
+ this.type = type;
198
+ this.timestamp = timestamp;
199
+ this.duration = duration;
200
+ this.sequenceNumber = sequenceNumber;
201
+ if (data === PLACEHOLDER_DATA && byteLength === undefined) {
202
+ throw new Error("Internal error: byteLength must be explicitly provided when constructing metadata-only packets.");
203
+ }
204
+ if (byteLength === undefined) {
205
+ byteLength = data.byteLength;
206
+ }
207
+ if (!(data instanceof Uint8Array)) {
208
+ throw new TypeError("data must be a Uint8Array.");
209
+ }
210
+ if (type !== "key" && type !== "delta") {
211
+ throw new TypeError('type must be either "key" or "delta".');
212
+ }
213
+ if (!Number.isFinite(timestamp)) {
214
+ throw new TypeError("timestamp must be a number.");
215
+ }
216
+ if (!Number.isFinite(duration) || duration < 0) {
217
+ throw new TypeError("duration must be a non-negative number.");
218
+ }
219
+ if (!Number.isFinite(sequenceNumber)) {
220
+ throw new TypeError("sequenceNumber must be a number.");
221
+ }
222
+ if (!Number.isInteger(byteLength) || byteLength < 0) {
223
+ throw new TypeError("byteLength must be a non-negative integer.");
224
+ }
225
+ if (sideData !== undefined && (typeof sideData !== "object" || !sideData)) {
226
+ throw new TypeError("sideData, when provided, must be an object.");
227
+ }
228
+ if (sideData?.alpha !== undefined && !(sideData.alpha instanceof Uint8Array)) {
229
+ throw new TypeError("sideData.alpha, when provided, must be a Uint8Array.");
230
+ }
231
+ if (sideData?.alphaByteLength !== undefined && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) {
232
+ throw new TypeError("sideData.alphaByteLength, when provided, must be a non-negative integer.");
233
+ }
234
+ this.byteLength = byteLength;
235
+ this.sideData = sideData ?? {};
236
+ if (this.sideData.alpha && this.sideData.alphaByteLength === undefined) {
237
+ this.sideData.alphaByteLength = this.sideData.alpha.byteLength;
238
+ }
239
+ }
240
+ get isMetadataOnly() {
241
+ return this.data === PLACEHOLDER_DATA;
242
+ }
243
+ get microsecondTimestamp() {
244
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);
245
+ }
246
+ get microsecondDuration() {
247
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);
248
+ }
249
+ toEncodedVideoChunk() {
250
+ if (this.isMetadataOnly) {
251
+ throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");
252
+ }
253
+ if (typeof EncodedVideoChunk === "undefined") {
254
+ throw new Error("Your browser does not support EncodedVideoChunk.");
255
+ }
256
+ return new EncodedVideoChunk({
257
+ data: this.data,
258
+ type: this.type,
259
+ timestamp: this.microsecondTimestamp,
260
+ duration: this.microsecondDuration
261
+ });
262
+ }
263
+ alphaToEncodedVideoChunk(type = this.type) {
264
+ if (!this.sideData.alpha) {
265
+ throw new TypeError("This packet does not contain alpha side data.");
266
+ }
267
+ if (this.isMetadataOnly) {
268
+ throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");
269
+ }
270
+ if (typeof EncodedVideoChunk === "undefined") {
271
+ throw new Error("Your browser does not support EncodedVideoChunk.");
272
+ }
273
+ return new EncodedVideoChunk({
274
+ data: this.sideData.alpha,
275
+ type,
276
+ timestamp: this.microsecondTimestamp,
277
+ duration: this.microsecondDuration
278
+ });
279
+ }
280
+ toEncodedAudioChunk() {
281
+ if (this.isMetadataOnly) {
282
+ throw new TypeError("Metadata-only packets cannot be converted to an audio chunk.");
283
+ }
284
+ if (typeof EncodedAudioChunk === "undefined") {
285
+ throw new Error("Your browser does not support EncodedAudioChunk.");
286
+ }
287
+ return new EncodedAudioChunk({
288
+ data: this.data,
289
+ type: this.type,
290
+ timestamp: this.microsecondTimestamp,
291
+ duration: this.microsecondDuration
292
+ });
293
+ }
294
+ static fromEncodedChunk(chunk, sideData) {
295
+ if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) {
296
+ throw new TypeError("chunk must be an EncodedVideoChunk or EncodedAudioChunk.");
297
+ }
298
+ const data = new Uint8Array(chunk.byteLength);
299
+ chunk.copyTo(data);
300
+ return new EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, undefined, undefined, sideData);
301
+ }
302
+ clone(options) {
303
+ if (options !== undefined && (typeof options !== "object" || options === null)) {
304
+ throw new TypeError("options, when provided, must be an object.");
305
+ }
306
+ if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) {
307
+ throw new TypeError("options.timestamp, when provided, must be a number.");
308
+ }
309
+ if (options?.duration !== undefined && !Number.isFinite(options.duration)) {
310
+ throw new TypeError("options.duration, when provided, must be a number.");
311
+ }
312
+ return new EncodedPacket(this.data, this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, this.sequenceNumber, this.byteLength);
313
+ }
314
+ }
315
+
316
+ // src/sample.ts
317
+ /*!
318
+ * Copyright (c) 2025-present, Vanilagy and contributors
319
+ *
320
+ * This Source Code Form is subject to the terms of the Mozilla Public
321
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
322
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
323
+ */
324
+ polyfillSymbolDispose();
325
+ var lastVideoGcErrorLog = -Infinity;
326
+ var lastAudioGcErrorLog = -Infinity;
327
+ var finalizationRegistry = null;
328
+ if (typeof FinalizationRegistry !== "undefined") {
329
+ finalizationRegistry = new FinalizationRegistry((value) => {
330
+ const now = Date.now();
331
+ if (value.type === "video") {
332
+ if (now - lastVideoGcErrorLog >= 1000) {
333
+ console.error(`A VideoSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your VideoSamples as soon as you're done using them.`);
334
+ lastVideoGcErrorLog = now;
335
+ }
336
+ if (typeof VideoFrame !== "undefined" && value.data instanceof VideoFrame) {
337
+ value.data.close();
338
+ }
339
+ } else {
340
+ if (now - lastAudioGcErrorLog >= 1000) {
341
+ console.error(`An AudioSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your AudioSamples as soon as you're done using them.`);
342
+ lastAudioGcErrorLog = now;
343
+ }
344
+ if (typeof AudioData !== "undefined" && value.data instanceof AudioData) {
345
+ value.data.close();
346
+ }
347
+ }
348
+ });
349
+ }
350
+ var VIDEO_SAMPLE_PIXEL_FORMATS = [
351
+ "I420",
352
+ "I420P10",
353
+ "I420P12",
354
+ "I420A",
355
+ "I420AP10",
356
+ "I420AP12",
357
+ "I422",
358
+ "I422P10",
359
+ "I422P12",
360
+ "I422A",
361
+ "I422AP10",
362
+ "I422AP12",
363
+ "I444",
364
+ "I444P10",
365
+ "I444P12",
366
+ "I444A",
367
+ "I444AP10",
368
+ "I444AP12",
369
+ "NV12",
370
+ "RGBA",
371
+ "RGBX",
372
+ "BGRA",
373
+ "BGRX"
374
+ ];
375
+ var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS);
376
+ var AUDIO_SAMPLE_FORMATS = new Set([
377
+ "f32",
378
+ "f32-planar",
379
+ "s16",
380
+ "s16-planar",
381
+ "s32",
382
+ "s32-planar",
383
+ "u8",
384
+ "u8-planar"
385
+ ]);
386
+
387
+ class AudioSample {
388
+ _data;
389
+ _closed = false;
390
+ format;
391
+ sampleRate;
392
+ numberOfFrames;
393
+ numberOfChannels;
394
+ duration;
395
+ timestamp;
396
+ get microsecondTimestamp() {
397
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);
398
+ }
399
+ get microsecondDuration() {
400
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);
401
+ }
402
+ constructor(init) {
403
+ if (isAudioData(init)) {
404
+ if (init.format === null) {
405
+ throw new TypeError("AudioData with null format is not supported.");
406
+ }
407
+ this._data = init;
408
+ this.format = init.format;
409
+ this.sampleRate = init.sampleRate;
410
+ this.numberOfFrames = init.numberOfFrames;
411
+ this.numberOfChannels = init.numberOfChannels;
412
+ this.timestamp = init.timestamp / 1e6;
413
+ this.duration = init.numberOfFrames / init.sampleRate;
414
+ } else {
415
+ if (!init || typeof init !== "object") {
416
+ throw new TypeError("Invalid AudioDataInit: must be an object.");
417
+ }
418
+ if (!AUDIO_SAMPLE_FORMATS.has(init.format)) {
419
+ throw new TypeError("Invalid AudioDataInit: invalid format.");
420
+ }
421
+ if (!Number.isFinite(init.sampleRate) || init.sampleRate <= 0) {
422
+ throw new TypeError("Invalid AudioDataInit: sampleRate must be > 0.");
423
+ }
424
+ if (!Number.isInteger(init.numberOfChannels) || init.numberOfChannels === 0) {
425
+ throw new TypeError("Invalid AudioDataInit: numberOfChannels must be an integer > 0.");
426
+ }
427
+ if (!Number.isFinite(init?.timestamp)) {
428
+ throw new TypeError("init.timestamp must be a number.");
429
+ }
430
+ const numberOfFrames = init.data.byteLength / (getBytesPerSample(init.format) * init.numberOfChannels);
431
+ if (!Number.isInteger(numberOfFrames)) {
432
+ throw new TypeError("Invalid AudioDataInit: data size is not a multiple of frame size.");
433
+ }
434
+ this.format = init.format;
435
+ this.sampleRate = init.sampleRate;
436
+ this.numberOfFrames = numberOfFrames;
437
+ this.numberOfChannels = init.numberOfChannels;
438
+ this.timestamp = init.timestamp;
439
+ this.duration = numberOfFrames / init.sampleRate;
440
+ let dataBuffer;
441
+ if (init.data instanceof ArrayBuffer) {
442
+ dataBuffer = new Uint8Array(init.data);
443
+ } else if (ArrayBuffer.isView(init.data)) {
444
+ dataBuffer = new Uint8Array(init.data.buffer, init.data.byteOffset, init.data.byteLength);
445
+ } else {
446
+ throw new TypeError("Invalid AudioDataInit: data is not a BufferSource.");
447
+ }
448
+ const expectedSize = this.numberOfFrames * this.numberOfChannels * getBytesPerSample(this.format);
449
+ if (dataBuffer.byteLength < expectedSize) {
450
+ throw new TypeError("Invalid AudioDataInit: insufficient data size.");
451
+ }
452
+ this._data = dataBuffer;
453
+ }
454
+ finalizationRegistry?.register(this, { type: "audio", data: this._data }, this);
455
+ }
456
+ allocationSize(options) {
457
+ if (!options || typeof options !== "object") {
458
+ throw new TypeError("options must be an object.");
459
+ }
460
+ if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {
461
+ throw new TypeError("planeIndex must be a non-negative integer.");
462
+ }
463
+ if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {
464
+ throw new TypeError("Invalid format.");
465
+ }
466
+ if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {
467
+ throw new TypeError("frameOffset must be a non-negative integer.");
468
+ }
469
+ if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {
470
+ throw new TypeError("frameCount must be a non-negative integer.");
471
+ }
472
+ if (this._closed) {
473
+ throw new Error("AudioSample is closed.");
474
+ }
475
+ const destFormat = options.format ?? this.format;
476
+ const frameOffset = options.frameOffset ?? 0;
477
+ if (frameOffset >= this.numberOfFrames) {
478
+ throw new RangeError("frameOffset out of range");
479
+ }
480
+ const copyFrameCount = options.frameCount !== undefined ? options.frameCount : this.numberOfFrames - frameOffset;
481
+ if (copyFrameCount > this.numberOfFrames - frameOffset) {
482
+ throw new RangeError("frameCount out of range");
483
+ }
484
+ const bytesPerSample = getBytesPerSample(destFormat);
485
+ const isPlanar = formatIsPlanar(destFormat);
486
+ if (isPlanar && options.planeIndex >= this.numberOfChannels) {
487
+ throw new RangeError("planeIndex out of range");
488
+ }
489
+ if (!isPlanar && options.planeIndex !== 0) {
490
+ throw new RangeError("planeIndex out of range");
491
+ }
492
+ const elementCount = isPlanar ? copyFrameCount : copyFrameCount * this.numberOfChannels;
493
+ return elementCount * bytesPerSample;
494
+ }
495
+ copyTo(destination, options) {
496
+ if (!isAllowSharedBufferSource(destination)) {
497
+ throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");
498
+ }
499
+ if (!options || typeof options !== "object") {
500
+ throw new TypeError("options must be an object.");
501
+ }
502
+ if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {
503
+ throw new TypeError("planeIndex must be a non-negative integer.");
504
+ }
505
+ if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {
506
+ throw new TypeError("Invalid format.");
507
+ }
508
+ if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {
509
+ throw new TypeError("frameOffset must be a non-negative integer.");
510
+ }
511
+ if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {
512
+ throw new TypeError("frameCount must be a non-negative integer.");
513
+ }
514
+ if (this._closed) {
515
+ throw new Error("AudioSample is closed.");
516
+ }
517
+ const { planeIndex, format, frameCount: optFrameCount, frameOffset: optFrameOffset } = options;
518
+ const destFormat = format ?? this.format;
519
+ if (!destFormat)
520
+ throw new Error("Destination format not determined");
521
+ const numFrames = this.numberOfFrames;
522
+ const numChannels = this.numberOfChannels;
523
+ const frameOffset = optFrameOffset ?? 0;
524
+ if (frameOffset >= numFrames) {
525
+ throw new RangeError("frameOffset out of range");
526
+ }
527
+ const copyFrameCount = optFrameCount !== undefined ? optFrameCount : numFrames - frameOffset;
528
+ if (copyFrameCount > numFrames - frameOffset) {
529
+ throw new RangeError("frameCount out of range");
530
+ }
531
+ const destBytesPerSample = getBytesPerSample(destFormat);
532
+ const destIsPlanar = formatIsPlanar(destFormat);
533
+ if (destIsPlanar && planeIndex >= numChannels) {
534
+ throw new RangeError("planeIndex out of range");
535
+ }
536
+ if (!destIsPlanar && planeIndex !== 0) {
537
+ throw new RangeError("planeIndex out of range");
538
+ }
539
+ const destElementCount = destIsPlanar ? copyFrameCount : copyFrameCount * numChannels;
540
+ const requiredSize = destElementCount * destBytesPerSample;
541
+ if (destination.byteLength < requiredSize) {
542
+ throw new RangeError("Destination buffer is too small");
543
+ }
544
+ const destView = toDataView(destination);
545
+ const writeFn = getWriteFunction(destFormat);
546
+ if (isAudioData(this._data)) {
547
+ if (destIsPlanar) {
548
+ if (destFormat === "f32-planar") {
549
+ this._data.copyTo(destination, {
550
+ planeIndex,
551
+ frameOffset,
552
+ frameCount: copyFrameCount,
553
+ format: "f32-planar"
554
+ });
555
+ } else {
556
+ const tempBuffer = new ArrayBuffer(copyFrameCount * 4);
557
+ const tempArray = new Float32Array(tempBuffer);
558
+ this._data.copyTo(tempArray, {
559
+ planeIndex,
560
+ frameOffset,
561
+ frameCount: copyFrameCount,
562
+ format: "f32-planar"
563
+ });
564
+ const tempView = new DataView(tempBuffer);
565
+ for (let i = 0;i < copyFrameCount; i++) {
566
+ const destOffset = i * destBytesPerSample;
567
+ const sample = tempView.getFloat32(i * 4, true);
568
+ writeFn(destView, destOffset, sample);
569
+ }
570
+ }
571
+ } else {
572
+ const numCh = numChannels;
573
+ const temp = new Float32Array(copyFrameCount);
574
+ for (let ch = 0;ch < numCh; ch++) {
575
+ this._data.copyTo(temp, {
576
+ planeIndex: ch,
577
+ frameOffset,
578
+ frameCount: copyFrameCount,
579
+ format: "f32-planar"
580
+ });
581
+ for (let i = 0;i < copyFrameCount; i++) {
582
+ const destIndex = i * numCh + ch;
583
+ const destOffset = destIndex * destBytesPerSample;
584
+ writeFn(destView, destOffset, temp[i]);
585
+ }
586
+ }
587
+ }
588
+ } else {
589
+ const uint8Data = this._data;
590
+ const srcView = toDataView(uint8Data);
591
+ const srcFormat = this.format;
592
+ const readFn = getReadFunction(srcFormat);
593
+ const srcBytesPerSample = getBytesPerSample(srcFormat);
594
+ const srcIsPlanar = formatIsPlanar(srcFormat);
595
+ for (let i = 0;i < copyFrameCount; i++) {
596
+ if (destIsPlanar) {
597
+ const destOffset = i * destBytesPerSample;
598
+ let srcOffset;
599
+ if (srcIsPlanar) {
600
+ srcOffset = (planeIndex * numFrames + (i + frameOffset)) * srcBytesPerSample;
601
+ } else {
602
+ srcOffset = ((i + frameOffset) * numChannels + planeIndex) * srcBytesPerSample;
603
+ }
604
+ const normalized = readFn(srcView, srcOffset);
605
+ writeFn(destView, destOffset, normalized);
606
+ } else {
607
+ for (let ch = 0;ch < numChannels; ch++) {
608
+ const destIndex = i * numChannels + ch;
609
+ const destOffset = destIndex * destBytesPerSample;
610
+ let srcOffset;
611
+ if (srcIsPlanar) {
612
+ srcOffset = (ch * numFrames + (i + frameOffset)) * srcBytesPerSample;
613
+ } else {
614
+ srcOffset = ((i + frameOffset) * numChannels + ch) * srcBytesPerSample;
615
+ }
616
+ const normalized = readFn(srcView, srcOffset);
617
+ writeFn(destView, destOffset, normalized);
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ clone() {
624
+ if (this._closed) {
625
+ throw new Error("AudioSample is closed.");
626
+ }
627
+ if (isAudioData(this._data)) {
628
+ const sample = new AudioSample(this._data.clone());
629
+ sample.setTimestamp(this.timestamp);
630
+ return sample;
631
+ }
632
+ return new AudioSample({
633
+ format: this.format,
634
+ sampleRate: this.sampleRate,
635
+ numberOfFrames: this.numberOfFrames,
636
+ numberOfChannels: this.numberOfChannels,
637
+ timestamp: this.timestamp,
638
+ data: this._data
639
+ });
640
+ }
641
+ close() {
642
+ if (this._closed) {
643
+ return;
644
+ }
645
+ finalizationRegistry?.unregister(this);
646
+ if (isAudioData(this._data)) {
647
+ this._data.close();
648
+ } else {
649
+ this._data = new Uint8Array(0);
650
+ }
651
+ this._closed = true;
652
+ }
653
+ toAudioData() {
654
+ if (this._closed) {
655
+ throw new Error("AudioSample is closed.");
656
+ }
657
+ if (isAudioData(this._data)) {
658
+ if (this._data.timestamp === this.microsecondTimestamp) {
659
+ return this._data.clone();
660
+ }
661
+ if (formatIsPlanar(this.format)) {
662
+ const size = this.allocationSize({
663
+ planeIndex: 0,
664
+ format: this.format
665
+ });
666
+ const data2 = new ArrayBuffer(size * this.numberOfChannels);
667
+ for (let i = 0;i < this.numberOfChannels; i++) {
668
+ this.copyTo(new Uint8Array(data2, i * size, size), {
669
+ planeIndex: i,
670
+ format: this.format
671
+ });
672
+ }
673
+ return new AudioData({
674
+ format: this.format,
675
+ sampleRate: this.sampleRate,
676
+ numberOfFrames: this.numberOfFrames,
677
+ numberOfChannels: this.numberOfChannels,
678
+ timestamp: this.microsecondTimestamp,
679
+ data: data2
680
+ });
681
+ }
682
+ const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format }));
683
+ this.copyTo(data, { planeIndex: 0, format: this.format });
684
+ return new AudioData({
685
+ format: this.format,
686
+ sampleRate: this.sampleRate,
687
+ numberOfFrames: this.numberOfFrames,
688
+ numberOfChannels: this.numberOfChannels,
689
+ timestamp: this.microsecondTimestamp,
690
+ data
691
+ });
692
+ }
693
+ return new AudioData({
694
+ format: this.format,
695
+ sampleRate: this.sampleRate,
696
+ numberOfFrames: this.numberOfFrames,
697
+ numberOfChannels: this.numberOfChannels,
698
+ timestamp: this.microsecondTimestamp,
699
+ data: this._data.buffer instanceof ArrayBuffer ? this._data.buffer : this._data.slice()
700
+ });
701
+ }
702
+ toAudioBuffer() {
703
+ if (this._closed) {
704
+ throw new Error("AudioSample is closed.");
705
+ }
706
+ const audioBuffer = new AudioBuffer({
707
+ numberOfChannels: this.numberOfChannels,
708
+ length: this.numberOfFrames,
709
+ sampleRate: this.sampleRate
710
+ });
711
+ const dataBytes = new Float32Array(this.allocationSize({ planeIndex: 0, format: "f32-planar" }) / 4);
712
+ for (let i = 0;i < this.numberOfChannels; i++) {
713
+ this.copyTo(dataBytes, { planeIndex: i, format: "f32-planar" });
714
+ audioBuffer.copyToChannel(dataBytes, i);
715
+ }
716
+ return audioBuffer;
717
+ }
718
+ setTimestamp(newTimestamp) {
719
+ if (!Number.isFinite(newTimestamp)) {
720
+ throw new TypeError("newTimestamp must be a number.");
721
+ }
722
+ this.timestamp = newTimestamp;
723
+ }
724
+ [Symbol.dispose]() {
725
+ this.close();
726
+ }
727
+ static *_fromAudioBuffer(audioBuffer, timestamp) {
728
+ if (!(audioBuffer instanceof AudioBuffer)) {
729
+ throw new TypeError("audioBuffer must be an AudioBuffer.");
730
+ }
731
+ const MAX_FLOAT_COUNT = 48000 * 5;
732
+ const numberOfChannels = audioBuffer.numberOfChannels;
733
+ const sampleRate = audioBuffer.sampleRate;
734
+ const totalFrames = audioBuffer.length;
735
+ const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);
736
+ let currentRelativeFrame = 0;
737
+ let remainingFrames = totalFrames;
738
+ while (remainingFrames > 0) {
739
+ const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);
740
+ const chunkData = new Float32Array(numberOfChannels * framesToCopy);
741
+ for (let channel = 0;channel < numberOfChannels; channel++) {
742
+ audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);
743
+ }
744
+ yield new AudioSample({
745
+ format: "f32-planar",
746
+ sampleRate,
747
+ numberOfFrames: framesToCopy,
748
+ numberOfChannels,
749
+ timestamp: timestamp + currentRelativeFrame / sampleRate,
750
+ data: chunkData
751
+ });
752
+ currentRelativeFrame += framesToCopy;
753
+ remainingFrames -= framesToCopy;
754
+ }
755
+ }
756
+ static fromAudioBuffer(audioBuffer, timestamp) {
757
+ if (!(audioBuffer instanceof AudioBuffer)) {
758
+ throw new TypeError("audioBuffer must be an AudioBuffer.");
759
+ }
760
+ const MAX_FLOAT_COUNT = 48000 * 5;
761
+ const numberOfChannels = audioBuffer.numberOfChannels;
762
+ const sampleRate = audioBuffer.sampleRate;
763
+ const totalFrames = audioBuffer.length;
764
+ const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);
765
+ let currentRelativeFrame = 0;
766
+ let remainingFrames = totalFrames;
767
+ const result = [];
768
+ while (remainingFrames > 0) {
769
+ const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);
770
+ const chunkData = new Float32Array(numberOfChannels * framesToCopy);
771
+ for (let channel = 0;channel < numberOfChannels; channel++) {
772
+ audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);
773
+ }
774
+ const audioSample = new AudioSample({
775
+ format: "f32-planar",
776
+ sampleRate,
777
+ numberOfFrames: framesToCopy,
778
+ numberOfChannels,
779
+ timestamp: timestamp + currentRelativeFrame / sampleRate,
780
+ data: chunkData
781
+ });
782
+ result.push(audioSample);
783
+ currentRelativeFrame += framesToCopy;
784
+ remainingFrames -= framesToCopy;
785
+ }
786
+ return result;
787
+ }
788
+ }
789
+ var getBytesPerSample = (format) => {
790
+ switch (format) {
791
+ case "u8":
792
+ case "u8-planar":
793
+ return 1;
794
+ case "s16":
795
+ case "s16-planar":
796
+ return 2;
797
+ case "s32":
798
+ case "s32-planar":
799
+ return 4;
800
+ case "f32":
801
+ case "f32-planar":
802
+ return 4;
803
+ default:
804
+ throw new Error("Unknown AudioSampleFormat");
805
+ }
806
+ };
807
+ var formatIsPlanar = (format) => {
808
+ switch (format) {
809
+ case "u8-planar":
810
+ case "s16-planar":
811
+ case "s32-planar":
812
+ case "f32-planar":
813
+ return true;
814
+ default:
815
+ return false;
816
+ }
817
+ };
818
+ var getReadFunction = (format) => {
819
+ switch (format) {
820
+ case "u8":
821
+ case "u8-planar":
822
+ return (view, offset) => (view.getUint8(offset) - 128) / 128;
823
+ case "s16":
824
+ case "s16-planar":
825
+ return (view, offset) => view.getInt16(offset, true) / 32768;
826
+ case "s32":
827
+ case "s32-planar":
828
+ return (view, offset) => view.getInt32(offset, true) / 2147483648;
829
+ case "f32":
830
+ case "f32-planar":
831
+ return (view, offset) => view.getFloat32(offset, true);
832
+ }
833
+ };
834
+ var getWriteFunction = (format) => {
835
+ switch (format) {
836
+ case "u8":
837
+ case "u8-planar":
838
+ return (view, offset, value) => view.setUint8(offset, clamp((value + 1) * 127.5, 0, 255));
839
+ case "s16":
840
+ case "s16-planar":
841
+ return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32767), -32768, 32767), true);
842
+ case "s32":
843
+ case "s32-planar":
844
+ return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), true);
845
+ case "f32":
846
+ case "f32-planar":
847
+ return (view, offset, value) => view.setFloat32(offset, value, true);
848
+ }
849
+ };
850
+ var isAudioData = (x) => {
851
+ return typeof AudioData !== "undefined" && x instanceof AudioData;
852
+ };
853
+ // src/index.ts
854
+ /*!
855
+ * Copyright (c) 2025-present, Vanilagy and contributors
856
+ *
857
+ * This Source Code Form is subject to the terms of the Mozilla Public
858
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
859
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
860
+ */
861
+
862
+ // packages/eac3/src/index.ts
863
+ /*!
864
+ * Copyright (c) 2025-present, Vanilagy and contributors (Wiedy Mi)
865
+ *
866
+ * This Source Code Form is subject to the terms of the Mozilla Public
867
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
868
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
869
+ */
870
+ var decodeWorkerUrl = new URL("./decode.worker.ts", import.meta.url);
871
+ var encodeWorkerUrl = new URL("./encode.worker.ts", import.meta.url);
872
+ var createWorker = (url) => {
873
+ return new Worker(url, { type: "module" });
874
+ };
875
+ var AV_CODEC_ID_AC3 = 86019;
876
+ var AV_CODEC_ID_EAC3 = 86056;
877
+
878
+ class Eac3Decoder extends CustomAudioDecoder {
879
+ worker = null;
880
+ nextMessageId = 0;
881
+ pendingMessages = new Map;
882
+ static supports(codec, _config) {
883
+ return codec === "eac3" || codec === "ac3";
884
+ }
885
+ async init() {
886
+ this.worker = createWorker(decodeWorkerUrl);
887
+ const onMessage = (event) => {
888
+ const data = event.data;
889
+ const pending = this.pendingMessages.get(data.id);
890
+ assert2(pending !== undefined);
891
+ this.pendingMessages.delete(data.id);
892
+ if (data.success) {
893
+ pending.resolve(data.data);
894
+ } else {
895
+ pending.reject(data.error);
896
+ }
897
+ };
898
+ this.worker.addEventListener("message", onMessage);
899
+ await this.sendCommand({
900
+ type: "init",
901
+ data: {
902
+ sampleRate: this.config.sampleRate,
903
+ channels: this.config.numberOfChannels,
904
+ codec: this.codec
905
+ }
906
+ });
907
+ }
908
+ async decode(packet) {
909
+ const packetData = packet.data.slice().buffer;
910
+ const result = await this.sendCommand({
911
+ type: "decode",
912
+ data: { packetData }
913
+ }, [packetData]);
914
+ if (!result || !("pcmData" in result)) {
915
+ return;
916
+ }
917
+ const audioSample = new AudioSample({
918
+ data: new Float32Array(result.pcmData),
919
+ format: "f32",
920
+ numberOfChannels: result.channels,
921
+ sampleRate: result.sampleRate,
922
+ timestamp: packet.timestamp
923
+ });
924
+ this.onSample(audioSample);
925
+ }
926
+ async flush() {
927
+ await this.sendCommand({ type: "flush" });
928
+ }
929
+ close() {
930
+ if (this.worker) {
931
+ this.sendCommand({ type: "close" });
932
+ this.worker.terminate();
933
+ }
934
+ }
935
+ sendCommand(command, transferables) {
936
+ return new Promise((resolve, reject) => {
937
+ const id = this.nextMessageId++;
938
+ this.pendingMessages.set(id, { resolve, reject });
939
+ assert2(this.worker);
940
+ if (transferables) {
941
+ this.worker.postMessage({ id, command }, transferables);
942
+ } else {
943
+ this.worker.postMessage({ id, command });
944
+ }
945
+ });
946
+ }
947
+ }
948
+
949
+ class Eac3Encoder extends CustomAudioEncoder {
950
+ worker = null;
951
+ nextMessageId = 0;
952
+ pendingMessages = new Map;
953
+ currentTimestamp = 0;
954
+ chunkMetadata = {};
955
+ static supports(codec, _config) {
956
+ return codec === "eac3" || codec === "ac3";
957
+ }
958
+ async init() {
959
+ this.worker = createWorker(encodeWorkerUrl);
960
+ const onMessage = (event) => {
961
+ const data = event.data;
962
+ const pending = this.pendingMessages.get(data.id);
963
+ assert2(pending !== undefined);
964
+ this.pendingMessages.delete(data.id);
965
+ if (data.success) {
966
+ pending.resolve(data.data);
967
+ } else {
968
+ pending.reject(data.error);
969
+ }
970
+ };
971
+ this.worker.addEventListener("message", onMessage);
972
+ assert2(this.config.bitrate);
973
+ await this.sendCommand({
974
+ type: "init",
975
+ data: {
976
+ sampleRate: this.config.sampleRate,
977
+ channels: this.config.numberOfChannels,
978
+ bitrate: this.config.bitrate,
979
+ codec: this.codec
980
+ }
981
+ });
982
+ this.chunkMetadata = {
983
+ decoderConfig: {
984
+ codec: this.codec === "eac3" ? "ec-3" : "ac3",
985
+ numberOfChannels: this.config.numberOfChannels,
986
+ sampleRate: this.config.sampleRate
987
+ }
988
+ };
989
+ }
990
+ async encode(audioSample) {
991
+ const sizePerChannel = audioSample.allocationSize({
992
+ format: "f32-planar",
993
+ planeIndex: 0
994
+ });
995
+ const requiredBytes = audioSample.numberOfChannels * sizePerChannel;
996
+ const audioData = new ArrayBuffer(requiredBytes);
997
+ const audioBytes = new Uint8Array(audioData);
998
+ for (let i = 0;i < audioSample.numberOfChannels; i++) {
999
+ audioSample.copyTo(audioBytes.subarray(i * sizePerChannel), {
1000
+ format: "f32-planar",
1001
+ planeIndex: i
1002
+ });
1003
+ }
1004
+ const result = await this.sendCommand({
1005
+ type: "encode",
1006
+ data: {
1007
+ pcmData: audioData,
1008
+ numberOfFrames: audioSample.numberOfFrames
1009
+ }
1010
+ }, [audioData]);
1011
+ assert2(result && "encodedData" in result);
1012
+ const duration = audioSample.numberOfFrames / this.config.sampleRate;
1013
+ const encodedPacket = new EncodedPacket(new Uint8Array(result.encodedData), "key", this.currentTimestamp, duration);
1014
+ this.onPacket(encodedPacket, this.currentTimestamp === 0 ? this.chunkMetadata : undefined);
1015
+ if (this.currentTimestamp === 0) {
1016
+ this.chunkMetadata = {};
1017
+ }
1018
+ this.currentTimestamp += duration;
1019
+ }
1020
+ async flush() {
1021
+ await this.sendCommand({ type: "flush" });
1022
+ }
1023
+ close() {
1024
+ if (this.worker) {
1025
+ this.sendCommand({ type: "close" });
1026
+ this.worker.terminate();
1027
+ }
1028
+ }
1029
+ sendCommand(command, transferables) {
1030
+ return new Promise((resolve, reject) => {
1031
+ const id = this.nextMessageId++;
1032
+ this.pendingMessages.set(id, { resolve, reject });
1033
+ assert2(this.worker);
1034
+ if (transferables) {
1035
+ this.worker.postMessage({ id, command }, transferables);
1036
+ } else {
1037
+ this.worker.postMessage({ id, command });
1038
+ }
1039
+ });
1040
+ }
1041
+ }
1042
+ var registerEac3Decoder = () => {
1043
+ registerDecoder(Eac3Decoder);
1044
+ };
1045
+ var registerEac3Encoder = () => {
1046
+ registerEncoder(Eac3Encoder);
1047
+ };
1048
+ function assert2(x) {
1049
+ if (!x) {
1050
+ throw new Error("Assertion failed.");
1051
+ }
1052
+ }
1053
+ export {
1054
+ registerEac3Encoder,
1055
+ registerEac3Decoder,
1056
+ AV_CODEC_ID_EAC3,
1057
+ AV_CODEC_ID_AC3
1058
+ };