@kenzuya/mediabunny 1.26.0 → 1.28.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +1 -1
  2. package/dist/bundles/{mediabunny.mjs → mediabunny.js} +21963 -21390
  3. package/dist/bundles/mediabunny.min.js +490 -0
  4. package/dist/modules/shared/mp3-misc.d.ts.map +1 -1
  5. package/dist/modules/src/adts/adts-demuxer.d.ts +6 -6
  6. package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
  7. package/dist/modules/src/adts/adts-muxer.d.ts +4 -4
  8. package/dist/modules/src/adts/adts-muxer.d.ts.map +1 -1
  9. package/dist/modules/src/adts/adts-reader.d.ts +1 -1
  10. package/dist/modules/src/adts/adts-reader.d.ts.map +1 -1
  11. package/dist/modules/src/avi/avi-demuxer.d.ts +44 -0
  12. package/dist/modules/src/avi/avi-demuxer.d.ts.map +1 -0
  13. package/dist/modules/src/avi/avi-misc.d.ts +88 -0
  14. package/dist/modules/src/avi/avi-misc.d.ts.map +1 -0
  15. package/dist/modules/src/avi/avi-muxer.d.ts +45 -0
  16. package/dist/modules/src/avi/avi-muxer.d.ts.map +1 -0
  17. package/dist/modules/src/avi/riff-writer.d.ts +26 -0
  18. package/dist/modules/src/avi/riff-writer.d.ts.map +1 -0
  19. package/dist/modules/src/codec-data.d.ts +8 -3
  20. package/dist/modules/src/codec-data.d.ts.map +1 -1
  21. package/dist/modules/src/codec.d.ts +10 -10
  22. package/dist/modules/src/codec.d.ts.map +1 -1
  23. package/dist/modules/src/conversion.d.ts +33 -16
  24. package/dist/modules/src/conversion.d.ts.map +1 -1
  25. package/dist/modules/src/custom-coder.d.ts +8 -8
  26. package/dist/modules/src/custom-coder.d.ts.map +1 -1
  27. package/dist/modules/src/demuxer.d.ts +3 -3
  28. package/dist/modules/src/demuxer.d.ts.map +1 -1
  29. package/dist/modules/src/encode.d.ts +8 -8
  30. package/dist/modules/src/encode.d.ts.map +1 -1
  31. package/dist/modules/src/flac/flac-demuxer.d.ts +7 -7
  32. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  33. package/dist/modules/src/flac/flac-misc.d.ts +3 -3
  34. package/dist/modules/src/flac/flac-misc.d.ts.map +1 -1
  35. package/dist/modules/src/flac/flac-muxer.d.ts +5 -5
  36. package/dist/modules/src/flac/flac-muxer.d.ts.map +1 -1
  37. package/dist/modules/src/id3.d.ts +3 -3
  38. package/dist/modules/src/id3.d.ts.map +1 -1
  39. package/dist/modules/src/index.d.ts +20 -20
  40. package/dist/modules/src/index.d.ts.map +1 -1
  41. package/dist/modules/src/input-format.d.ts +22 -0
  42. package/dist/modules/src/input-format.d.ts.map +1 -1
  43. package/dist/modules/src/input-track.d.ts +8 -8
  44. package/dist/modules/src/input-track.d.ts.map +1 -1
  45. package/dist/modules/src/input.d.ts +12 -12
  46. package/dist/modules/src/isobmff/isobmff-boxes.d.ts +2 -2
  47. package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
  48. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts +12 -12
  49. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
  50. package/dist/modules/src/isobmff/isobmff-misc.d.ts.map +1 -1
  51. package/dist/modules/src/isobmff/isobmff-muxer.d.ts +11 -11
  52. package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
  53. package/dist/modules/src/isobmff/isobmff-reader.d.ts +2 -2
  54. package/dist/modules/src/isobmff/isobmff-reader.d.ts.map +1 -1
  55. package/dist/modules/src/matroska/ebml.d.ts +3 -3
  56. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  57. package/dist/modules/src/matroska/matroska-demuxer.d.ts +13 -13
  58. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  59. package/dist/modules/src/matroska/matroska-input.d.ts +33 -0
  60. package/dist/modules/src/matroska/matroska-input.d.ts.map +1 -0
  61. package/dist/modules/src/matroska/matroska-misc.d.ts.map +1 -1
  62. package/dist/modules/src/matroska/matroska-muxer.d.ts +5 -5
  63. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  64. package/dist/modules/src/media-sink.d.ts +5 -5
  65. package/dist/modules/src/media-sink.d.ts.map +1 -1
  66. package/dist/modules/src/media-source.d.ts +22 -4
  67. package/dist/modules/src/media-source.d.ts.map +1 -1
  68. package/dist/modules/src/metadata.d.ts +2 -2
  69. package/dist/modules/src/metadata.d.ts.map +1 -1
  70. package/dist/modules/src/misc.d.ts +5 -4
  71. package/dist/modules/src/misc.d.ts.map +1 -1
  72. package/dist/modules/src/mp3/mp3-demuxer.d.ts +7 -7
  73. package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
  74. package/dist/modules/src/mp3/mp3-muxer.d.ts +4 -4
  75. package/dist/modules/src/mp3/mp3-muxer.d.ts.map +1 -1
  76. package/dist/modules/src/mp3/mp3-reader.d.ts +2 -2
  77. package/dist/modules/src/mp3/mp3-reader.d.ts.map +1 -1
  78. package/dist/modules/src/mp3/mp3-writer.d.ts +1 -1
  79. package/dist/modules/src/mp3/mp3-writer.d.ts.map +1 -1
  80. package/dist/modules/src/muxer.d.ts +4 -4
  81. package/dist/modules/src/muxer.d.ts.map +1 -1
  82. package/dist/modules/src/ogg/ogg-demuxer.d.ts +7 -7
  83. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  84. package/dist/modules/src/ogg/ogg-misc.d.ts +1 -1
  85. package/dist/modules/src/ogg/ogg-misc.d.ts.map +1 -1
  86. package/dist/modules/src/ogg/ogg-muxer.d.ts +5 -5
  87. package/dist/modules/src/ogg/ogg-muxer.d.ts.map +1 -1
  88. package/dist/modules/src/ogg/ogg-reader.d.ts +1 -1
  89. package/dist/modules/src/ogg/ogg-reader.d.ts.map +1 -1
  90. package/dist/modules/src/output-format.d.ts +51 -6
  91. package/dist/modules/src/output-format.d.ts.map +1 -1
  92. package/dist/modules/src/output.d.ts +13 -13
  93. package/dist/modules/src/output.d.ts.map +1 -1
  94. package/dist/modules/src/packet.d.ts +1 -1
  95. package/dist/modules/src/packet.d.ts.map +1 -1
  96. package/dist/modules/src/pcm.d.ts.map +1 -1
  97. package/dist/modules/src/reader.d.ts +2 -2
  98. package/dist/modules/src/reader.d.ts.map +1 -1
  99. package/dist/modules/src/sample.d.ts +57 -15
  100. package/dist/modules/src/sample.d.ts.map +1 -1
  101. package/dist/modules/src/source.d.ts +3 -3
  102. package/dist/modules/src/source.d.ts.map +1 -1
  103. package/dist/modules/src/subtitles.d.ts +1 -1
  104. package/dist/modules/src/subtitles.d.ts.map +1 -1
  105. package/dist/modules/src/target.d.ts +2 -2
  106. package/dist/modules/src/target.d.ts.map +1 -1
  107. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  108. package/dist/modules/src/wave/riff-writer.d.ts +1 -1
  109. package/dist/modules/src/wave/riff-writer.d.ts.map +1 -1
  110. package/dist/modules/src/wave/wave-demuxer.d.ts +6 -6
  111. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  112. package/dist/modules/src/wave/wave-muxer.d.ts +4 -4
  113. package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
  114. package/dist/modules/src/writer.d.ts +1 -1
  115. package/dist/modules/src/writer.d.ts.map +1 -1
  116. package/dist/packages/eac3/eac3.wasm +0 -0
  117. package/dist/packages/eac3/mediabunny-eac3.js +1058 -0
  118. package/dist/packages/eac3/mediabunny-eac3.min.js +44 -0
  119. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.js +694 -0
  120. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.min.js +58 -0
  121. package/dist/packages/mpeg4/mediabunny-mpeg4.js +1198 -0
  122. package/dist/packages/mpeg4/mediabunny-mpeg4.min.js +44 -0
  123. package/dist/packages/mpeg4/xvid.wasm +0 -0
  124. package/package.json +18 -57
  125. package/dist/bundles/mediabunny.cjs +0 -26140
  126. package/dist/bundles/mediabunny.min.cjs +0 -147
  127. package/dist/bundles/mediabunny.min.mjs +0 -146
  128. package/dist/mediabunny.d.ts +0 -3319
  129. package/dist/modules/shared/mp3-misc.js +0 -147
  130. package/dist/modules/src/adts/adts-demuxer.js +0 -239
  131. package/dist/modules/src/adts/adts-muxer.js +0 -80
  132. package/dist/modules/src/adts/adts-reader.js +0 -63
  133. package/dist/modules/src/codec-data.js +0 -1730
  134. package/dist/modules/src/codec.js +0 -869
  135. package/dist/modules/src/conversion.js +0 -1459
  136. package/dist/modules/src/custom-coder.js +0 -117
  137. package/dist/modules/src/demuxer.js +0 -12
  138. package/dist/modules/src/encode.js +0 -442
  139. package/dist/modules/src/flac/flac-demuxer.js +0 -504
  140. package/dist/modules/src/flac/flac-misc.js +0 -135
  141. package/dist/modules/src/flac/flac-muxer.js +0 -222
  142. package/dist/modules/src/id3.js +0 -848
  143. package/dist/modules/src/index.js +0 -28
  144. package/dist/modules/src/input-format.js +0 -480
  145. package/dist/modules/src/input-track.js +0 -372
  146. package/dist/modules/src/input.js +0 -188
  147. package/dist/modules/src/isobmff/isobmff-boxes.js +0 -1480
  148. package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -2618
  149. package/dist/modules/src/isobmff/isobmff-misc.js +0 -20
  150. package/dist/modules/src/isobmff/isobmff-muxer.js +0 -966
  151. package/dist/modules/src/isobmff/isobmff-reader.js +0 -72
  152. package/dist/modules/src/matroska/ebml.js +0 -653
  153. package/dist/modules/src/matroska/matroska-demuxer.js +0 -2133
  154. package/dist/modules/src/matroska/matroska-misc.js +0 -20
  155. package/dist/modules/src/matroska/matroska-muxer.js +0 -1017
  156. package/dist/modules/src/media-sink.js +0 -1736
  157. package/dist/modules/src/media-source.js +0 -1825
  158. package/dist/modules/src/metadata.js +0 -193
  159. package/dist/modules/src/misc.js +0 -623
  160. package/dist/modules/src/mp3/mp3-demuxer.js +0 -285
  161. package/dist/modules/src/mp3/mp3-muxer.js +0 -123
  162. package/dist/modules/src/mp3/mp3-reader.js +0 -26
  163. package/dist/modules/src/mp3/mp3-writer.js +0 -78
  164. package/dist/modules/src/muxer.js +0 -50
  165. package/dist/modules/src/node.d.ts +0 -9
  166. package/dist/modules/src/node.d.ts.map +0 -1
  167. package/dist/modules/src/node.js +0 -9
  168. package/dist/modules/src/ogg/ogg-demuxer.js +0 -763
  169. package/dist/modules/src/ogg/ogg-misc.js +0 -78
  170. package/dist/modules/src/ogg/ogg-muxer.js +0 -353
  171. package/dist/modules/src/ogg/ogg-reader.js +0 -65
  172. package/dist/modules/src/output-format.js +0 -527
  173. package/dist/modules/src/output.js +0 -300
  174. package/dist/modules/src/packet.js +0 -182
  175. package/dist/modules/src/pcm.js +0 -85
  176. package/dist/modules/src/reader.js +0 -236
  177. package/dist/modules/src/sample.js +0 -1056
  178. package/dist/modules/src/source.js +0 -1182
  179. package/dist/modules/src/subtitles.js +0 -575
  180. package/dist/modules/src/target.js +0 -140
  181. package/dist/modules/src/wave/riff-writer.js +0 -30
  182. package/dist/modules/src/wave/wave-demuxer.js +0 -447
  183. package/dist/modules/src/wave/wave-muxer.js +0 -318
  184. package/dist/modules/src/writer.js +0 -370
  185. package/src/adts/adts-demuxer.ts +0 -331
  186. package/src/adts/adts-muxer.ts +0 -111
  187. package/src/adts/adts-reader.ts +0 -85
  188. package/src/codec-data.ts +0 -2078
  189. package/src/codec.ts +0 -1092
  190. package/src/conversion.ts +0 -2112
  191. package/src/custom-coder.ts +0 -197
  192. package/src/demuxer.ts +0 -24
  193. package/src/encode.ts +0 -739
  194. package/src/flac/flac-demuxer.ts +0 -730
  195. package/src/flac/flac-misc.ts +0 -164
  196. package/src/flac/flac-muxer.ts +0 -320
  197. package/src/id3.ts +0 -925
  198. package/src/index.ts +0 -221
  199. package/src/input-format.ts +0 -541
  200. package/src/input-track.ts +0 -529
  201. package/src/input.ts +0 -235
  202. package/src/isobmff/isobmff-boxes.ts +0 -1719
  203. package/src/isobmff/isobmff-demuxer.ts +0 -3190
  204. package/src/isobmff/isobmff-misc.ts +0 -29
  205. package/src/isobmff/isobmff-muxer.ts +0 -1348
  206. package/src/isobmff/isobmff-reader.ts +0 -91
  207. package/src/matroska/ebml.ts +0 -730
  208. package/src/matroska/matroska-demuxer.ts +0 -2481
  209. package/src/matroska/matroska-misc.ts +0 -29
  210. package/src/matroska/matroska-muxer.ts +0 -1276
  211. package/src/media-sink.ts +0 -2179
  212. package/src/media-source.ts +0 -2243
  213. package/src/metadata.ts +0 -320
  214. package/src/misc.ts +0 -798
  215. package/src/mp3/mp3-demuxer.ts +0 -383
  216. package/src/mp3/mp3-muxer.ts +0 -166
  217. package/src/mp3/mp3-reader.ts +0 -34
  218. package/src/mp3/mp3-writer.ts +0 -120
  219. package/src/muxer.ts +0 -88
  220. package/src/node.ts +0 -11
  221. package/src/ogg/ogg-demuxer.ts +0 -1053
  222. package/src/ogg/ogg-misc.ts +0 -116
  223. package/src/ogg/ogg-muxer.ts +0 -497
  224. package/src/ogg/ogg-reader.ts +0 -93
  225. package/src/output-format.ts +0 -945
  226. package/src/output.ts +0 -488
  227. package/src/packet.ts +0 -263
  228. package/src/pcm.ts +0 -112
  229. package/src/reader.ts +0 -323
  230. package/src/sample.ts +0 -1461
  231. package/src/source.ts +0 -1688
  232. package/src/subtitles.ts +0 -711
  233. package/src/target.ts +0 -204
  234. package/src/tsconfig.json +0 -16
  235. package/src/wave/riff-writer.ts +0 -36
  236. package/src/wave/wave-demuxer.ts +0 -529
  237. package/src/wave/wave-muxer.ts +0 -371
  238. package/src/writer.ts +0 -490
