@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
@@ -0,0 +1,1198 @@
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
+ function assert(x) {
18
+ if (!x) {
19
+ throw new Error("Assertion failed.");
20
+ }
21
+ }
22
+ var toUint8Array = (source) => {
23
+ if (source.constructor === Uint8Array) {
24
+ return source;
25
+ }
26
+ if (ArrayBuffer.isView(source)) {
27
+ return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
28
+ }
29
+ if (typeof SharedArrayBuffer !== "undefined" && source instanceof SharedArrayBuffer) {
30
+ return new Uint8Array(source);
31
+ }
32
+ return new Uint8Array(source);
33
+ };
34
+ var textDecoder = new TextDecoder;
35
+ var textEncoder = new TextEncoder;
36
+ var invertObject = (object) => {
37
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [value, key]));
38
+ };
39
+ var COLOR_PRIMARIES_MAP = {
40
+ bt709: 1,
41
+ bt470bg: 5,
42
+ smpte170m: 6,
43
+ bt2020: 9,
44
+ smpte432: 12
45
+ };
46
+ var COLOR_PRIMARIES_MAP_INVERSE = invertObject(COLOR_PRIMARIES_MAP);
47
+ var TRANSFER_CHARACTERISTICS_MAP = {
48
+ bt709: 1,
49
+ smpte170m: 6,
50
+ linear: 8,
51
+ "iec61966-2-1": 13,
52
+ pq: 16,
53
+ hlg: 18
54
+ };
55
+ var TRANSFER_CHARACTERISTICS_MAP_INVERSE = invertObject(TRANSFER_CHARACTERISTICS_MAP);
56
+ var MATRIX_COEFFICIENTS_MAP = {
57
+ rgb: 0,
58
+ bt709: 1,
59
+ bt470bg: 5,
60
+ smpte170m: 6,
61
+ "bt2020-ncl": 9
62
+ };
63
+ var MATRIX_COEFFICIENTS_MAP_INVERSE = invertObject(MATRIX_COEFFICIENTS_MAP);
64
+ var isAllowSharedBufferSource = (x) => {
65
+ return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x);
66
+ };
67
+
68
+ class AsyncMutex {
69
+ currentPromise = Promise.resolve();
70
+ async acquire() {
71
+ let resolver;
72
+ const nextPromise = new Promise((resolve) => {
73
+ resolver = resolve;
74
+ });
75
+ const currentPromiseAlias = this.currentPromise;
76
+ this.currentPromise = nextPromise;
77
+ await currentPromiseAlias;
78
+ return resolver;
79
+ }
80
+ }
81
+ var assertNever = (x) => {
82
+ throw new Error(`Unexpected value: ${x}`);
83
+ };
84
+ var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);
85
+ class CallSerializer {
86
+ currentPromise = Promise.resolve();
87
+ call(fn) {
88
+ return this.currentPromise = this.currentPromise.then(fn);
89
+ }
90
+ }
91
+ var isFirefoxCache = null;
92
+ var isFirefox = () => {
93
+ if (isFirefoxCache !== null) {
94
+ return isFirefoxCache;
95
+ }
96
+ return isFirefoxCache = typeof navigator !== "undefined" && navigator.userAgent?.includes("Firefox");
97
+ };
98
+ var polyfillSymbolDispose = () => {
99
+ Symbol.dispose ??= Symbol("Symbol.dispose");
100
+ };
101
+
102
+ // src/custom-coder.ts
103
+ /*!
104
+ * Copyright (c) 2025-present, Vanilagy and contributors
105
+ *
106
+ * This Source Code Form is subject to the terms of the Mozilla Public
107
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
108
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
109
+ */
110
+
111
+ class CustomVideoDecoder {
112
+ codec;
113
+ config;
114
+ onSample;
115
+ static supports(codec, config) {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ class CustomAudioDecoder {
121
+ codec;
122
+ config;
123
+ onSample;
124
+ static supports(codec, config) {
125
+ return false;
126
+ }
127
+ }
128
+
129
+ class CustomVideoEncoder {
130
+ codec;
131
+ config;
132
+ onPacket;
133
+ static supports(codec, config) {
134
+ return false;
135
+ }
136
+ }
137
+
138
+ class CustomAudioEncoder {
139
+ codec;
140
+ config;
141
+ onPacket;
142
+ static supports(codec, config) {
143
+ return false;
144
+ }
145
+ }
146
+ var customVideoDecoders = [];
147
+ var customAudioDecoders = [];
148
+ var customVideoEncoders = [];
149
+ var customAudioEncoders = [];
150
+ var registerDecoder = (decoder) => {
151
+ if (decoder.prototype instanceof CustomVideoDecoder) {
152
+ const casted = decoder;
153
+ if (customVideoDecoders.includes(casted)) {
154
+ console.warn("Video decoder already registered.");
155
+ return;
156
+ }
157
+ customVideoDecoders.push(casted);
158
+ } else if (decoder.prototype instanceof CustomAudioDecoder) {
159
+ const casted = decoder;
160
+ if (customAudioDecoders.includes(casted)) {
161
+ console.warn("Audio decoder already registered.");
162
+ return;
163
+ }
164
+ customAudioDecoders.push(casted);
165
+ } else {
166
+ throw new TypeError("Decoder must be a CustomVideoDecoder or CustomAudioDecoder.");
167
+ }
168
+ };
169
+ var registerEncoder = (encoder) => {
170
+ if (encoder.prototype instanceof CustomVideoEncoder) {
171
+ const casted = encoder;
172
+ if (customVideoEncoders.includes(casted)) {
173
+ console.warn("Video encoder already registered.");
174
+ return;
175
+ }
176
+ customVideoEncoders.push(casted);
177
+ } else if (encoder.prototype instanceof CustomAudioEncoder) {
178
+ const casted = encoder;
179
+ if (customAudioEncoders.includes(casted)) {
180
+ console.warn("Audio encoder already registered.");
181
+ return;
182
+ }
183
+ customAudioEncoders.push(casted);
184
+ } else {
185
+ throw new TypeError("Encoder must be a CustomVideoEncoder or CustomAudioEncoder.");
186
+ }
187
+ };
188
+
189
+ // src/packet.ts
190
+ /*!
191
+ * Copyright (c) 2025-present, Vanilagy and contributors
192
+ *
193
+ * This Source Code Form is subject to the terms of the Mozilla Public
194
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
195
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
196
+ */
197
+ var PLACEHOLDER_DATA = new Uint8Array(0);
198
+
199
+ class EncodedPacket {
200
+ data;
201
+ type;
202
+ timestamp;
203
+ duration;
204
+ sequenceNumber;
205
+ byteLength;
206
+ sideData;
207
+ constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) {
208
+ this.data = data;
209
+ this.type = type;
210
+ this.timestamp = timestamp;
211
+ this.duration = duration;
212
+ this.sequenceNumber = sequenceNumber;
213
+ if (data === PLACEHOLDER_DATA && byteLength === undefined) {
214
+ throw new Error("Internal error: byteLength must be explicitly provided when constructing metadata-only packets.");
215
+ }
216
+ if (byteLength === undefined) {
217
+ byteLength = data.byteLength;
218
+ }
219
+ if (!(data instanceof Uint8Array)) {
220
+ throw new TypeError("data must be a Uint8Array.");
221
+ }
222
+ if (type !== "key" && type !== "delta") {
223
+ throw new TypeError('type must be either "key" or "delta".');
224
+ }
225
+ if (!Number.isFinite(timestamp)) {
226
+ throw new TypeError("timestamp must be a number.");
227
+ }
228
+ if (!Number.isFinite(duration) || duration < 0) {
229
+ throw new TypeError("duration must be a non-negative number.");
230
+ }
231
+ if (!Number.isFinite(sequenceNumber)) {
232
+ throw new TypeError("sequenceNumber must be a number.");
233
+ }
234
+ if (!Number.isInteger(byteLength) || byteLength < 0) {
235
+ throw new TypeError("byteLength must be a non-negative integer.");
236
+ }
237
+ if (sideData !== undefined && (typeof sideData !== "object" || !sideData)) {
238
+ throw new TypeError("sideData, when provided, must be an object.");
239
+ }
240
+ if (sideData?.alpha !== undefined && !(sideData.alpha instanceof Uint8Array)) {
241
+ throw new TypeError("sideData.alpha, when provided, must be a Uint8Array.");
242
+ }
243
+ if (sideData?.alphaByteLength !== undefined && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) {
244
+ throw new TypeError("sideData.alphaByteLength, when provided, must be a non-negative integer.");
245
+ }
246
+ this.byteLength = byteLength;
247
+ this.sideData = sideData ?? {};
248
+ if (this.sideData.alpha && this.sideData.alphaByteLength === undefined) {
249
+ this.sideData.alphaByteLength = this.sideData.alpha.byteLength;
250
+ }
251
+ }
252
+ get isMetadataOnly() {
253
+ return this.data === PLACEHOLDER_DATA;
254
+ }
255
+ get microsecondTimestamp() {
256
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);
257
+ }
258
+ get microsecondDuration() {
259
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);
260
+ }
261
+ toEncodedVideoChunk() {
262
+ if (this.isMetadataOnly) {
263
+ throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");
264
+ }
265
+ if (typeof EncodedVideoChunk === "undefined") {
266
+ throw new Error("Your browser does not support EncodedVideoChunk.");
267
+ }
268
+ return new EncodedVideoChunk({
269
+ data: this.data,
270
+ type: this.type,
271
+ timestamp: this.microsecondTimestamp,
272
+ duration: this.microsecondDuration
273
+ });
274
+ }
275
+ alphaToEncodedVideoChunk(type = this.type) {
276
+ if (!this.sideData.alpha) {
277
+ throw new TypeError("This packet does not contain alpha side data.");
278
+ }
279
+ if (this.isMetadataOnly) {
280
+ throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");
281
+ }
282
+ if (typeof EncodedVideoChunk === "undefined") {
283
+ throw new Error("Your browser does not support EncodedVideoChunk.");
284
+ }
285
+ return new EncodedVideoChunk({
286
+ data: this.sideData.alpha,
287
+ type,
288
+ timestamp: this.microsecondTimestamp,
289
+ duration: this.microsecondDuration
290
+ });
291
+ }
292
+ toEncodedAudioChunk() {
293
+ if (this.isMetadataOnly) {
294
+ throw new TypeError("Metadata-only packets cannot be converted to an audio chunk.");
295
+ }
296
+ if (typeof EncodedAudioChunk === "undefined") {
297
+ throw new Error("Your browser does not support EncodedAudioChunk.");
298
+ }
299
+ return new EncodedAudioChunk({
300
+ data: this.data,
301
+ type: this.type,
302
+ timestamp: this.microsecondTimestamp,
303
+ duration: this.microsecondDuration
304
+ });
305
+ }
306
+ static fromEncodedChunk(chunk, sideData) {
307
+ if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) {
308
+ throw new TypeError("chunk must be an EncodedVideoChunk or EncodedAudioChunk.");
309
+ }
310
+ const data = new Uint8Array(chunk.byteLength);
311
+ chunk.copyTo(data);
312
+ return new EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, undefined, undefined, sideData);
313
+ }
314
+ clone(options) {
315
+ if (options !== undefined && (typeof options !== "object" || options === null)) {
316
+ throw new TypeError("options, when provided, must be an object.");
317
+ }
318
+ if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) {
319
+ throw new TypeError("options.timestamp, when provided, must be a number.");
320
+ }
321
+ if (options?.duration !== undefined && !Number.isFinite(options.duration)) {
322
+ throw new TypeError("options.duration, when provided, must be a number.");
323
+ }
324
+ return new EncodedPacket(this.data, this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, this.sequenceNumber, this.byteLength);
325
+ }
326
+ }
327
+
328
+ // src/sample.ts
329
+ /*!
330
+ * Copyright (c) 2025-present, Vanilagy and contributors
331
+ *
332
+ * This Source Code Form is subject to the terms of the Mozilla Public
333
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
334
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
335
+ */
336
+ polyfillSymbolDispose();
337
+ var lastVideoGcErrorLog = -Infinity;
338
+ var lastAudioGcErrorLog = -Infinity;
339
+ var finalizationRegistry = null;
340
+ if (typeof FinalizationRegistry !== "undefined") {
341
+ finalizationRegistry = new FinalizationRegistry((value) => {
342
+ const now = Date.now();
343
+ if (value.type === "video") {
344
+ if (now - lastVideoGcErrorLog >= 1000) {
345
+ 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.`);
346
+ lastVideoGcErrorLog = now;
347
+ }
348
+ if (typeof VideoFrame !== "undefined" && value.data instanceof VideoFrame) {
349
+ value.data.close();
350
+ }
351
+ } else {
352
+ if (now - lastAudioGcErrorLog >= 1000) {
353
+ 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.`);
354
+ lastAudioGcErrorLog = now;
355
+ }
356
+ if (typeof AudioData !== "undefined" && value.data instanceof AudioData) {
357
+ value.data.close();
358
+ }
359
+ }
360
+ });
361
+ }
362
+ var VIDEO_SAMPLE_PIXEL_FORMATS = [
363
+ "I420",
364
+ "I420P10",
365
+ "I420P12",
366
+ "I420A",
367
+ "I420AP10",
368
+ "I420AP12",
369
+ "I422",
370
+ "I422P10",
371
+ "I422P12",
372
+ "I422A",
373
+ "I422AP10",
374
+ "I422AP12",
375
+ "I444",
376
+ "I444P10",
377
+ "I444P12",
378
+ "I444A",
379
+ "I444AP10",
380
+ "I444AP12",
381
+ "NV12",
382
+ "RGBA",
383
+ "RGBX",
384
+ "BGRA",
385
+ "BGRX"
386
+ ];
387
+ var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS);
388
+
389
+ class VideoSample {
390
+ _data;
391
+ _layout;
392
+ _closed = false;
393
+ format;
394
+ codedWidth;
395
+ codedHeight;
396
+ rotation;
397
+ timestamp;
398
+ duration;
399
+ colorSpace;
400
+ get displayWidth() {
401
+ return this.rotation % 180 === 0 ? this.codedWidth : this.codedHeight;
402
+ }
403
+ get displayHeight() {
404
+ return this.rotation % 180 === 0 ? this.codedHeight : this.codedWidth;
405
+ }
406
+ get microsecondTimestamp() {
407
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);
408
+ }
409
+ get microsecondDuration() {
410
+ return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);
411
+ }
412
+ get hasAlpha() {
413
+ return this.format && this.format.includes("A");
414
+ }
415
+ constructor(data, init) {
416
+ if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) {
417
+ if (!init || typeof init !== "object") {
418
+ throw new TypeError("init must be an object.");
419
+ }
420
+ if (init.format === undefined || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) {
421
+ throw new TypeError("init.format must be one of: " + VIDEO_SAMPLE_PIXEL_FORMATS.join(", "));
422
+ }
423
+ if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) {
424
+ throw new TypeError("init.codedWidth must be a positive integer.");
425
+ }
426
+ if (!Number.isInteger(init.codedHeight) || init.codedHeight <= 0) {
427
+ throw new TypeError("init.codedHeight must be a positive integer.");
428
+ }
429
+ if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {
430
+ throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");
431
+ }
432
+ if (!Number.isFinite(init.timestamp)) {
433
+ throw new TypeError("init.timestamp must be a number.");
434
+ }
435
+ if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {
436
+ throw new TypeError("init.duration, when provided, must be a non-negative number.");
437
+ }
438
+ this._data = toUint8Array(data).slice();
439
+ this._layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);
440
+ this.format = init.format;
441
+ this.codedWidth = init.codedWidth;
442
+ this.codedHeight = init.codedHeight;
443
+ this.rotation = init.rotation ?? 0;
444
+ this.timestamp = init.timestamp;
445
+ this.duration = init.duration ?? 0;
446
+ this.colorSpace = new VideoSampleColorSpace(init.colorSpace);
447
+ } else if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) {
448
+ if (init?.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {
449
+ throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");
450
+ }
451
+ if (init?.timestamp !== undefined && !Number.isFinite(init?.timestamp)) {
452
+ throw new TypeError("init.timestamp, when provided, must be a number.");
453
+ }
454
+ if (init?.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {
455
+ throw new TypeError("init.duration, when provided, must be a non-negative number.");
456
+ }
457
+ this._data = data;
458
+ this._layout = null;
459
+ this.format = data.format;
460
+ this.codedWidth = data.displayWidth;
461
+ this.codedHeight = data.displayHeight;
462
+ this.rotation = init?.rotation ?? 0;
463
+ this.timestamp = init?.timestamp ?? data.timestamp / 1e6;
464
+ this.duration = init?.duration ?? (data.duration ?? 0) / 1e6;
465
+ this.colorSpace = new VideoSampleColorSpace(data.colorSpace);
466
+ } else if (typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement || typeof SVGImageElement !== "undefined" && data instanceof SVGImageElement || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas) {
467
+ if (!init || typeof init !== "object") {
468
+ throw new TypeError("init must be an object.");
469
+ }
470
+ if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {
471
+ throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");
472
+ }
473
+ if (!Number.isFinite(init.timestamp)) {
474
+ throw new TypeError("init.timestamp must be a number.");
475
+ }
476
+ if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {
477
+ throw new TypeError("init.duration, when provided, must be a non-negative number.");
478
+ }
479
+ if (typeof VideoFrame !== "undefined") {
480
+ return new VideoSample(new VideoFrame(data, {
481
+ timestamp: Math.trunc(init.timestamp * SECOND_TO_MICROSECOND_FACTOR),
482
+ duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || undefined
483
+ }), init);
484
+ }
485
+ let width = 0;
486
+ let height = 0;
487
+ if ("naturalWidth" in data) {
488
+ width = data.naturalWidth;
489
+ height = data.naturalHeight;
490
+ } else if ("videoWidth" in data) {
491
+ width = data.videoWidth;
492
+ height = data.videoHeight;
493
+ } else if ("width" in data) {
494
+ width = Number(data.width);
495
+ height = Number(data.height);
496
+ }
497
+ if (!width || !height) {
498
+ throw new TypeError("Could not determine dimensions.");
499
+ }
500
+ const canvas = new OffscreenCanvas(width, height);
501
+ const context = canvas.getContext("2d", {
502
+ alpha: isFirefox(),
503
+ willReadFrequently: true
504
+ });
505
+ assert(context);
506
+ context.drawImage(data, 0, 0);
507
+ this._data = canvas;
508
+ this._layout = null;
509
+ this.format = "RGBX";
510
+ this.codedWidth = width;
511
+ this.codedHeight = height;
512
+ this.rotation = init.rotation ?? 0;
513
+ this.timestamp = init.timestamp;
514
+ this.duration = init.duration ?? 0;
515
+ this.colorSpace = new VideoSampleColorSpace({
516
+ matrix: "rgb",
517
+ primaries: "bt709",
518
+ transfer: "iec61966-2-1",
519
+ fullRange: true
520
+ });
521
+ } else {
522
+ throw new TypeError("Invalid data type: Must be a BufferSource or CanvasImageSource.");
523
+ }
524
+ finalizationRegistry?.register(this, { type: "video", data: this._data }, this);
525
+ }
526
+ clone() {
527
+ if (this._closed) {
528
+ throw new Error("VideoSample is closed.");
529
+ }
530
+ assert(this._data !== null);
531
+ if (isVideoFrame(this._data)) {
532
+ return new VideoSample(this._data.clone(), {
533
+ timestamp: this.timestamp,
534
+ duration: this.duration,
535
+ rotation: this.rotation
536
+ });
537
+ }
538
+ if (this._data instanceof Uint8Array) {
539
+ assert(this._layout);
540
+ return new VideoSample(this._data, {
541
+ format: this.format,
542
+ layout: this._layout,
543
+ codedWidth: this.codedWidth,
544
+ codedHeight: this.codedHeight,
545
+ timestamp: this.timestamp,
546
+ duration: this.duration,
547
+ colorSpace: this.colorSpace,
548
+ rotation: this.rotation
549
+ });
550
+ }
551
+ return new VideoSample(this._data, {
552
+ format: this.format,
553
+ codedWidth: this.codedWidth,
554
+ codedHeight: this.codedHeight,
555
+ timestamp: this.timestamp,
556
+ duration: this.duration,
557
+ colorSpace: this.colorSpace,
558
+ rotation: this.rotation
559
+ });
560
+ }
561
+ close() {
562
+ if (this._closed) {
563
+ return;
564
+ }
565
+ finalizationRegistry?.unregister(this);
566
+ if (isVideoFrame(this._data)) {
567
+ this._data.close();
568
+ } else {
569
+ this._data = null;
570
+ }
571
+ this._closed = true;
572
+ }
573
+ allocationSize(options = {}) {
574
+ validateVideoFrameCopyToOptions(options);
575
+ if (this._closed) {
576
+ throw new Error("VideoSample is closed.");
577
+ }
578
+ if ((options.format ?? this.format) === null) {
579
+ throw new Error("Cannot get allocation size when format is null. Please manually provide an RGB pixel format in the" + " options instead.");
580
+ }
581
+ assert(this._data !== null);
582
+ if (!isVideoFrame(this._data)) {
583
+ if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) {
584
+ const videoFrame = this.toVideoFrame();
585
+ const size = videoFrame.allocationSize(options);
586
+ videoFrame.close();
587
+ return size;
588
+ }
589
+ }
590
+ if (isVideoFrame(this._data)) {
591
+ return this._data.allocationSize(options);
592
+ }
593
+ if (this._data instanceof Uint8Array) {
594
+ return this._data.byteLength;
595
+ }
596
+ return this.codedWidth * this.codedHeight * 4;
597
+ }
598
+ async copyTo(destination, options = {}) {
599
+ if (!isAllowSharedBufferSource(destination)) {
600
+ throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");
601
+ }
602
+ if (this._closed) {
603
+ throw new Error("VideoSample is closed.");
604
+ }
605
+ if ((options.format ?? this.format) === null) {
606
+ throw new Error("Cannot copy video sample data when format is null. Please manually provide an RGB pixel format in the" + " options instead.");
607
+ }
608
+ assert(this._data !== null);
609
+ if (!isVideoFrame(this._data)) {
610
+ if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) {
611
+ const videoFrame = this.toVideoFrame();
612
+ const layout = await videoFrame.copyTo(destination, options);
613
+ videoFrame.close();
614
+ return layout;
615
+ }
616
+ }
617
+ if (isVideoFrame(this._data)) {
618
+ return this._data.copyTo(destination, options);
619
+ } else if (this._data instanceof Uint8Array) {
620
+ assert(this._layout);
621
+ const dest = toUint8Array(destination);
622
+ dest.set(this._data);
623
+ return this._layout;
624
+ } else {
625
+ const canvas = this._data;
626
+ const context = canvas.getContext("2d");
627
+ assert(context);
628
+ const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight);
629
+ const dest = toUint8Array(destination);
630
+ dest.set(imageData.data);
631
+ return [
632
+ {
633
+ offset: 0,
634
+ stride: 4 * this.codedWidth
635
+ }
636
+ ];
637
+ }
638
+ }
639
+ toVideoFrame() {
640
+ if (this._closed) {
641
+ throw new Error("VideoSample is closed.");
642
+ }
643
+ assert(this._data !== null);
644
+ if (isVideoFrame(this._data)) {
645
+ return new VideoFrame(this._data, {
646
+ timestamp: this.microsecondTimestamp,
647
+ duration: this.microsecondDuration || undefined
648
+ });
649
+ }
650
+ if (this._data instanceof Uint8Array) {
651
+ return new VideoFrame(this._data, {
652
+ format: this.format,
653
+ codedWidth: this.codedWidth,
654
+ codedHeight: this.codedHeight,
655
+ timestamp: this.microsecondTimestamp,
656
+ duration: this.microsecondDuration || undefined,
657
+ colorSpace: this.colorSpace
658
+ });
659
+ }
660
+ return new VideoFrame(this._data, {
661
+ timestamp: this.microsecondTimestamp,
662
+ duration: this.microsecondDuration || undefined
663
+ });
664
+ }
665
+ draw(context, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
666
+ let sx = 0;
667
+ let sy = 0;
668
+ let sWidth = this.displayWidth;
669
+ let sHeight = this.displayHeight;
670
+ let dx = 0;
671
+ let dy = 0;
672
+ let dWidth = this.displayWidth;
673
+ let dHeight = this.displayHeight;
674
+ if (arg5 !== undefined) {
675
+ sx = arg1;
676
+ sy = arg2;
677
+ sWidth = arg3;
678
+ sHeight = arg4;
679
+ dx = arg5;
680
+ dy = arg6;
681
+ if (arg7 !== undefined) {
682
+ dWidth = arg7;
683
+ dHeight = arg8;
684
+ } else {
685
+ dWidth = sWidth;
686
+ dHeight = sHeight;
687
+ }
688
+ } else {
689
+ dx = arg1;
690
+ dy = arg2;
691
+ if (arg3 !== undefined) {
692
+ dWidth = arg3;
693
+ dHeight = arg4;
694
+ }
695
+ }
696
+ if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) {
697
+ throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.");
698
+ }
699
+ if (!Number.isFinite(sx)) {
700
+ throw new TypeError("sx must be a number.");
701
+ }
702
+ if (!Number.isFinite(sy)) {
703
+ throw new TypeError("sy must be a number.");
704
+ }
705
+ if (!Number.isFinite(sWidth) || sWidth < 0) {
706
+ throw new TypeError("sWidth must be a non-negative number.");
707
+ }
708
+ if (!Number.isFinite(sHeight) || sHeight < 0) {
709
+ throw new TypeError("sHeight must be a non-negative number.");
710
+ }
711
+ if (!Number.isFinite(dx)) {
712
+ throw new TypeError("dx must be a number.");
713
+ }
714
+ if (!Number.isFinite(dy)) {
715
+ throw new TypeError("dy must be a number.");
716
+ }
717
+ if (!Number.isFinite(dWidth) || dWidth < 0) {
718
+ throw new TypeError("dWidth must be a non-negative number.");
719
+ }
720
+ if (!Number.isFinite(dHeight) || dHeight < 0) {
721
+ throw new TypeError("dHeight must be a non-negative number.");
722
+ }
723
+ if (this._closed) {
724
+ throw new Error("VideoSample is closed.");
725
+ }
726
+ ({ sx, sy, sWidth, sHeight } = this._rotateSourceRegion(sx, sy, sWidth, sHeight, this.rotation));
727
+ const source = this.toCanvasImageSource();
728
+ context.save();
729
+ const centerX = dx + dWidth / 2;
730
+ const centerY = dy + dHeight / 2;
731
+ context.translate(centerX, centerY);
732
+ context.rotate(this.rotation * Math.PI / 180);
733
+ const aspectRatioChange = this.rotation % 180 === 0 ? 1 : dWidth / dHeight;
734
+ context.scale(1 / aspectRatioChange, aspectRatioChange);
735
+ context.drawImage(source, sx, sy, sWidth, sHeight, -dWidth / 2, -dHeight / 2, dWidth, dHeight);
736
+ context.restore();
737
+ }
738
+ drawWithFit(context, options) {
739
+ if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) {
740
+ throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.");
741
+ }
742
+ if (!options || typeof options !== "object") {
743
+ throw new TypeError("options must be an object.");
744
+ }
745
+ if (!["fill", "contain", "cover"].includes(options.fit)) {
746
+ throw new TypeError("options.fit must be 'fill', 'contain', or 'cover'.");
747
+ }
748
+ if (options.rotation !== undefined && ![0, 90, 180, 270].includes(options.rotation)) {
749
+ throw new TypeError("options.rotation, when provided, must be 0, 90, 180, or 270.");
750
+ }
751
+ if (options.crop !== undefined) {
752
+ validateCropRectangle(options.crop, "options.");
753
+ }
754
+ const canvasWidth = context.canvas.width;
755
+ const canvasHeight = context.canvas.height;
756
+ const rotation = options.rotation ?? this.rotation;
757
+ const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.codedWidth, this.codedHeight] : [this.codedHeight, this.codedWidth];
758
+ if (options.crop) {
759
+ clampCropRectangle(options.crop, rotatedWidth, rotatedHeight);
760
+ }
761
+ let dx;
762
+ let dy;
763
+ let newWidth;
764
+ let newHeight;
765
+ const { sx, sy, sWidth, sHeight } = this._rotateSourceRegion(options.crop?.left ?? 0, options.crop?.top ?? 0, options.crop?.width ?? rotatedWidth, options.crop?.height ?? rotatedHeight, rotation);
766
+ if (options.fit === "fill") {
767
+ dx = 0;
768
+ dy = 0;
769
+ newWidth = canvasWidth;
770
+ newHeight = canvasHeight;
771
+ } else {
772
+ const [sampleWidth, sampleHeight] = options.crop ? [options.crop.width, options.crop.height] : [rotatedWidth, rotatedHeight];
773
+ const scale = options.fit === "contain" ? Math.min(canvasWidth / sampleWidth, canvasHeight / sampleHeight) : Math.max(canvasWidth / sampleWidth, canvasHeight / sampleHeight);
774
+ newWidth = sampleWidth * scale;
775
+ newHeight = sampleHeight * scale;
776
+ dx = (canvasWidth - newWidth) / 2;
777
+ dy = (canvasHeight - newHeight) / 2;
778
+ }
779
+ context.save();
780
+ const aspectRatioChange = rotation % 180 === 0 ? 1 : newWidth / newHeight;
781
+ context.translate(canvasWidth / 2, canvasHeight / 2);
782
+ context.rotate(rotation * Math.PI / 180);
783
+ context.scale(1 / aspectRatioChange, aspectRatioChange);
784
+ context.translate(-canvasWidth / 2, -canvasHeight / 2);
785
+ context.drawImage(this.toCanvasImageSource(), sx, sy, sWidth, sHeight, dx, dy, newWidth, newHeight);
786
+ context.restore();
787
+ }
788
+ _rotateSourceRegion(sx, sy, sWidth, sHeight, rotation) {
789
+ if (rotation === 90) {
790
+ [sx, sy, sWidth, sHeight] = [sy, this.codedHeight - sx - sWidth, sHeight, sWidth];
791
+ } else if (rotation === 180) {
792
+ [sx, sy] = [this.codedWidth - sx - sWidth, this.codedHeight - sy - sHeight];
793
+ } else if (rotation === 270) {
794
+ [sx, sy, sWidth, sHeight] = [this.codedWidth - sy - sHeight, sx, sHeight, sWidth];
795
+ }
796
+ return { sx, sy, sWidth, sHeight };
797
+ }
798
+ toCanvasImageSource() {
799
+ if (this._closed) {
800
+ throw new Error("VideoSample is closed.");
801
+ }
802
+ assert(this._data !== null);
803
+ if (this._data instanceof Uint8Array) {
804
+ const videoFrame = this.toVideoFrame();
805
+ queueMicrotask(() => videoFrame.close());
806
+ return videoFrame;
807
+ }
808
+ return this._data;
809
+ }
810
+ setRotation(newRotation) {
811
+ if (![0, 90, 180, 270].includes(newRotation)) {
812
+ throw new TypeError("newRotation must be 0, 90, 180, or 270.");
813
+ }
814
+ this.rotation = newRotation;
815
+ }
816
+ setTimestamp(newTimestamp) {
817
+ if (!Number.isFinite(newTimestamp)) {
818
+ throw new TypeError("newTimestamp must be a number.");
819
+ }
820
+ this.timestamp = newTimestamp;
821
+ }
822
+ setDuration(newDuration) {
823
+ if (!Number.isFinite(newDuration) || newDuration < 0) {
824
+ throw new TypeError("newDuration must be a non-negative number.");
825
+ }
826
+ this.duration = newDuration;
827
+ }
828
+ [Symbol.dispose]() {
829
+ this.close();
830
+ }
831
+ }
832
+
833
+ class VideoSampleColorSpace {
834
+ primaries;
835
+ transfer;
836
+ matrix;
837
+ fullRange;
838
+ constructor(init) {
839
+ this.primaries = init?.primaries ?? null;
840
+ this.transfer = init?.transfer ?? null;
841
+ this.matrix = init?.matrix ?? null;
842
+ this.fullRange = init?.fullRange ?? null;
843
+ }
844
+ toJSON() {
845
+ return {
846
+ primaries: this.primaries,
847
+ transfer: this.transfer,
848
+ matrix: this.matrix,
849
+ fullRange: this.fullRange
850
+ };
851
+ }
852
+ }
853
+ var isVideoFrame = (x) => {
854
+ return typeof VideoFrame !== "undefined" && x instanceof VideoFrame;
855
+ };
856
+ var clampCropRectangle = (crop, outerWidth, outerHeight) => {
857
+ crop.left = Math.min(crop.left, outerWidth);
858
+ crop.top = Math.min(crop.top, outerHeight);
859
+ crop.width = Math.min(crop.width, outerWidth - crop.left);
860
+ crop.height = Math.min(crop.height, outerHeight - crop.top);
861
+ assert(crop.width >= 0);
862
+ assert(crop.height >= 0);
863
+ };
864
+ var validateCropRectangle = (crop, prefix) => {
865
+ if (!crop || typeof crop !== "object") {
866
+ throw new TypeError(prefix + "crop, when provided, must be an object.");
867
+ }
868
+ if (!Number.isInteger(crop.left) || crop.left < 0) {
869
+ throw new TypeError(prefix + "crop.left must be a non-negative integer.");
870
+ }
871
+ if (!Number.isInteger(crop.top) || crop.top < 0) {
872
+ throw new TypeError(prefix + "crop.top must be a non-negative integer.");
873
+ }
874
+ if (!Number.isInteger(crop.width) || crop.width < 0) {
875
+ throw new TypeError(prefix + "crop.width must be a non-negative integer.");
876
+ }
877
+ if (!Number.isInteger(crop.height) || crop.height < 0) {
878
+ throw new TypeError(prefix + "crop.height must be a non-negative integer.");
879
+ }
880
+ };
881
+ var validateVideoFrameCopyToOptions = (options) => {
882
+ if (!options || typeof options !== "object") {
883
+ throw new TypeError("options must be an object.");
884
+ }
885
+ if (options.colorSpace !== undefined && !["display-p3", "srgb"].includes(options.colorSpace)) {
886
+ throw new TypeError("options.colorSpace, when provided, must be 'display-p3' or 'srgb'.");
887
+ }
888
+ if (options.format !== undefined && typeof options.format !== "string") {
889
+ throw new TypeError("options.format, when provided, must be a string.");
890
+ }
891
+ if (options.layout !== undefined) {
892
+ if (!Array.isArray(options.layout)) {
893
+ throw new TypeError("options.layout, when provided, must be an array.");
894
+ }
895
+ for (const plane of options.layout) {
896
+ if (!plane || typeof plane !== "object") {
897
+ throw new TypeError("Each entry in options.layout must be an object.");
898
+ }
899
+ if (!Number.isInteger(plane.offset) || plane.offset < 0) {
900
+ throw new TypeError("plane.offset must be a non-negative integer.");
901
+ }
902
+ if (!Number.isInteger(plane.stride) || plane.stride < 0) {
903
+ throw new TypeError("plane.stride must be a non-negative integer.");
904
+ }
905
+ }
906
+ }
907
+ if (options.rect !== undefined) {
908
+ if (!options.rect || typeof options.rect !== "object") {
909
+ throw new TypeError("options.rect, when provided, must be an object.");
910
+ }
911
+ if (options.rect.x !== undefined && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) {
912
+ throw new TypeError("options.rect.x, when provided, must be a non-negative integer.");
913
+ }
914
+ if (options.rect.y !== undefined && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) {
915
+ throw new TypeError("options.rect.y, when provided, must be a non-negative integer.");
916
+ }
917
+ if (options.rect.width !== undefined && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) {
918
+ throw new TypeError("options.rect.width, when provided, must be a non-negative integer.");
919
+ }
920
+ if (options.rect.height !== undefined && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) {
921
+ throw new TypeError("options.rect.height, when provided, must be a non-negative integer.");
922
+ }
923
+ }
924
+ };
925
+ var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => {
926
+ const planes = getPlaneConfigs(format);
927
+ const layouts = [];
928
+ let currentOffset = 0;
929
+ for (const plane of planes) {
930
+ const planeWidth = Math.ceil(codedWidth / plane.widthDivisor);
931
+ const planeHeight = Math.ceil(codedHeight / plane.heightDivisor);
932
+ const stride = planeWidth * plane.sampleBytes;
933
+ const planeSize = stride * planeHeight;
934
+ layouts.push({
935
+ offset: currentOffset,
936
+ stride
937
+ });
938
+ currentOffset += planeSize;
939
+ }
940
+ return layouts;
941
+ };
942
+ var getPlaneConfigs = (format) => {
943
+ const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => {
944
+ const configs = [
945
+ { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 },
946
+ { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY },
947
+ { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }
948
+ ];
949
+ if (hasAlpha) {
950
+ configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 });
951
+ }
952
+ return configs;
953
+ };
954
+ switch (format) {
955
+ case "I420":
956
+ return yuv(1, 1, 2, 2, false);
957
+ case "I420P10":
958
+ case "I420P12":
959
+ return yuv(2, 2, 2, 2, false);
960
+ case "I420A":
961
+ return yuv(1, 1, 2, 2, true);
962
+ case "I420AP10":
963
+ case "I420AP12":
964
+ return yuv(2, 2, 2, 2, true);
965
+ case "I422":
966
+ return yuv(1, 1, 2, 1, false);
967
+ case "I422P10":
968
+ case "I422P12":
969
+ return yuv(2, 2, 2, 1, false);
970
+ case "I422A":
971
+ return yuv(1, 1, 2, 1, true);
972
+ case "I422AP10":
973
+ case "I422AP12":
974
+ return yuv(2, 2, 2, 1, true);
975
+ case "I444":
976
+ return yuv(1, 1, 1, 1, false);
977
+ case "I444P10":
978
+ case "I444P12":
979
+ return yuv(2, 2, 1, 1, false);
980
+ case "I444A":
981
+ return yuv(1, 1, 1, 1, true);
982
+ case "I444AP10":
983
+ case "I444AP12":
984
+ return yuv(2, 2, 1, 1, true);
985
+ case "NV12":
986
+ return [
987
+ { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 },
988
+ { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 }
989
+ ];
990
+ case "RGBA":
991
+ case "RGBX":
992
+ case "BGRA":
993
+ case "BGRX":
994
+ return [{ sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 }];
995
+ default:
996
+ assertNever(format);
997
+ assert(false);
998
+ }
999
+ };
1000
+ var AUDIO_SAMPLE_FORMATS = new Set([
1001
+ "f32",
1002
+ "f32-planar",
1003
+ "s16",
1004
+ "s16-planar",
1005
+ "s32",
1006
+ "s32-planar",
1007
+ "u8",
1008
+ "u8-planar"
1009
+ ]);
1010
+ // src/index.ts
1011
+ /*!
1012
+ * Copyright (c) 2025-present, Vanilagy and contributors
1013
+ *
1014
+ * This Source Code Form is subject to the terms of the Mozilla Public
1015
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
1016
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
1017
+ */
1018
+
1019
+ // packages/mpeg4/src/index.ts
1020
+ /*!
1021
+ * Copyright (c) 2025-present, Vanilagy and contributors (Wiedy Mi)
1022
+ *
1023
+ * This Source Code Form is subject to the terms of the Mozilla Public
1024
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
1025
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
1026
+ */
1027
+ var decodeWorkerUrl = new URL("./decode.worker.ts", import.meta.url);
1028
+ var encodeWorkerUrl = new URL("./encode.worker.ts", import.meta.url);
1029
+ var createWorker = (url) => {
1030
+ return new Worker(url, { type: "module" });
1031
+ };
1032
+
1033
+ class Mpeg4Decoder extends CustomVideoDecoder {
1034
+ worker = null;
1035
+ nextMessageId = 0;
1036
+ pendingMessages = new Map;
1037
+ static supports(codec, _config) {
1038
+ return codec === "mpeg4";
1039
+ }
1040
+ async init() {
1041
+ this.worker = createWorker(decodeWorkerUrl);
1042
+ const onMessage = (event) => {
1043
+ const data = event.data;
1044
+ const pending = this.pendingMessages.get(data.id);
1045
+ assert2(pending !== undefined);
1046
+ this.pendingMessages.delete(data.id);
1047
+ if (data.success) {
1048
+ pending.resolve(data.data);
1049
+ } else {
1050
+ pending.reject(data.error);
1051
+ }
1052
+ };
1053
+ this.worker.addEventListener("message", onMessage);
1054
+ await this.sendCommand({
1055
+ type: "init",
1056
+ data: {
1057
+ width: this.config.codedWidth,
1058
+ height: this.config.codedHeight
1059
+ }
1060
+ });
1061
+ }
1062
+ async decode(packet, _meta) {
1063
+ const frameData = packet.data.slice().buffer;
1064
+ const result = await this.sendCommand({
1065
+ type: "decode",
1066
+ data: {
1067
+ frameData
1068
+ }
1069
+ }, [frameData]);
1070
+ if (!result || !("yuvData" in result)) {
1071
+ return;
1072
+ }
1073
+ const videoFrame = new VideoFrame(new Uint8Array(result.yuvData), {
1074
+ format: "I420",
1075
+ codedWidth: result.width,
1076
+ codedHeight: result.height,
1077
+ timestamp: packet.timestamp * 1e6
1078
+ });
1079
+ const videoSample = new VideoSample(videoFrame);
1080
+ this.onSample(videoSample);
1081
+ }
1082
+ async flush() {
1083
+ await this.sendCommand({ type: "flush" });
1084
+ }
1085
+ close() {
1086
+ if (this.worker) {
1087
+ this.sendCommand({ type: "close" });
1088
+ this.worker.terminate();
1089
+ }
1090
+ }
1091
+ sendCommand(command, transferables) {
1092
+ return new Promise((resolve, reject) => {
1093
+ const id = this.nextMessageId++;
1094
+ this.pendingMessages.set(id, { resolve, reject });
1095
+ assert2(this.worker !== null);
1096
+ if (transferables) {
1097
+ this.worker.postMessage({ id, command }, transferables);
1098
+ } else {
1099
+ this.worker.postMessage({ id, command });
1100
+ }
1101
+ });
1102
+ }
1103
+ }
1104
+
1105
+ class Mpeg4Encoder extends CustomVideoEncoder {
1106
+ worker = null;
1107
+ nextMessageId = 0;
1108
+ pendingMessages = new Map;
1109
+ frameCount = 0;
1110
+ static supports(codec, _config) {
1111
+ return codec === "mpeg4";
1112
+ }
1113
+ async init() {
1114
+ this.worker = createWorker(encodeWorkerUrl);
1115
+ const onMessage = (event) => {
1116
+ const data = event.data;
1117
+ const pending = this.pendingMessages.get(data.id);
1118
+ assert2(pending !== undefined);
1119
+ this.pendingMessages.delete(data.id);
1120
+ if (data.success) {
1121
+ pending.resolve(data.data);
1122
+ } else {
1123
+ pending.reject(data.error);
1124
+ }
1125
+ };
1126
+ this.worker.addEventListener("message", onMessage);
1127
+ const fpsNum = Math.round((this.config.framerate ?? 30) * 1000);
1128
+ const fpsDen = 1000;
1129
+ await this.sendCommand({
1130
+ type: "init",
1131
+ data: {
1132
+ width: this.config.width,
1133
+ height: this.config.height,
1134
+ bitrate: this.config.bitrate ?? 2000000,
1135
+ fpsNum,
1136
+ fpsDen
1137
+ }
1138
+ });
1139
+ }
1140
+ async encode(videoSample, options) {
1141
+ const yuvSize = videoSample.codedWidth * videoSample.codedHeight * 3 / 2;
1142
+ const yuvData = new ArrayBuffer(yuvSize);
1143
+ const yuvBytes = new Uint8Array(yuvData);
1144
+ await videoSample.copyTo(yuvBytes);
1145
+ const result = await this.sendCommand({
1146
+ type: "encode",
1147
+ data: {
1148
+ yuvData,
1149
+ forceKeyframe: options.keyFrame ?? false
1150
+ }
1151
+ }, [yuvData]);
1152
+ assert2(result && "encodedData" in result);
1153
+ const encodedPacket = new EncodedPacket(new Uint8Array(result.encodedData), options.keyFrame ? "key" : "delta", videoSample.timestamp, videoSample.duration, this.frameCount++);
1154
+ this.onPacket(encodedPacket, this.frameCount === 1 ? {
1155
+ decoderConfig: {
1156
+ codec: "mpeg4",
1157
+ codedWidth: this.config.width,
1158
+ codedHeight: this.config.height
1159
+ }
1160
+ } : undefined);
1161
+ }
1162
+ async flush() {}
1163
+ close() {
1164
+ if (this.worker) {
1165
+ this.sendCommand({ type: "close" });
1166
+ this.worker.terminate();
1167
+ }
1168
+ }
1169
+ sendCommand(command, transferables) {
1170
+ return new Promise((resolve, reject) => {
1171
+ const id = this.nextMessageId++;
1172
+ this.pendingMessages.set(id, { resolve, reject });
1173
+ assert2(this.worker !== null);
1174
+ if (transferables) {
1175
+ this.worker.postMessage({ id, command }, transferables);
1176
+ } else {
1177
+ this.worker.postMessage({ id, command });
1178
+ }
1179
+ });
1180
+ }
1181
+ }
1182
+ var registerMpeg4Decoder = () => {
1183
+ registerDecoder(Mpeg4Decoder);
1184
+ };
1185
+ var registerMpeg4Encoder = () => {
1186
+ registerEncoder(Mpeg4Encoder);
1187
+ };
1188
+ function assert2(x) {
1189
+ if (!x) {
1190
+ throw new Error("Assertion failed.");
1191
+ }
1192
+ }
1193
+ export {
1194
+ registerMpeg4Encoder,
1195
+ registerMpeg4Decoder,
1196
+ Mpeg4Encoder,
1197
+ Mpeg4Decoder
1198
+ };