@@ -1,869 +0,0 @@
1
- /*!
2
- * Copyright (c) 2025-present, Vanilagy and contributors
3
- *
4
- * This Source Code Form is subject to the terms of the Mozilla Public
5
- * License, v. 2.0. If a copy of the MPL was not distributed with this
6
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
- */
8
- import { Bitstream, COLOR_PRIMARIES_MAP, MATRIX_COEFFICIENTS_MAP, TRANSFER_CHARACTERISTICS_MAP, assert, bytesToHexString, isAllowSharedBufferSource, last, reverseBitsU32, toDataView, } from './misc.js';
9
- /**
10
- * List of known video codecs, ordered by encoding preference.
11
- * @group Codecs
12
- * @public
13
- */
14
- export const VIDEO_CODECS = [
15
- 'avc',
16
- 'hevc',
17
- 'vp9',
18
- 'av1',
19
- 'vp8',
20
- ];
21
- /**
22
- * List of known PCM (uncompressed) audio codecs, ordered by encoding preference.
23
- * @group Codecs
24
- * @public
25
- */
26
- export const PCM_AUDIO_CODECS = [
27
- 'pcm-s16', // We don't prefix 'le' so we're compatible with the WebCodecs-registered PCM codec strings
28
- 'pcm-s16be',
29
- 'pcm-s24',
30
- 'pcm-s24be',
31
- 'pcm-s32',
32
- 'pcm-s32be',
33
- 'pcm-f32',
34
- 'pcm-f32be',
35
- 'pcm-f64',
36
- 'pcm-f64be',
37
- 'pcm-u8',
38
- 'pcm-s8',
39
- 'ulaw',
40
- 'alaw',
41
- ];
42
- /**
43
- * List of known compressed audio codecs, ordered by encoding preference.
44
- * @group Codecs
45
- * @public
46
- */
47
- export const NON_PCM_AUDIO_CODECS = [
48
- 'aac',
49
- 'opus',
50
- 'mp3',
51
- 'vorbis',
52
- 'flac',
53
- ];
54
- /**
55
- * List of known audio codecs, ordered by encoding preference.
56
- * @group Codecs
57
- * @public
58
- */
59
- export const AUDIO_CODECS = [
60
- ...NON_PCM_AUDIO_CODECS,
61
- ...PCM_AUDIO_CODECS,
62
- ];
63
- /**
64
- * List of known subtitle codecs, ordered by encoding preference.
65
- * @group Codecs
66
- * @public
67
- */
68
- export const SUBTITLE_CODECS = [
69
- 'webvtt',
70
- 'tx3g',
71
- 'ttml',
72
- 'srt',
73
- 'ass',
74
- 'ssa',
75
- ];
76
- // https://en.wikipedia.org/wiki/Advanced_Video_Coding
77
- const AVC_LEVEL_TABLE = [
78
- { maxMacroblocks: 99, maxBitrate: 64000, level: 0x0A }, // Level 1
79
- { maxMacroblocks: 396, maxBitrate: 192000, level: 0x0B }, // Level 1.1
80
- { maxMacroblocks: 396, maxBitrate: 384000, level: 0x0C }, // Level 1.2
81
- { maxMacroblocks: 396, maxBitrate: 768000, level: 0x0D }, // Level 1.3
82
- { maxMacroblocks: 396, maxBitrate: 2000000, level: 0x14 }, // Level 2
83
- { maxMacroblocks: 792, maxBitrate: 4000000, level: 0x15 }, // Level 2.1
84
- { maxMacroblocks: 1620, maxBitrate: 4000000, level: 0x16 }, // Level 2.2
85
- { maxMacroblocks: 1620, maxBitrate: 10000000, level: 0x1E }, // Level 3
86
- { maxMacroblocks: 3600, maxBitrate: 14000000, level: 0x1F }, // Level 3.1
87
- { maxMacroblocks: 5120, maxBitrate: 20000000, level: 0x20 }, // Level 3.2
88
- { maxMacroblocks: 8192, maxBitrate: 20000000, level: 0x28 }, // Level 4
89
- { maxMacroblocks: 8192, maxBitrate: 50000000, level: 0x29 }, // Level 4.1
90
- { maxMacroblocks: 8704, maxBitrate: 50000000, level: 0x2A }, // Level 4.2
91
- { maxMacroblocks: 22080, maxBitrate: 135000000, level: 0x32 }, // Level 5
92
- { maxMacroblocks: 36864, maxBitrate: 240000000, level: 0x33 }, // Level 5.1
93
- { maxMacroblocks: 36864, maxBitrate: 240000000, level: 0x34 }, // Level 5.2
94
- { maxMacroblocks: 139264, maxBitrate: 240000000, level: 0x3C }, // Level 6
95
- { maxMacroblocks: 139264, maxBitrate: 480000000, level: 0x3D }, // Level 6.1
96
- { maxMacroblocks: 139264, maxBitrate: 800000000, level: 0x3E }, // Level 6.2
97
- ];
98
- // https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding
99
- const HEVC_LEVEL_TABLE = [
100
- { maxPictureSize: 36864, maxBitrate: 128000, tier: 'L', level: 30 }, // Level 1 (Low Tier)
101
- { maxPictureSize: 122880, maxBitrate: 1500000, tier: 'L', level: 60 }, // Level 2 (Low Tier)
102
- { maxPictureSize: 245760, maxBitrate: 3000000, tier: 'L', level: 63 }, // Level 2.1 (Low Tier)
103
- { maxPictureSize: 552960, maxBitrate: 6000000, tier: 'L', level: 90 }, // Level 3 (Low Tier)
104
- { maxPictureSize: 983040, maxBitrate: 10000000, tier: 'L', level: 93 }, // Level 3.1 (Low Tier)
105
- { maxPictureSize: 2228224, maxBitrate: 12000000, tier: 'L', level: 120 }, // Level 4 (Low Tier)
106
- { maxPictureSize: 2228224, maxBitrate: 30000000, tier: 'H', level: 120 }, // Level 4 (High Tier)
107
- { maxPictureSize: 2228224, maxBitrate: 20000000, tier: 'L', level: 123 }, // Level 4.1 (Low Tier)
108
- { maxPictureSize: 2228224, maxBitrate: 50000000, tier: 'H', level: 123 }, // Level 4.1 (High Tier)
109
- { maxPictureSize: 8912896, maxBitrate: 25000000, tier: 'L', level: 150 }, // Level 5 (Low Tier)
110
- { maxPictureSize: 8912896, maxBitrate: 100000000, tier: 'H', level: 150 }, // Level 5 (High Tier)
111
- { maxPictureSize: 8912896, maxBitrate: 40000000, tier: 'L', level: 153 }, // Level 5.1 (Low Tier)
112
- { maxPictureSize: 8912896, maxBitrate: 160000000, tier: 'H', level: 153 }, // Level 5.1 (High Tier)
113
- { maxPictureSize: 8912896, maxBitrate: 60000000, tier: 'L', level: 156 }, // Level 5.2 (Low Tier)
114
- { maxPictureSize: 8912896, maxBitrate: 240000000, tier: 'H', level: 156 }, // Level 5.2 (High Tier)
115
- { maxPictureSize: 35651584, maxBitrate: 60000000, tier: 'L', level: 180 }, // Level 6 (Low Tier)
116
- { maxPictureSize: 35651584, maxBitrate: 240000000, tier: 'H', level: 180 }, // Level 6 (High Tier)
117
- { maxPictureSize: 35651584, maxBitrate: 120000000, tier: 'L', level: 183 }, // Level 6.1 (Low Tier)
118
- { maxPictureSize: 35651584, maxBitrate: 480000000, tier: 'H', level: 183 }, // Level 6.1 (High Tier)
119
- { maxPictureSize: 35651584, maxBitrate: 240000000, tier: 'L', level: 186 }, // Level 6.2 (Low Tier)
120
- { maxPictureSize: 35651584, maxBitrate: 800000000, tier: 'H', level: 186 }, // Level 6.2 (High Tier)
121
- ];
122
- // https://en.wikipedia.org/wiki/VP9
123
- export const VP9_LEVEL_TABLE = [
124
- { maxPictureSize: 36864, maxBitrate: 200000, level: 10 }, // Level 1
125
- { maxPictureSize: 73728, maxBitrate: 800000, level: 11 }, // Level 1.1
126
- { maxPictureSize: 122880, maxBitrate: 1800000, level: 20 }, // Level 2
127
- { maxPictureSize: 245760, maxBitrate: 3600000, level: 21 }, // Level 2.1
128
- { maxPictureSize: 552960, maxBitrate: 7200000, level: 30 }, // Level 3
129
- { maxPictureSize: 983040, maxBitrate: 12000000, level: 31 }, // Level 3.1
130
- { maxPictureSize: 2228224, maxBitrate: 18000000, level: 40 }, // Level 4
131
- { maxPictureSize: 2228224, maxBitrate: 30000000, level: 41 }, // Level 4.1
132
- { maxPictureSize: 8912896, maxBitrate: 60000000, level: 50 }, // Level 5
133
- { maxPictureSize: 8912896, maxBitrate: 120000000, level: 51 }, // Level 5.1
134
- { maxPictureSize: 8912896, maxBitrate: 180000000, level: 52 }, // Level 5.2
135
- { maxPictureSize: 35651584, maxBitrate: 180000000, level: 60 }, // Level 6
136
- { maxPictureSize: 35651584, maxBitrate: 240000000, level: 61 }, // Level 6.1
137
- { maxPictureSize: 35651584, maxBitrate: 480000000, level: 62 }, // Level 6.2
138
- ];
139
- // https://en.wikipedia.org/wiki/AV1
140
- const AV1_LEVEL_TABLE = [
141
- { maxPictureSize: 147456, maxBitrate: 1500000, tier: 'M', level: 0 }, // Level 2.0 (Main Tier)
142
- { maxPictureSize: 278784, maxBitrate: 3000000, tier: 'M', level: 1 }, // Level 2.1 (Main Tier)
143
- { maxPictureSize: 665856, maxBitrate: 6000000, tier: 'M', level: 4 }, // Level 3.0 (Main Tier)
144
- { maxPictureSize: 1065024, maxBitrate: 10000000, tier: 'M', level: 5 }, // Level 3.1 (Main Tier)
145
- { maxPictureSize: 2359296, maxBitrate: 12000000, tier: 'M', level: 8 }, // Level 4.0 (Main Tier)
146
- { maxPictureSize: 2359296, maxBitrate: 30000000, tier: 'H', level: 8 }, // Level 4.0 (High Tier)
147
- { maxPictureSize: 2359296, maxBitrate: 20000000, tier: 'M', level: 9 }, // Level 4.1 (Main Tier)
148
- { maxPictureSize: 2359296, maxBitrate: 50000000, tier: 'H', level: 9 }, // Level 4.1 (High Tier)
149
- { maxPictureSize: 8912896, maxBitrate: 30000000, tier: 'M', level: 12 }, // Level 5.0 (Main Tier)
150
- { maxPictureSize: 8912896, maxBitrate: 100000000, tier: 'H', level: 12 }, // Level 5.0 (High Tier)
151
- { maxPictureSize: 8912896, maxBitrate: 40000000, tier: 'M', level: 13 }, // Level 5.1 (Main Tier)
152
- { maxPictureSize: 8912896, maxBitrate: 160000000, tier: 'H', level: 13 }, // Level 5.1 (High Tier)
153
- { maxPictureSize: 8912896, maxBitrate: 60000000, tier: 'M', level: 14 }, // Level 5.2 (Main Tier)
154
- { maxPictureSize: 8912896, maxBitrate: 240000000, tier: 'H', level: 14 }, // Level 5.2 (High Tier)
155
- { maxPictureSize: 35651584, maxBitrate: 60000000, tier: 'M', level: 15 }, // Level 5.3 (Main Tier)
156
- { maxPictureSize: 35651584, maxBitrate: 240000000, tier: 'H', level: 15 }, // Level 5.3 (High Tier)
157
- { maxPictureSize: 35651584, maxBitrate: 60000000, tier: 'M', level: 16 }, // Level 6.0 (Main Tier)
158
- { maxPictureSize: 35651584, maxBitrate: 240000000, tier: 'H', level: 16 }, // Level 6.0 (High Tier)
159
- { maxPictureSize: 35651584, maxBitrate: 100000000, tier: 'M', level: 17 }, // Level 6.1 (Main Tier)
160
- { maxPictureSize: 35651584, maxBitrate: 480000000, tier: 'H', level: 17 }, // Level 6.1 (High Tier)
161
- { maxPictureSize: 35651584, maxBitrate: 160000000, tier: 'M', level: 18 }, // Level 6.2 (Main Tier)
162
- { maxPictureSize: 35651584, maxBitrate: 800000000, tier: 'H', level: 18 }, // Level 6.2 (High Tier)
163
- { maxPictureSize: 35651584, maxBitrate: 160000000, tier: 'M', level: 19 }, // Level 6.3 (Main Tier)
164
- { maxPictureSize: 35651584, maxBitrate: 800000000, tier: 'H', level: 19 }, // Level 6.3 (High Tier)
165
- ];
166
- const VP9_DEFAULT_SUFFIX = '.01.01.01.01.00';
167
- const AV1_DEFAULT_SUFFIX = '.0.110.01.01.01.0';
168
- export const buildVideoCodecString = (codec, width, height, bitrate) => {
169
- if (codec === 'avc') {
170
- const profileIndication = 0x64; // High Profile
171
- const totalMacroblocks = Math.ceil(width / 16) * Math.ceil(height / 16);
172
- // Determine the level based on the table
173
- const levelInfo = AVC_LEVEL_TABLE.find(level => totalMacroblocks <= level.maxMacroblocks && bitrate <= level.maxBitrate) ?? last(AVC_LEVEL_TABLE);
174
- const levelIndication = levelInfo ? levelInfo.level : 0;
175
- const hexProfileIndication = profileIndication.toString(16).padStart(2, '0');
176
- const hexProfileCompatibility = '00';
177
- const hexLevelIndication = levelIndication.toString(16).padStart(2, '0');
178
- return `avc1.${hexProfileIndication}${hexProfileCompatibility}${hexLevelIndication}`;
179
- }
180
- else if (codec === 'hevc') {
181
- const profilePrefix = ''; // Profile space 0
182
- const profileIdc = 1; // Main Profile
183
- const compatibilityFlags = '6'; // Taken from the example in ISO 14496-15
184
- const pictureSize = width * height;
185
- const levelInfo = HEVC_LEVEL_TABLE.find(level => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(HEVC_LEVEL_TABLE);
186
- const constraintFlags = 'B0'; // Progressive source flag
187
- return 'hev1.'
188
- + `${profilePrefix}${profileIdc}.`
189
- + `${compatibilityFlags}.`
190
- + `${levelInfo.tier}${levelInfo.level}.`
191
- + `${constraintFlags}`;
192
- }
193
- else if (codec === 'vp8') {
194
- return 'vp8'; // Easy, this one
195
- }
196
- else if (codec === 'vp9') {
197
- const profile = '00'; // Profile 0
198
- const pictureSize = width * height;
199
- const levelInfo = VP9_LEVEL_TABLE.find(level => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(VP9_LEVEL_TABLE);
200
- const bitDepth = '08'; // 8-bit
201
- return `vp09.${profile}.${levelInfo.level.toString().padStart(2, '0')}.${bitDepth}`;
202
- }
203
- else if (codec === 'av1') {
204
- const profile = 0; // Main Profile, single digit
205
- const pictureSize = width * height;
206
- const levelInfo = AV1_LEVEL_TABLE.find(level => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(AV1_LEVEL_TABLE);
207
- const level = levelInfo.level.toString().padStart(2, '0');
208
- const bitDepth = '08'; // 8-bit
209
- return `av01.${profile}.${level}${levelInfo.tier}.${bitDepth}`;
210
- }
211
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
212
- throw new TypeError(`Unhandled codec '${codec}'.`);
213
- };
214
- export const generateVp9CodecConfigurationFromCodecString = (codecString) => {
215
- // Reference: https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate
216
- const parts = codecString.split('.'); // We can derive the required values from the codec string
217
- const profile = Number(parts[1]);
218
- const level = Number(parts[2]);
219
- const bitDepth = Number(parts[3]);
220
- const chromaSubsampling = parts[4] ? Number(parts[4]) : 1;
221
- return [
222
- 1, 1, profile,
223
- 2, 1, level,
224
- 3, 1, bitDepth,
225
- 4, 1, chromaSubsampling,
226
- ];
227
- };
228
- export const generateAv1CodecConfigurationFromCodecString = (codecString) => {
229
- // Reference: https://aomediacodec.github.io/av1-isobmff/
230
- const parts = codecString.split('.'); // We can derive the required values from the codec string
231
- const marker = 1;
232
- const version = 1;
233
- const firstByte = (marker << 7) + version;
234
- const profile = Number(parts[1]);
235
- const levelAndTier = parts[2];
236
- const level = Number(levelAndTier.slice(0, -1));
237
- const secondByte = (profile << 5) + level;
238
- const tier = levelAndTier.slice(-1) === 'H' ? 1 : 0;
239
- const bitDepth = Number(parts[3]);
240
- const highBitDepth = bitDepth === 8 ? 0 : 1;
241
- const twelveBit = 0;
242
- const monochrome = parts[4] ? Number(parts[4]) : 0;
243
- const chromaSubsamplingX = parts[5] ? Number(parts[5][0]) : 1;
244
- const chromaSubsamplingY = parts[5] ? Number(parts[5][1]) : 1;
245
- const chromaSamplePosition = parts[5] ? Number(parts[5][2]) : 0; // CSP_UNKNOWN
246
- const thirdByte = (tier << 7)
247
- + (highBitDepth << 6)
248
- + (twelveBit << 5)
249
- + (monochrome << 4)
250
- + (chromaSubsamplingX << 3)
251
- + (chromaSubsamplingY << 2)
252
- + chromaSamplePosition;
253
- const initialPresentationDelayPresent = 0; // Should be fine
254
- const fourthByte = initialPresentationDelayPresent;
255
- return [firstByte, secondByte, thirdByte, fourthByte];
256
- };
257
- export const extractVideoCodecString = (trackInfo) => {
258
- const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo } = trackInfo;
259
- if (codec === 'avc') {
260
- assert(trackInfo.avcType !== null);
261
- if (avcCodecInfo) {
262
- const bytes = new Uint8Array([
263
- avcCodecInfo.avcProfileIndication,
264
- avcCodecInfo.profileCompatibility,
265
- avcCodecInfo.avcLevelIndication,
266
- ]);
267
- return `avc${trackInfo.avcType}.${bytesToHexString(bytes)}`;
268
- }
269
- if (!codecDescription || codecDescription.byteLength < 4) {
270
- throw new TypeError('AVC decoder description is not provided or is not at least 4 bytes long.');
271
- }
272
- return `avc${trackInfo.avcType}.${bytesToHexString(codecDescription.subarray(1, 4))}`;
273
- }
274
- else if (codec === 'hevc') {
275
- let generalProfileSpace;
276
- let generalProfileIdc;
277
- let compatibilityFlags;
278
- let generalTierFlag;
279
- let generalLevelIdc;
280
- let constraintFlags;
281
- if (hevcCodecInfo) {
282
- generalProfileSpace = hevcCodecInfo.generalProfileSpace;
283
- generalProfileIdc = hevcCodecInfo.generalProfileIdc;
284
- compatibilityFlags = reverseBitsU32(hevcCodecInfo.generalProfileCompatibilityFlags);
285
- generalTierFlag = hevcCodecInfo.generalTierFlag;
286
- generalLevelIdc = hevcCodecInfo.generalLevelIdc;
287
- constraintFlags = [...hevcCodecInfo.generalConstraintIndicatorFlags];
288
- }
289
- else {
290
- if (!codecDescription || codecDescription.byteLength < 23) {
291
- throw new TypeError('HEVC decoder description is not provided or is not at least 23 bytes long.');
292
- }
293
- const view = toDataView(codecDescription);
294
- const profileByte = view.getUint8(1);
295
- generalProfileSpace = (profileByte >> 6) & 0x03;
296
- generalProfileIdc = profileByte & 0x1F;
297
- compatibilityFlags = reverseBitsU32(view.getUint32(2));
298
- generalTierFlag = (profileByte >> 5) & 0x01;
299
- generalLevelIdc = view.getUint8(12);
300
- constraintFlags = [];
301
- for (let i = 0; i < 6; i++) {
302
- constraintFlags.push(view.getUint8(6 + i));
303
- }
304
- }
305
- let codecString = 'hev1.';
306
- codecString += ['', 'A', 'B', 'C'][generalProfileSpace] + generalProfileIdc;
307
- codecString += '.';
308
- codecString += compatibilityFlags.toString(16).toUpperCase();
309
- codecString += '.';
310
- codecString += generalTierFlag === 0 ? 'L' : 'H';
311
- codecString += generalLevelIdc;
312
- while (constraintFlags.length > 0 && constraintFlags[constraintFlags.length - 1] === 0) {
313
- constraintFlags.pop();
314
- }
315
- if (constraintFlags.length > 0) {
316
- codecString += '.';
317
- codecString += constraintFlags.map(x => x.toString(16).toUpperCase()).join('.');
318
- }
319
- return codecString;
320
- }
321
- else if (codec === 'vp8') {
322
- return 'vp8'; // Easy, this one
323
- }
324
- else if (codec === 'vp9') {
325
- if (!vp9CodecInfo) {
326
- // Calculate level based on dimensions
327
- const pictureSize = trackInfo.width * trackInfo.height;
328
- let level = last(VP9_LEVEL_TABLE).level; // Default to highest level
329
- for (const entry of VP9_LEVEL_TABLE) {
330
- if (pictureSize <= entry.maxPictureSize) {
331
- level = entry.level;
332
- break;
333
- }
334
- }
335
- // We don't really know better, so let's return a general-purpose, common codec string and hope for the best
336
- return `vp09.00.${level.toString().padStart(2, '0')}.08`;
337
- }
338
- const profile = vp9CodecInfo.profile.toString().padStart(2, '0');
339
- const level = vp9CodecInfo.level.toString().padStart(2, '0');
340
- const bitDepth = vp9CodecInfo.bitDepth.toString().padStart(2, '0');
341
- const chromaSubsampling = vp9CodecInfo.chromaSubsampling.toString().padStart(2, '0');
342
- const colourPrimaries = vp9CodecInfo.colourPrimaries.toString().padStart(2, '0');
343
- const transferCharacteristics = vp9CodecInfo.transferCharacteristics.toString().padStart(2, '0');
344
- const matrixCoefficients = vp9CodecInfo.matrixCoefficients.toString().padStart(2, '0');
345
- const videoFullRangeFlag = vp9CodecInfo.videoFullRangeFlag.toString().padStart(2, '0');
346
- let string = `vp09.${profile}.${level}.${bitDepth}.${chromaSubsampling}`;
347
- string += `.${colourPrimaries}.${transferCharacteristics}.${matrixCoefficients}.${videoFullRangeFlag}`;
348
- if (string.endsWith(VP9_DEFAULT_SUFFIX)) {
349
- string = string.slice(0, -VP9_DEFAULT_SUFFIX.length);
350
- }
351
- return string;
352
- }
353
- else if (codec === 'av1') {
354
- if (!av1CodecInfo) {
355
- // Calculate level based on dimensions
356
- const pictureSize = trackInfo.width * trackInfo.height;
357
- let level = last(VP9_LEVEL_TABLE).level; // Default to highest level
358
- for (const entry of VP9_LEVEL_TABLE) {
359
- if (pictureSize <= entry.maxPictureSize) {
360
- level = entry.level;
361
- break;
362
- }
363
- }
364
- // We don't really know better, so let's return a general-purpose, common codec string and hope for the best
365
- return `av01.0.${level.toString().padStart(2, '0')}M.08`;
366
- }
367
- // https://aomediacodec.github.io/av1-isobmff/#codecsparam
368
- const profile = av1CodecInfo.profile; // Single digit
369
- const level = av1CodecInfo.level.toString().padStart(2, '0');
370
- const tier = av1CodecInfo.tier ? 'H' : 'M';
371
- const bitDepth = av1CodecInfo.bitDepth.toString().padStart(2, '0');
372
- const monochrome = av1CodecInfo.monochrome ? '1' : '0';
373
- const chromaSubsampling = 100 * av1CodecInfo.chromaSubsamplingX
374
- + 10 * av1CodecInfo.chromaSubsamplingY
375
- + 1 * (av1CodecInfo.chromaSubsamplingX && av1CodecInfo.chromaSubsamplingY
376
- ? av1CodecInfo.chromaSamplePosition
377
- : 0);
378
- // The defaults are 1 (ITU-R BT.709)
379
- const colorPrimaries = colorSpace?.primaries ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 1;
380
- const transferCharacteristics = colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 1;
381
- const matrixCoefficients = colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 1;
382
- const videoFullRangeFlag = colorSpace?.fullRange ? 1 : 0;
383
- let string = `av01.${profile}.${level}${tier}.${bitDepth}`;
384
- string += `.${monochrome}.${chromaSubsampling.toString().padStart(3, '0')}`;
385
- string += `.${colorPrimaries.toString().padStart(2, '0')}`;
386
- string += `.${transferCharacteristics.toString().padStart(2, '0')}`;
387
- string += `.${matrixCoefficients.toString().padStart(2, '0')}`;
388
- string += `.${videoFullRangeFlag}`;
389
- if (string.endsWith(AV1_DEFAULT_SUFFIX)) {
390
- string = string.slice(0, -AV1_DEFAULT_SUFFIX.length);
391
- }
392
- return string;
393
- }
394
- throw new TypeError(`Unhandled codec '${codec}'.`);
395
- };
396
- export const buildAudioCodecString = (codec, numberOfChannels, sampleRate) => {
397
- if (codec === 'aac') {
398
- // If stereo or higher channels and lower sample rate, likely using HE-AAC v2 with PS
399
- if (numberOfChannels >= 2 && sampleRate <= 24000) {
400
- return 'mp4a.40.29'; // HE-AAC v2 (AAC LC + SBR + PS)
401
- }
402
- // If sample rate is low, likely using HE-AAC v1 with SBR
403
- if (sampleRate <= 24000) {
404
- return 'mp4a.40.5'; // HE-AAC v1 (AAC LC + SBR)
405
- }
406
- // Default to standard AAC-LC for higher sample rates
407
- return 'mp4a.40.2'; // AAC-LC
408
- }
409
- else if (codec === 'mp3') {
410
- return 'mp3';
411
- }
412
- else if (codec === 'opus') {
413
- return 'opus';
414
- }
415
- else if (codec === 'vorbis') {
416
- return 'vorbis';
417
- }
418
- else if (codec === 'flac') {
419
- return 'flac';
420
- }
421
- else if (PCM_AUDIO_CODECS.includes(codec)) {
422
- return codec;
423
- }
424
- throw new TypeError(`Unhandled codec '${codec}'.`);
425
- };
426
- export const extractAudioCodecString = (trackInfo) => {
427
- const { codec, codecDescription, aacCodecInfo } = trackInfo;
428
- if (codec === 'aac') {
429
- if (!aacCodecInfo) {
430
- throw new TypeError('AAC codec info must be provided.');
431
- }
432
- if (aacCodecInfo.isMpeg2) {
433
- return 'mp4a.67';
434
- }
435
- else {
436
- const audioSpecificConfig = parseAacAudioSpecificConfig(codecDescription);
437
- return `mp4a.40.${audioSpecificConfig.objectType}`;
438
- }
439
- }
440
- else if (codec === 'mp3') {
441
- return 'mp3';
442
- }
443
- else if (codec === 'opus') {
444
- return 'opus';
445
- }
446
- else if (codec === 'vorbis') {
447
- return 'vorbis';
448
- }
449
- else if (codec === 'flac') {
450
- return 'flac';
451
- }
452
- else if (codec && PCM_AUDIO_CODECS.includes(codec)) {
453
- return codec;
454
- }
455
- throw new TypeError(`Unhandled codec '${codec}'.`);
456
- };
457
- export const aacFrequencyTable = [
458
- 96000, 88200, 64000, 48000, 44100, 32000,
459
- 24000, 22050, 16000, 12000, 11025, 8000, 7350,
460
- ];
461
- export const aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8];
462
- export const parseAacAudioSpecificConfig = (bytes) => {
463
- if (!bytes || bytes.byteLength < 2) {
464
- throw new TypeError('AAC description must be at least 2 bytes long.');
465
- }
466
- const bitstream = new Bitstream(bytes);
467
- let objectType = bitstream.readBits(5);
468
- if (objectType === 31) {
469
- objectType = 32 + bitstream.readBits(6);
470
- }
471
- const frequencyIndex = bitstream.readBits(4);
472
- let sampleRate = null;
473
- if (frequencyIndex === 15) {
474
- sampleRate = bitstream.readBits(24);
475
- }
476
- else {
477
- if (frequencyIndex < aacFrequencyTable.length) {
478
- sampleRate = aacFrequencyTable[frequencyIndex];
479
- }
480
- }
481
- const channelConfiguration = bitstream.readBits(4);
482
- let numberOfChannels = null;
483
- if (channelConfiguration >= 1 && channelConfiguration <= 7) {
484
- numberOfChannels = aacChannelMap[channelConfiguration];
485
- }
486
- return {
487
- objectType,
488
- frequencyIndex,
489
- sampleRate,
490
- channelConfiguration,
491
- numberOfChannels,
492
- };
493
- };
494
- export const buildAacAudioSpecificConfig = (config) => {
495
- let frequencyIndex = aacFrequencyTable.indexOf(config.sampleRate);
496
- let customSampleRate = null;
497
- if (frequencyIndex === -1) {
498
- frequencyIndex = 15;
499
- customSampleRate = config.sampleRate;
500
- }
501
- const channelConfiguration = aacChannelMap.indexOf(config.numberOfChannels);
502
- if (channelConfiguration === -1) {
503
- throw new TypeError(`Unsupported number of channels: ${config.numberOfChannels}`);
504
- }
505
- let bitCount = 5 + 4 + 4;
506
- if (config.objectType >= 32) {
507
- bitCount += 6;
508
- }
509
- if (frequencyIndex === 15) {
510
- bitCount += 24;
511
- }
512
- const byteCount = Math.ceil(bitCount / 8);
513
- const bytes = new Uint8Array(byteCount);
514
- const bitstream = new Bitstream(bytes);
515
- if (config.objectType < 32) {
516
- bitstream.writeBits(5, config.objectType);
517
- }
518
- else {
519
- bitstream.writeBits(5, 31);
520
- bitstream.writeBits(6, config.objectType - 32);
521
- }
522
- bitstream.writeBits(4, frequencyIndex);
523
- if (frequencyIndex === 15) {
524
- bitstream.writeBits(24, customSampleRate);
525
- }
526
- bitstream.writeBits(4, channelConfiguration);
527
- return bytes;
528
- };
529
- export const OPUS_SAMPLE_RATE = 48_000;
530
- const PCM_CODEC_REGEX = /^pcm-([usf])(\d+)+(be)?$/;
531
- export const parsePcmCodec = (codec) => {
532
- assert(PCM_AUDIO_CODECS.includes(codec));
533
- if (codec === 'ulaw') {
534
- return { dataType: 'ulaw', sampleSize: 1, littleEndian: true, silentValue: 255 };
535
- }
536
- else if (codec === 'alaw') {
537
- return { dataType: 'alaw', sampleSize: 1, littleEndian: true, silentValue: 213 };
538
- }
539
- const match = PCM_CODEC_REGEX.exec(codec);
540
- assert(match);
541
- let dataType;
542
- if (match[1] === 'u') {
543
- dataType = 'unsigned';
544
- }
545
- else if (match[1] === 's') {
546
- dataType = 'signed';
547
- }
548
- else {
549
- dataType = 'float';
550
- }
551
- const sampleSize = (Number(match[2]) / 8);
552
- const littleEndian = match[3] !== 'be';
553
- const silentValue = codec === 'pcm-u8' ? 2 ** 7 : 0;
554
- return { dataType, sampleSize, littleEndian, silentValue };
555
- };
556
- export const inferCodecFromCodecString = (codecString) => {
557
- // Video codecs
558
- if (codecString.startsWith('avc1') || codecString.startsWith('avc3')) {
559
- return 'avc';
560
- }
561
- else if (codecString.startsWith('hev1') || codecString.startsWith('hvc1')) {
562
- return 'hevc';
563
- }
564
- else if (codecString === 'vp8') {
565
- return 'vp8';
566
- }
567
- else if (codecString.startsWith('vp09')) {
568
- return 'vp9';
569
- }
570
- else if (codecString.startsWith('av01')) {
571
- return 'av1';
572
- }
573
- // Audio codecs
574
- if (codecString.startsWith('mp4a.40') || codecString === 'mp4a.67') {
575
- return 'aac';
576
- }
577
- else if (codecString === 'mp3'
578
- || codecString === 'mp4a.69'
579
- || codecString === 'mp4a.6B'
580
- || codecString === 'mp4a.6b') {
581
- return 'mp3';
582
- }
583
- else if (codecString === 'opus') {
584
- return 'opus';
585
- }
586
- else if (codecString === 'vorbis') {
587
- return 'vorbis';
588
- }
589
- else if (codecString === 'flac') {
590
- return 'flac';
591
- }
592
- else if (codecString === 'ulaw') {
593
- return 'ulaw';
594
- }
595
- else if (codecString === 'alaw') {
596
- return 'alaw';
597
- }
598
- else if (PCM_CODEC_REGEX.test(codecString)) {
599
- return codecString;
600
- }
601
- // Subtitle codecs
602
- if (codecString === 'webvtt') {
603
- return 'webvtt';
604
- }
605
- else if (codecString === 'srt') {
606
- return 'srt';
607
- }
608
- else if (codecString === 'ass') {
609
- return 'ass';
610
- }
611
- else if (codecString === 'ssa') {
612
- return 'ssa';
613
- }
614
- return null;
615
- };
616
- export const getVideoEncoderConfigExtension = (codec) => {
617
- if (codec === 'avc') {
618
- return {
619
- avc: {
620
- format: 'avc', // Ensure the format is not Annex B
621
- },
622
- };
623
- }
624
- else if (codec === 'hevc') {
625
- return {
626
- hevc: {
627
- format: 'hevc', // Ensure the format is not Annex B
628
- },
629
- };
630
- }
631
- return {};
632
- };
633
- export const getAudioEncoderConfigExtension = (codec) => {
634
- if (codec === 'aac') {
635
- return {
636
- aac: {
637
- format: 'aac', // Ensure the format is not ADTS
638
- },
639
- };
640
- }
641
- else if (codec === 'opus') {
642
- return {
643
- opus: {
644
- format: 'opus',
645
- },
646
- };
647
- }
648
- return {};
649
- };
650
- const VALID_VIDEO_CODEC_STRING_PREFIXES = ['avc1', 'avc3', 'hev1', 'hvc1', 'vp8', 'vp09', 'av01'];
651
- const AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\.[0-9a-fA-F]{6}$/;
652
- const HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\.(?:[ABC]?\d+)\.[0-9a-fA-F]{1,8}\.[LH]\d+(?:\.[0-9a-fA-F]{1,2}){0,6}$/;
653
- const VP9_CODEC_STRING_REGEX = /^vp09(?:\.\d{2}){3}(?:(?:\.\d{2}){5})?$/;
654
- const AV1_CODEC_STRING_REGEX = /^av01\.\d\.\d{2}[MH]\.\d{2}(?:\.\d\.\d{3}\.\d{2}\.\d{2}\.\d{2}\.\d)?$/;
655
- export const validateVideoChunkMetadata = (metadata) => {
656
- if (!metadata) {
657
- throw new TypeError('Video chunk metadata must be provided.');
658
- }
659
- if (typeof metadata !== 'object') {
660
- throw new TypeError('Video chunk metadata must be an object.');
661
- }
662
- if (!metadata.decoderConfig) {
663
- throw new TypeError('Video chunk metadata must include a decoder configuration.');
664
- }
665
- if (typeof metadata.decoderConfig !== 'object') {
666
- throw new TypeError('Video chunk metadata decoder configuration must be an object.');
667
- }
668
- if (typeof metadata.decoderConfig.codec !== 'string') {
669
- throw new TypeError('Video chunk metadata decoder configuration must specify a codec string.');
670
- }
671
- if (!VALID_VIDEO_CODEC_STRING_PREFIXES.some(prefix => metadata.decoderConfig.codec.startsWith(prefix))) {
672
- throw new TypeError('Video chunk metadata decoder configuration codec string must be a valid video codec string as specified in'
673
- + ' the WebCodecs Codec Registry.');
674
- }
675
- if (!Number.isInteger(metadata.decoderConfig.codedWidth) || metadata.decoderConfig.codedWidth <= 0) {
676
- throw new TypeError('Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).');
677
- }
678
- if (!Number.isInteger(metadata.decoderConfig.codedHeight) || metadata.decoderConfig.codedHeight <= 0) {
679
- throw new TypeError('Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).');
680
- }
681
- if (metadata.decoderConfig.description !== undefined) {
682
- if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {
683
- throw new TypeError('Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an'
684
- + ' ArrayBuffer view.');
685
- }
686
- }
687
- if (metadata.decoderConfig.colorSpace !== undefined) {
688
- const { colorSpace } = metadata.decoderConfig;
689
- if (typeof colorSpace !== 'object') {
690
- throw new TypeError('Video chunk metadata decoder configuration colorSpace, when provided, must be an object.');
691
- }
692
- const primariesValues = Object.keys(COLOR_PRIMARIES_MAP);
693
- if (colorSpace.primaries != null && !primariesValues.includes(colorSpace.primaries)) {
694
- throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of`
695
- + ` ${primariesValues.join(', ')}.`);
696
- }
697
- const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP);
698
- if (colorSpace.transfer != null && !transferValues.includes(colorSpace.transfer)) {
699
- throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of`
700
- + ` ${transferValues.join(', ')}.`);
701
- }
702
- const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP);
703
- if (colorSpace.matrix != null && !matrixValues.includes(colorSpace.matrix)) {
704
- throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of`
705
- + ` ${matrixValues.join(', ')}.`);
706
- }
707
- if (colorSpace.fullRange != null && typeof colorSpace.fullRange !== 'boolean') {
708
- throw new TypeError('Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.');
709
- }
710
- }
711
- if (metadata.decoderConfig.codec.startsWith('avc1') || metadata.decoderConfig.codec.startsWith('avc3')) {
712
- // AVC-specific validation
713
- if (!AVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {
714
- throw new TypeError('Video chunk metadata decoder configuration codec string for AVC must be a valid AVC codec string as'
715
- + ' specified in Section 3.4 of RFC 6381.');
716
- }
717
- // `description` may or may not be set, depending on if the format is AVCC or Annex B, so don't perform any
718
- // validation for it.
719
- // https://www.w3.org/TR/webcodecs-avc-codec-registration
720
- }
721
- else if (metadata.decoderConfig.codec.startsWith('hev1') || metadata.decoderConfig.codec.startsWith('hvc1')) {
722
- // HEVC-specific validation
723
- if (!HEVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {
724
- throw new TypeError('Video chunk metadata decoder configuration codec string for HEVC must be a valid HEVC codec string as'
725
- + ' specified in Section E.3 of ISO 14496-15.');
726
- }
727
- // `description` may or may not be set, depending on if the format is HEVC or Annex B, so don't perform any
728
- // validation for it.
729
- // https://www.w3.org/TR/webcodecs-hevc-codec-registration
730
- }
731
- else if (metadata.decoderConfig.codec.startsWith('vp8')) {
732
- // VP8-specific validation
733
- if (metadata.decoderConfig.codec !== 'vp8') {
734
- throw new TypeError('Video chunk metadata decoder configuration codec string for VP8 must be "vp8".');
735
- }
736
- }
737
- else if (metadata.decoderConfig.codec.startsWith('vp09')) {
738
- // VP9-specific validation
739
- if (!VP9_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {
740
- throw new TypeError('Video chunk metadata decoder configuration codec string for VP9 must be a valid VP9 codec string as'
741
- + ' specified in Section "Codecs Parameter String" of https://www.webmproject.org/vp9/mp4/.');
742
- }
743
- }
744
- else if (metadata.decoderConfig.codec.startsWith('av01')) {
745
- // AV1-specific validation
746
- if (!AV1_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {
747
- throw new TypeError('Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as'
748
- + ' specified in Section "Codecs Parameter String" of https://aomediacodec.github.io/av1-isobmff/.');
749
- }
750
- }
751
- };
752
- const VALID_AUDIO_CODEC_STRING_PREFIXES = ['mp4a', 'mp3', 'opus', 'vorbis', 'flac', 'ulaw', 'alaw', 'pcm'];
753
- export const validateAudioChunkMetadata = (metadata) => {
754
- if (!metadata) {
755
- throw new TypeError('Audio chunk metadata must be provided.');
756
- }
757
- if (typeof metadata !== 'object') {
758
- throw new TypeError('Audio chunk metadata must be an object.');
759
- }
760
- if (!metadata.decoderConfig) {
761
- throw new TypeError('Audio chunk metadata must include a decoder configuration.');
762
- }
763
- if (typeof metadata.decoderConfig !== 'object') {
764
- throw new TypeError('Audio chunk metadata decoder configuration must be an object.');
765
- }
766
- if (typeof metadata.decoderConfig.codec !== 'string') {
767
- throw new TypeError('Audio chunk metadata decoder configuration must specify a codec string.');
768
- }
769
- if (!VALID_AUDIO_CODEC_STRING_PREFIXES.some(prefix => metadata.decoderConfig.codec.startsWith(prefix))) {
770
- throw new TypeError('Audio chunk metadata decoder configuration codec string must be a valid audio codec string as specified in'
771
- + ' the WebCodecs Codec Registry.');
772
- }
773
- if (!Number.isInteger(metadata.decoderConfig.sampleRate) || metadata.decoderConfig.sampleRate <= 0) {
774
- throw new TypeError('Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).');
775
- }
776
- if (!Number.isInteger(metadata.decoderConfig.numberOfChannels) || metadata.decoderConfig.numberOfChannels <= 0) {
777
- throw new TypeError('Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).');
778
- }
779
- if (metadata.decoderConfig.description !== undefined) {
780
- if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {
781
- throw new TypeError('Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an'
782
- + ' ArrayBuffer view.');
783
- }
784
- }
785
- if (metadata.decoderConfig.codec.startsWith('mp4a')
786
- // These three refer to MP3:
787
- && metadata.decoderConfig.codec !== 'mp4a.69'
788
- && metadata.decoderConfig.codec !== 'mp4a.6B'
789
- && metadata.decoderConfig.codec !== 'mp4a.6b') {
790
- // AAC-specific validation
791
- const validStrings = ['mp4a.40.2', 'mp4a.40.02', 'mp4a.40.5', 'mp4a.40.05', 'mp4a.40.29', 'mp4a.67'];
792
- if (!validStrings.includes(metadata.decoderConfig.codec)) {
793
- throw new TypeError('Audio chunk metadata decoder configuration codec string for AAC must be a valid AAC codec string as'
794
- + ' specified in https://www.w3.org/TR/webcodecs-aac-codec-registration/.');
795
- }
796
- if (!metadata.decoderConfig.description) {
797
- throw new TypeError('Audio chunk metadata decoder configuration for AAC must include a description, which is expected to be'
798
- + ' an AudioSpecificConfig as specified in ISO 14496-3.');
799
- }
800
- }
801
- else if (metadata.decoderConfig.codec.startsWith('mp3') || metadata.decoderConfig.codec.startsWith('mp4a')) {
802
- // MP3-specific validation
803
- if (metadata.decoderConfig.codec !== 'mp3'
804
- && metadata.decoderConfig.codec !== 'mp4a.69'
805
- && metadata.decoderConfig.codec !== 'mp4a.6B'
806
- && metadata.decoderConfig.codec !== 'mp4a.6b') {
807
- throw new TypeError('Audio chunk metadata decoder configuration codec string for MP3 must be "mp3", "mp4a.69" or'
808
- + ' "mp4a.6B".');
809
- }
810
- }
811
- else if (metadata.decoderConfig.codec.startsWith('opus')) {
812
- // Opus-specific validation
813
- if (metadata.decoderConfig.codec !== 'opus') {
814
- throw new TypeError('Audio chunk metadata decoder configuration codec string for Opus must be "opus".');
815
- }
816
- if (metadata.decoderConfig.description && metadata.decoderConfig.description.byteLength < 18) {
817
- // Description is optional for Opus per-spec, so we shouldn't enforce it
818
- throw new TypeError('Audio chunk metadata decoder configuration description, when specified, is expected to be an'
819
- + ' Identification Header as specified in Section 5.1 of RFC 7845.');
820
- }
821
- }
822
- else if (metadata.decoderConfig.codec.startsWith('vorbis')) {
823
- // Vorbis-specific validation
824
- if (metadata.decoderConfig.codec !== 'vorbis') {
825
- throw new TypeError('Audio chunk metadata decoder configuration codec string for Vorbis must be "vorbis".');
826
- }
827
- if (!metadata.decoderConfig.description) {
828
- throw new TypeError('Audio chunk metadata decoder configuration for Vorbis must include a description, which is expected to'
829
- + ' adhere to the format described in https://www.w3.org/TR/webcodecs-vorbis-codec-registration/.');
830
- }
831
- }
832
- else if (metadata.decoderConfig.codec.startsWith('flac')) {
833
- // FLAC-specific validation
834
- if (metadata.decoderConfig.codec !== 'flac') {
835
- throw new TypeError('Audio chunk metadata decoder configuration codec string for FLAC must be "flac".');
836
- }
837
- const minDescriptionSize = 4 + 4 + 34; // 'fLaC' + metadata block header + STREAMINFO block
838
- if (!metadata.decoderConfig.description || metadata.decoderConfig.description.byteLength < minDescriptionSize) {
839
- throw new TypeError('Audio chunk metadata decoder configuration for FLAC must include a description, which is expected to'
840
- + ' adhere to the format described in https://www.w3.org/TR/webcodecs-flac-codec-registration/.');
841
- }
842
- }
843
- else if (metadata.decoderConfig.codec.startsWith('pcm')
844
- || metadata.decoderConfig.codec.startsWith('ulaw')
845
- || metadata.decoderConfig.codec.startsWith('alaw')) {
846
- // PCM-specific validation
847
- if (!PCM_AUDIO_CODECS.includes(metadata.decoderConfig.codec)) {
848
- throw new TypeError('Audio chunk metadata decoder configuration codec string for PCM must be one of the supported PCM'
849
- + ` codecs (${PCM_AUDIO_CODECS.join(', ')}).`);
850
- }
851
- }
852
- };
853
- export const validateSubtitleMetadata = (metadata) => {
854
- if (!metadata) {
855
- throw new TypeError('Subtitle metadata must be provided.');
856
- }
857
- if (typeof metadata !== 'object') {
858
- throw new TypeError('Subtitle metadata must be an object.');
859
- }
860
- if (!metadata.config) {
861
- throw new TypeError('Subtitle metadata must include a config object.');
862
- }
863
- if (typeof metadata.config !== 'object') {
864
- throw new TypeError('Subtitle metadata config must be an object.');
865
- }
866
- if (typeof metadata.config.description !== 'string') {
867
- throw new TypeError('Subtitle metadata config description must be a string.');
868
- }
869
- };