@kenzuya/mediabunny 1.26.0 → 1.28.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +1 -1
  2. package/dist/bundles/{mediabunny.mjs → mediabunny.js} +21963 -21388
  3. package/dist/bundles/mediabunny.min.js +490 -0
  4. package/dist/modules/shared/mp3-misc.d.ts.map +1 -1
  5. package/dist/modules/src/adts/adts-demuxer.d.ts +6 -6
  6. package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
  7. package/dist/modules/src/adts/adts-muxer.d.ts +4 -4
  8. package/dist/modules/src/adts/adts-muxer.d.ts.map +1 -1
  9. package/dist/modules/src/adts/adts-reader.d.ts +1 -1
  10. package/dist/modules/src/adts/adts-reader.d.ts.map +1 -1
  11. package/dist/modules/src/avi/avi-demuxer.d.ts +44 -0
  12. package/dist/modules/src/avi/avi-demuxer.d.ts.map +1 -0
  13. package/dist/modules/src/avi/avi-misc.d.ts +88 -0
  14. package/dist/modules/src/avi/avi-misc.d.ts.map +1 -0
  15. package/dist/modules/src/avi/avi-muxer.d.ts +45 -0
  16. package/dist/modules/src/avi/avi-muxer.d.ts.map +1 -0
  17. package/dist/modules/src/avi/riff-writer.d.ts +26 -0
  18. package/dist/modules/src/avi/riff-writer.d.ts.map +1 -0
  19. package/dist/modules/src/codec-data.d.ts +8 -3
  20. package/dist/modules/src/codec-data.d.ts.map +1 -1
  21. package/dist/modules/src/codec.d.ts +10 -10
  22. package/dist/modules/src/codec.d.ts.map +1 -1
  23. package/dist/modules/src/conversion.d.ts +33 -16
  24. package/dist/modules/src/conversion.d.ts.map +1 -1
  25. package/dist/modules/src/custom-coder.d.ts +8 -8
  26. package/dist/modules/src/custom-coder.d.ts.map +1 -1
  27. package/dist/modules/src/demuxer.d.ts +3 -3
  28. package/dist/modules/src/demuxer.d.ts.map +1 -1
  29. package/dist/modules/src/encode.d.ts +8 -8
  30. package/dist/modules/src/encode.d.ts.map +1 -1
  31. package/dist/modules/src/flac/flac-demuxer.d.ts +7 -7
  32. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  33. package/dist/modules/src/flac/flac-misc.d.ts +3 -3
  34. package/dist/modules/src/flac/flac-misc.d.ts.map +1 -1
  35. package/dist/modules/src/flac/flac-muxer.d.ts +5 -5
  36. package/dist/modules/src/flac/flac-muxer.d.ts.map +1 -1
  37. package/dist/modules/src/id3.d.ts +3 -3
  38. package/dist/modules/src/id3.d.ts.map +1 -1
  39. package/dist/modules/src/index.d.ts +20 -20
  40. package/dist/modules/src/index.d.ts.map +1 -1
  41. package/dist/modules/src/input-format.d.ts +22 -0
  42. package/dist/modules/src/input-format.d.ts.map +1 -1
  43. package/dist/modules/src/input-track.d.ts +8 -8
  44. package/dist/modules/src/input-track.d.ts.map +1 -1
  45. package/dist/modules/src/input.d.ts +12 -12
  46. package/dist/modules/src/isobmff/isobmff-boxes.d.ts +2 -2
  47. package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
  48. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts +12 -12
  49. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
  50. package/dist/modules/src/isobmff/isobmff-misc.d.ts.map +1 -1
  51. package/dist/modules/src/isobmff/isobmff-muxer.d.ts +11 -11
  52. package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
  53. package/dist/modules/src/isobmff/isobmff-reader.d.ts +2 -2
  54. package/dist/modules/src/isobmff/isobmff-reader.d.ts.map +1 -1
  55. package/dist/modules/src/matroska/ebml.d.ts +3 -3
  56. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  57. package/dist/modules/src/matroska/matroska-demuxer.d.ts +13 -13
  58. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  59. package/dist/modules/src/matroska/matroska-input.d.ts +33 -0
  60. package/dist/modules/src/matroska/matroska-input.d.ts.map +1 -0
  61. package/dist/modules/src/matroska/matroska-misc.d.ts.map +1 -1
  62. package/dist/modules/src/matroska/matroska-muxer.d.ts +5 -5
  63. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  64. package/dist/modules/src/media-sink.d.ts +5 -5
  65. package/dist/modules/src/media-sink.d.ts.map +1 -1
  66. package/dist/modules/src/media-source.d.ts +22 -4
  67. package/dist/modules/src/media-source.d.ts.map +1 -1
  68. package/dist/modules/src/metadata.d.ts +2 -2
  69. package/dist/modules/src/metadata.d.ts.map +1 -1
  70. package/dist/modules/src/misc.d.ts +5 -4
  71. package/dist/modules/src/misc.d.ts.map +1 -1
  72. package/dist/modules/src/mp3/mp3-demuxer.d.ts +7 -7
  73. package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
  74. package/dist/modules/src/mp3/mp3-muxer.d.ts +4 -4
  75. package/dist/modules/src/mp3/mp3-muxer.d.ts.map +1 -1
  76. package/dist/modules/src/mp3/mp3-reader.d.ts +2 -2
  77. package/dist/modules/src/mp3/mp3-reader.d.ts.map +1 -1
  78. package/dist/modules/src/mp3/mp3-writer.d.ts +1 -1
  79. package/dist/modules/src/mp3/mp3-writer.d.ts.map +1 -1
  80. package/dist/modules/src/muxer.d.ts +4 -4
  81. package/dist/modules/src/muxer.d.ts.map +1 -1
  82. package/dist/modules/src/node.d.ts +1 -1
  83. package/dist/modules/src/ogg/ogg-demuxer.d.ts +7 -7
  84. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  85. package/dist/modules/src/ogg/ogg-misc.d.ts +1 -1
  86. package/dist/modules/src/ogg/ogg-misc.d.ts.map +1 -1
  87. package/dist/modules/src/ogg/ogg-muxer.d.ts +5 -5
  88. package/dist/modules/src/ogg/ogg-muxer.d.ts.map +1 -1
  89. package/dist/modules/src/ogg/ogg-reader.d.ts +1 -1
  90. package/dist/modules/src/ogg/ogg-reader.d.ts.map +1 -1
  91. package/dist/modules/src/output-format.d.ts +51 -6
  92. package/dist/modules/src/output-format.d.ts.map +1 -1
  93. package/dist/modules/src/output.d.ts +13 -13
  94. package/dist/modules/src/output.d.ts.map +1 -1
  95. package/dist/modules/src/packet.d.ts +1 -1
  96. package/dist/modules/src/packet.d.ts.map +1 -1
  97. package/dist/modules/src/pcm.d.ts.map +1 -1
  98. package/dist/modules/src/reader.d.ts +2 -2
  99. package/dist/modules/src/reader.d.ts.map +1 -1
  100. package/dist/modules/src/sample.d.ts +57 -15
  101. package/dist/modules/src/sample.d.ts.map +1 -1
  102. package/dist/modules/src/source.d.ts +3 -3
  103. package/dist/modules/src/source.d.ts.map +1 -1
  104. package/dist/modules/src/subtitles.d.ts +1 -1
  105. package/dist/modules/src/subtitles.d.ts.map +1 -1
  106. package/dist/modules/src/target.d.ts +2 -2
  107. package/dist/modules/src/target.d.ts.map +1 -1
  108. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  109. package/dist/modules/src/wave/riff-writer.d.ts +1 -1
  110. package/dist/modules/src/wave/riff-writer.d.ts.map +1 -1
  111. package/dist/modules/src/wave/wave-demuxer.d.ts +6 -6
  112. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  113. package/dist/modules/src/wave/wave-muxer.d.ts +4 -4
  114. package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
  115. package/dist/modules/src/writer.d.ts +1 -1
  116. package/dist/modules/src/writer.d.ts.map +1 -1
  117. package/dist/packages/eac3/eac3.wasm +0 -0
  118. package/dist/packages/eac3/mediabunny-eac3.js +1058 -0
  119. package/dist/packages/eac3/mediabunny-eac3.min.js +44 -0
  120. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.js +694 -0
  121. package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.min.js +58 -0
  122. package/dist/packages/mpeg4/mediabunny-mpeg4.js +1198 -0
  123. package/dist/packages/mpeg4/mediabunny-mpeg4.min.js +44 -0
  124. package/dist/packages/mpeg4/xvid.wasm +0 -0
  125. package/package.json +18 -57
  126. package/dist/bundles/mediabunny.cjs +0 -26140
  127. package/dist/bundles/mediabunny.min.cjs +0 -147
  128. package/dist/bundles/mediabunny.min.mjs +0 -146
  129. package/dist/mediabunny.d.ts +0 -3319
  130. package/dist/modules/shared/mp3-misc.js +0 -147
  131. package/dist/modules/src/adts/adts-demuxer.js +0 -239
  132. package/dist/modules/src/adts/adts-muxer.js +0 -80
  133. package/dist/modules/src/adts/adts-reader.js +0 -63
  134. package/dist/modules/src/codec-data.js +0 -1730
  135. package/dist/modules/src/codec.js +0 -869
  136. package/dist/modules/src/conversion.js +0 -1459
  137. package/dist/modules/src/custom-coder.js +0 -117
  138. package/dist/modules/src/demuxer.js +0 -12
  139. package/dist/modules/src/encode.js +0 -442
  140. package/dist/modules/src/flac/flac-demuxer.js +0 -504
  141. package/dist/modules/src/flac/flac-misc.js +0 -135
  142. package/dist/modules/src/flac/flac-muxer.js +0 -222
  143. package/dist/modules/src/id3.js +0 -848
  144. package/dist/modules/src/index.js +0 -28
  145. package/dist/modules/src/input-format.js +0 -480
  146. package/dist/modules/src/input-track.js +0 -372
  147. package/dist/modules/src/input.js +0 -188
  148. package/dist/modules/src/isobmff/isobmff-boxes.js +0 -1480
  149. package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -2618
  150. package/dist/modules/src/isobmff/isobmff-misc.js +0 -20
  151. package/dist/modules/src/isobmff/isobmff-muxer.js +0 -966
  152. package/dist/modules/src/isobmff/isobmff-reader.js +0 -72
  153. package/dist/modules/src/matroska/ebml.js +0 -653
  154. package/dist/modules/src/matroska/matroska-demuxer.js +0 -2133
  155. package/dist/modules/src/matroska/matroska-misc.js +0 -20
  156. package/dist/modules/src/matroska/matroska-muxer.js +0 -1017
  157. package/dist/modules/src/media-sink.js +0 -1736
  158. package/dist/modules/src/media-source.js +0 -1825
  159. package/dist/modules/src/metadata.js +0 -193
  160. package/dist/modules/src/misc.js +0 -623
  161. package/dist/modules/src/mp3/mp3-demuxer.js +0 -285
  162. package/dist/modules/src/mp3/mp3-muxer.js +0 -123
  163. package/dist/modules/src/mp3/mp3-reader.js +0 -26
  164. package/dist/modules/src/mp3/mp3-writer.js +0 -78
  165. package/dist/modules/src/muxer.js +0 -50
  166. package/dist/modules/src/node.js +0 -9
  167. package/dist/modules/src/ogg/ogg-demuxer.js +0 -763
  168. package/dist/modules/src/ogg/ogg-misc.js +0 -78
  169. package/dist/modules/src/ogg/ogg-muxer.js +0 -353
  170. package/dist/modules/src/ogg/ogg-reader.js +0 -65
  171. package/dist/modules/src/output-format.js +0 -527
  172. package/dist/modules/src/output.js +0 -300
  173. package/dist/modules/src/packet.js +0 -182
  174. package/dist/modules/src/pcm.js +0 -85
  175. package/dist/modules/src/reader.js +0 -236
  176. package/dist/modules/src/sample.js +0 -1056
  177. package/dist/modules/src/source.js +0 -1182
  178. package/dist/modules/src/subtitles.js +0 -575
  179. package/dist/modules/src/target.js +0 -140
  180. package/dist/modules/src/wave/riff-writer.js +0 -30
  181. package/dist/modules/src/wave/wave-demuxer.js +0 -447
  182. package/dist/modules/src/wave/wave-muxer.js +0 -318
  183. package/dist/modules/src/writer.js +0 -370
  184. package/src/adts/adts-demuxer.ts +0 -331
  185. package/src/adts/adts-muxer.ts +0 -111
  186. package/src/adts/adts-reader.ts +0 -85
  187. package/src/codec-data.ts +0 -2078
  188. package/src/codec.ts +0 -1092
  189. package/src/conversion.ts +0 -2112
  190. package/src/custom-coder.ts +0 -197
  191. package/src/demuxer.ts +0 -24
  192. package/src/encode.ts +0 -739
  193. package/src/flac/flac-demuxer.ts +0 -730
  194. package/src/flac/flac-misc.ts +0 -164
  195. package/src/flac/flac-muxer.ts +0 -320
  196. package/src/id3.ts +0 -925
  197. package/src/index.ts +0 -221
  198. package/src/input-format.ts +0 -541
  199. package/src/input-track.ts +0 -529
  200. package/src/input.ts +0 -235
  201. package/src/isobmff/isobmff-boxes.ts +0 -1719
  202. package/src/isobmff/isobmff-demuxer.ts +0 -3190
  203. package/src/isobmff/isobmff-misc.ts +0 -29
  204. package/src/isobmff/isobmff-muxer.ts +0 -1348
  205. package/src/isobmff/isobmff-reader.ts +0 -91
  206. package/src/matroska/ebml.ts +0 -730
  207. package/src/matroska/matroska-demuxer.ts +0 -2481
  208. package/src/matroska/matroska-misc.ts +0 -29
  209. package/src/matroska/matroska-muxer.ts +0 -1276
  210. package/src/media-sink.ts +0 -2179
  211. package/src/media-source.ts +0 -2243
  212. package/src/metadata.ts +0 -320
  213. package/src/misc.ts +0 -798
  214. package/src/mp3/mp3-demuxer.ts +0 -383
  215. package/src/mp3/mp3-muxer.ts +0 -166
  216. package/src/mp3/mp3-reader.ts +0 -34
  217. package/src/mp3/mp3-writer.ts +0 -120
  218. package/src/muxer.ts +0 -88
  219. package/src/node.ts +0 -11
  220. package/src/ogg/ogg-demuxer.ts +0 -1053
  221. package/src/ogg/ogg-misc.ts +0 -116
  222. package/src/ogg/ogg-muxer.ts +0 -497
  223. package/src/ogg/ogg-reader.ts +0 -93
  224. package/src/output-format.ts +0 -945
  225. package/src/output.ts +0 -488
  226. package/src/packet.ts +0 -263
  227. package/src/pcm.ts +0 -112
  228. package/src/reader.ts +0 -323
  229. package/src/sample.ts +0 -1461
  230. package/src/source.ts +0 -1688
  231. package/src/subtitles.ts +0 -711
  232. package/src/target.ts +0 -204
  233. package/src/tsconfig.json +0 -16
  234. package/src/wave/riff-writer.ts +0 -36
  235. package/src/wave/wave-demuxer.ts +0 -529
  236. package/src/wave/wave-muxer.ts +0 -371
  237. package/src/writer.ts +0 -490
@@ -1,1730 +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 { VP9_LEVEL_TABLE } from './codec.js';
9
- import { assert, assertNever, base64ToBytes, Bitstream, bytesToBase64, keyValueIterator, getUint24, last, readExpGolomb, readSignedExpGolomb, textDecoder, textEncoder, toDataView, toUint8Array, } from './misc.js';
10
- // References for AVC/HEVC code:
11
- // ISO 14496-15
12
- // Rec. ITU-T H.264
13
- // Rec. ITU-T H.265
14
- // https://stackoverflow.com/questions/24884827
15
- export var AvcNalUnitType;
16
- (function (AvcNalUnitType) {
17
- AvcNalUnitType[AvcNalUnitType["IDR"] = 5] = "IDR";
18
- AvcNalUnitType[AvcNalUnitType["SPS"] = 7] = "SPS";
19
- AvcNalUnitType[AvcNalUnitType["PPS"] = 8] = "PPS";
20
- AvcNalUnitType[AvcNalUnitType["SPS_EXT"] = 13] = "SPS_EXT";
21
- })(AvcNalUnitType || (AvcNalUnitType = {}));
22
- export var HevcNalUnitType;
23
- (function (HevcNalUnitType) {
24
- HevcNalUnitType[HevcNalUnitType["RASL_N"] = 8] = "RASL_N";
25
- HevcNalUnitType[HevcNalUnitType["RASL_R"] = 9] = "RASL_R";
26
- HevcNalUnitType[HevcNalUnitType["BLA_W_LP"] = 16] = "BLA_W_LP";
27
- HevcNalUnitType[HevcNalUnitType["RSV_IRAP_VCL23"] = 23] = "RSV_IRAP_VCL23";
28
- HevcNalUnitType[HevcNalUnitType["VPS_NUT"] = 32] = "VPS_NUT";
29
- HevcNalUnitType[HevcNalUnitType["SPS_NUT"] = 33] = "SPS_NUT";
30
- HevcNalUnitType[HevcNalUnitType["PPS_NUT"] = 34] = "PPS_NUT";
31
- HevcNalUnitType[HevcNalUnitType["PREFIX_SEI_NUT"] = 39] = "PREFIX_SEI_NUT";
32
- HevcNalUnitType[HevcNalUnitType["SUFFIX_SEI_NUT"] = 40] = "SUFFIX_SEI_NUT";
33
- })(HevcNalUnitType || (HevcNalUnitType = {}));
34
- /** Finds all NAL units in an AVC packet in Annex B format. */
35
- export const findNalUnitsInAnnexB = (packetData) => {
36
- const nalUnits = [];
37
- let i = 0;
38
- while (i < packetData.length) {
39
- let startCodePos = -1;
40
- let startCodeLength = 0;
41
- for (let j = i; j < packetData.length - 3; j++) {
42
- // Check for 3-byte start code (0x000001)
43
- if (packetData[j] === 0 && packetData[j + 1] === 0 && packetData[j + 2] === 1) {
44
- startCodePos = j;
45
- startCodeLength = 3;
46
- break;
47
- }
48
- // Check for 4-byte start code (0x00000001)
49
- if (j < packetData.length - 4
50
- && packetData[j] === 0
51
- && packetData[j + 1] === 0
52
- && packetData[j + 2] === 0
53
- && packetData[j + 3] === 1) {
54
- startCodePos = j;
55
- startCodeLength = 4;
56
- break;
57
- }
58
- }
59
- if (startCodePos === -1) {
60
- break; // No more start codes found
61
- }
62
- // If this isn't the first start code, extract the previous NAL unit
63
- if (i > 0 && startCodePos > i) {
64
- const nalData = packetData.subarray(i, startCodePos);
65
- if (nalData.length > 0) {
66
- nalUnits.push(nalData);
67
- }
68
- }
69
- i = startCodePos + startCodeLength;
70
- }
71
- // Extract the last NAL unit if there is one
72
- if (i < packetData.length) {
73
- const nalData = packetData.subarray(i);
74
- if (nalData.length > 0) {
75
- nalUnits.push(nalData);
76
- }
77
- }
78
- return nalUnits;
79
- };
80
- /** Finds all NAL units in an AVC packet in length-prefixed format. */
81
- const findNalUnitsInLengthPrefixed = (packetData, lengthSize) => {
82
- const nalUnits = [];
83
- let offset = 0;
84
- const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength);
85
- while (offset + lengthSize <= packetData.length) {
86
- let nalUnitLength;
87
- if (lengthSize === 1) {
88
- nalUnitLength = dataView.getUint8(offset);
89
- }
90
- else if (lengthSize === 2) {
91
- nalUnitLength = dataView.getUint16(offset, false);
92
- }
93
- else if (lengthSize === 3) {
94
- nalUnitLength = getUint24(dataView, offset, false);
95
- }
96
- else if (lengthSize === 4) {
97
- nalUnitLength = dataView.getUint32(offset, false);
98
- }
99
- else {
100
- assertNever(lengthSize);
101
- assert(false);
102
- }
103
- offset += lengthSize;
104
- const nalUnit = packetData.subarray(offset, offset + nalUnitLength);
105
- nalUnits.push(nalUnit);
106
- offset += nalUnitLength;
107
- }
108
- return nalUnits;
109
- };
110
- const removeEmulationPreventionBytes = (data) => {
111
- const result = [];
112
- const len = data.length;
113
- for (let i = 0; i < len; i++) {
114
- // Look for the 0x000003 pattern
115
- if (i + 2 < len && data[i] === 0x00 && data[i + 1] === 0x00 && data[i + 2] === 0x03) {
116
- result.push(0x00, 0x00); // Push the first two bytes
117
- i += 2; // Skip the 0x03 byte
118
- }
119
- else {
120
- result.push(data[i]);
121
- }
122
- }
123
- return new Uint8Array(result);
124
- };
125
- /** Converts an AVC packet in Annex B format to length-prefixed format. */
126
- export const transformAnnexBToLengthPrefixed = (packetData) => {
127
- const NAL_UNIT_LENGTH_SIZE = 4;
128
- const nalUnits = findNalUnitsInAnnexB(packetData);
129
- if (nalUnits.length === 0) {
130
- // If no NAL units were found, it's not valid Annex B data
131
- return null;
132
- }
133
- let totalSize = 0;
134
- for (const nalUnit of nalUnits) {
135
- totalSize += NAL_UNIT_LENGTH_SIZE + nalUnit.byteLength;
136
- }
137
- const avccData = new Uint8Array(totalSize);
138
- const dataView = new DataView(avccData.buffer);
139
- let offset = 0;
140
- // Write each NAL unit with its length prefix
141
- for (const nalUnit of nalUnits) {
142
- const length = nalUnit.byteLength;
143
- dataView.setUint32(offset, length, false);
144
- offset += 4;
145
- avccData.set(nalUnit, offset);
146
- offset += nalUnit.byteLength;
147
- }
148
- return avccData;
149
- };
150
- export const extractAvcNalUnits = (packetData, decoderConfig) => {
151
- if (decoderConfig.description) {
152
- // Stream is length-prefixed. Let's extract the size of the length prefix from the decoder config
153
- const bytes = toUint8Array(decoderConfig.description);
154
- const lengthSizeMinusOne = bytes[4] & 0b11;
155
- const lengthSize = (lengthSizeMinusOne + 1);
156
- return findNalUnitsInLengthPrefixed(packetData, lengthSize);
157
- }
158
- else {
159
- // Stream is in Annex B format
160
- return findNalUnitsInAnnexB(packetData);
161
- }
162
- };
163
- const extractNalUnitTypeForAvc = (data) => {
164
- return data[0] & 0x1F;
165
- };
166
- /** Builds an AvcDecoderConfigurationRecord from an AVC packet in Annex B format. */
167
- export const extractAvcDecoderConfigurationRecord = (packetData) => {
168
- try {
169
- const nalUnits = findNalUnitsInAnnexB(packetData);
170
- const spsUnits = nalUnits.filter(unit => extractNalUnitTypeForAvc(unit) === AvcNalUnitType.SPS);
171
- const ppsUnits = nalUnits.filter(unit => extractNalUnitTypeForAvc(unit) === AvcNalUnitType.PPS);
172
- const spsExtUnits = nalUnits.filter(unit => extractNalUnitTypeForAvc(unit) === AvcNalUnitType.SPS_EXT);
173
- if (spsUnits.length === 0) {
174
- return null;
175
- }
176
- if (ppsUnits.length === 0) {
177
- return null;
178
- }
179
- // Let's get the first SPS for profile and level information
180
- const spsData = spsUnits[0];
181
- const spsInfo = parseAvcSps(spsData);
182
- assert(spsInfo !== null);
183
- const hasExtendedData = spsInfo.profileIdc === 100
184
- || spsInfo.profileIdc === 110
185
- || spsInfo.profileIdc === 122
186
- || spsInfo.profileIdc === 144;
187
- return {
188
- configurationVersion: 1,
189
- avcProfileIndication: spsInfo.profileIdc,
190
- profileCompatibility: spsInfo.constraintFlags,
191
- avcLevelIndication: spsInfo.levelIdc,
192
- lengthSizeMinusOne: 3, // Typically 4 bytes for length field
193
- sequenceParameterSets: spsUnits,
194
- pictureParameterSets: ppsUnits,
195
- chromaFormat: hasExtendedData ? spsInfo.chromaFormatIdc : null,
196
- bitDepthLumaMinus8: hasExtendedData ? spsInfo.bitDepthLumaMinus8 : null,
197
- bitDepthChromaMinus8: hasExtendedData ? spsInfo.bitDepthChromaMinus8 : null,
198
- sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null,
199
- };
200
- }
201
- catch (error) {
202
- console.error('Error building AVC Decoder Configuration Record:', error);
203
- return null;
204
- }
205
- };
206
- /** Serializes an AvcDecoderConfigurationRecord into the format specified in Section 5.3.3.1 of ISO 14496-15. */
207
- export const serializeAvcDecoderConfigurationRecord = (record) => {
208
- const bytes = [];
209
- // Write header
210
- bytes.push(record.configurationVersion);
211
- bytes.push(record.avcProfileIndication);
212
- bytes.push(record.profileCompatibility);
213
- bytes.push(record.avcLevelIndication);
214
- bytes.push(0xFC | (record.lengthSizeMinusOne & 0x03)); // Reserved bits (6) + lengthSizeMinusOne (2)
215
- // Reserved bits (3) + numOfSequenceParameterSets (5)
216
- bytes.push(0xE0 | (record.sequenceParameterSets.length & 0x1F));
217
- // Write SPS
218
- for (const sps of record.sequenceParameterSets) {
219
- const length = sps.byteLength;
220
- bytes.push(length >> 8); // High byte
221
- bytes.push(length & 0xFF); // Low byte
222
- for (let i = 0; i < length; i++) {
223
- bytes.push(sps[i]);
224
- }
225
- }
226
- bytes.push(record.pictureParameterSets.length);
227
- // Write PPS
228
- for (const pps of record.pictureParameterSets) {
229
- const length = pps.byteLength;
230
- bytes.push(length >> 8); // High byte
231
- bytes.push(length & 0xFF); // Low byte
232
- for (let i = 0; i < length; i++) {
233
- bytes.push(pps[i]);
234
- }
235
- }
236
- if (record.avcProfileIndication === 100
237
- || record.avcProfileIndication === 110
238
- || record.avcProfileIndication === 122
239
- || record.avcProfileIndication === 144) {
240
- assert(record.chromaFormat !== null);
241
- assert(record.bitDepthLumaMinus8 !== null);
242
- assert(record.bitDepthChromaMinus8 !== null);
243
- assert(record.sequenceParameterSetExt !== null);
244
- bytes.push(0xFC | (record.chromaFormat & 0x03)); // Reserved bits + chroma_format
245
- bytes.push(0xF8 | (record.bitDepthLumaMinus8 & 0x07)); // Reserved bits + bit_depth_luma_minus8
246
- bytes.push(0xF8 | (record.bitDepthChromaMinus8 & 0x07)); // Reserved bits + bit_depth_chroma_minus8
247
- bytes.push(record.sequenceParameterSetExt.length);
248
- // Write SPS Ext
249
- for (const spsExt of record.sequenceParameterSetExt) {
250
- const length = spsExt.byteLength;
251
- bytes.push(length >> 8); // High byte
252
- bytes.push(length & 0xFF); // Low byte
253
- for (let i = 0; i < length; i++) {
254
- bytes.push(spsExt[i]);
255
- }
256
- }
257
- }
258
- return new Uint8Array(bytes);
259
- };
260
- /** Deserializes an AvcDecoderConfigurationRecord from the format specified in Section 5.3.3.1 of ISO 14496-15. */
261
- export const deserializeAvcDecoderConfigurationRecord = (data) => {
262
- try {
263
- const view = toDataView(data);
264
- let offset = 0;
265
- // Read header
266
- const configurationVersion = view.getUint8(offset++);
267
- const avcProfileIndication = view.getUint8(offset++);
268
- const profileCompatibility = view.getUint8(offset++);
269
- const avcLevelIndication = view.getUint8(offset++);
270
- const lengthSizeMinusOne = view.getUint8(offset++) & 0x03;
271
- const numOfSequenceParameterSets = view.getUint8(offset++) & 0x1F;
272
- // Read SPS
273
- const sequenceParameterSets = [];
274
- for (let i = 0; i < numOfSequenceParameterSets; i++) {
275
- const length = view.getUint16(offset, false);
276
- offset += 2;
277
- sequenceParameterSets.push(data.subarray(offset, offset + length));
278
- offset += length;
279
- }
280
- const numOfPictureParameterSets = view.getUint8(offset++);
281
- // Read PPS
282
- const pictureParameterSets = [];
283
- for (let i = 0; i < numOfPictureParameterSets; i++) {
284
- const length = view.getUint16(offset, false);
285
- offset += 2;
286
- pictureParameterSets.push(data.subarray(offset, offset + length));
287
- offset += length;
288
- }
289
- const record = {
290
- configurationVersion,
291
- avcProfileIndication,
292
- profileCompatibility,
293
- avcLevelIndication,
294
- lengthSizeMinusOne,
295
- sequenceParameterSets,
296
- pictureParameterSets,
297
- chromaFormat: null,
298
- bitDepthLumaMinus8: null,
299
- bitDepthChromaMinus8: null,
300
- sequenceParameterSetExt: null,
301
- };
302
- // Check if there are extended profile fields
303
- if ((avcProfileIndication === 100
304
- || avcProfileIndication === 110
305
- || avcProfileIndication === 122
306
- || avcProfileIndication === 144)
307
- && offset + 4 <= data.length) {
308
- const chromaFormat = view.getUint8(offset++) & 0x03;
309
- const bitDepthLumaMinus8 = view.getUint8(offset++) & 0x07;
310
- const bitDepthChromaMinus8 = view.getUint8(offset++) & 0x07;
311
- const numOfSequenceParameterSetExt = view.getUint8(offset++);
312
- record.chromaFormat = chromaFormat;
313
- record.bitDepthLumaMinus8 = bitDepthLumaMinus8;
314
- record.bitDepthChromaMinus8 = bitDepthChromaMinus8;
315
- // Read SPS Ext
316
- const sequenceParameterSetExt = [];
317
- for (let i = 0; i < numOfSequenceParameterSetExt; i++) {
318
- const length = view.getUint16(offset, false);
319
- offset += 2;
320
- sequenceParameterSetExt.push(data.subarray(offset, offset + length));
321
- offset += length;
322
- }
323
- record.sequenceParameterSetExt = sequenceParameterSetExt;
324
- }
325
- return record;
326
- }
327
- catch (error) {
328
- console.error('Error deserializing AVC Decoder Configuration Record:', error);
329
- return null;
330
- }
331
- };
332
- /** Parses an AVC SPS (Sequence Parameter Set) to extract basic information. */
333
- export const parseAvcSps = (sps) => {
334
- try {
335
- const bitstream = new Bitstream(removeEmulationPreventionBytes(sps));
336
- bitstream.skipBits(1); // forbidden_zero_bit
337
- bitstream.skipBits(2); // nal_ref_idc
338
- const nalUnitType = bitstream.readBits(5);
339
- if (nalUnitType !== 7) { // SPS NAL unit type is 7
340
- return null;
341
- }
342
- const profileIdc = bitstream.readAlignedByte();
343
- const constraintFlags = bitstream.readAlignedByte();
344
- const levelIdc = bitstream.readAlignedByte();
345
- readExpGolomb(bitstream); // seq_parameter_set_id
346
- let chromaFormatIdc = null;
347
- let bitDepthLumaMinus8 = null;
348
- let bitDepthChromaMinus8 = null;
349
- // Handle high profile chroma_format_idc
350
- if (profileIdc === 100
351
- || profileIdc === 110
352
- || profileIdc === 122
353
- || profileIdc === 244
354
- || profileIdc === 44
355
- || profileIdc === 83
356
- || profileIdc === 86
357
- || profileIdc === 118
358
- || profileIdc === 128) {
359
- chromaFormatIdc = readExpGolomb(bitstream);
360
- if (chromaFormatIdc === 3) {
361
- bitstream.skipBits(1); // separate_colour_plane_flag
362
- }
363
- bitDepthLumaMinus8 = readExpGolomb(bitstream);
364
- bitDepthChromaMinus8 = readExpGolomb(bitstream);
365
- bitstream.skipBits(1); // qpprime_y_zero_transform_bypass_flag
366
- const seqScalingMatrixPresentFlag = bitstream.readBits(1);
367
- if (seqScalingMatrixPresentFlag) {
368
- for (let i = 0; i < (chromaFormatIdc !== 3 ? 8 : 12); i++) {
369
- const seqScalingListPresentFlag = bitstream.readBits(1);
370
- if (seqScalingListPresentFlag) {
371
- const sizeOfScalingList = i < 6 ? 16 : 64;
372
- let lastScale = 8;
373
- let nextScale = 8;
374
- for (let j = 0; j < sizeOfScalingList; j++) {
375
- if (nextScale !== 0) {
376
- const deltaScale = readSignedExpGolomb(bitstream);
377
- nextScale = (lastScale + deltaScale + 256) % 256;
378
- }
379
- lastScale = nextScale === 0 ? lastScale : nextScale;
380
- }
381
- }
382
- }
383
- }
384
- }
385
- readExpGolomb(bitstream); // log2_max_frame_num_minus4
386
- const picOrderCntType = readExpGolomb(bitstream);
387
- if (picOrderCntType === 0) {
388
- readExpGolomb(bitstream); // log2_max_pic_order_cnt_lsb_minus4
389
- }
390
- else if (picOrderCntType === 1) {
391
- bitstream.skipBits(1); // delta_pic_order_always_zero_flag
392
- readSignedExpGolomb(bitstream); // offset_for_non_ref_pic
393
- readSignedExpGolomb(bitstream); // offset_for_top_to_bottom_field
394
- const numRefFramesInPicOrderCntCycle = readExpGolomb(bitstream);
395
- for (let i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
396
- readSignedExpGolomb(bitstream); // offset_for_ref_frame[i]
397
- }
398
- }
399
- readExpGolomb(bitstream); // max_num_ref_frames
400
- bitstream.skipBits(1); // gaps_in_frame_num_value_allowed_flag
401
- readExpGolomb(bitstream); // pic_width_in_mbs_minus1
402
- readExpGolomb(bitstream); // pic_height_in_map_units_minus1
403
- const frameMbsOnlyFlag = bitstream.readBits(1);
404
- return {
405
- profileIdc,
406
- constraintFlags,
407
- levelIdc,
408
- frameMbsOnlyFlag,
409
- chromaFormatIdc,
410
- bitDepthLumaMinus8,
411
- bitDepthChromaMinus8,
412
- };
413
- }
414
- catch (error) {
415
- console.error('Error parsing AVC SPS:', error);
416
- return null;
417
- }
418
- };
419
- export const extractHevcNalUnits = (packetData, decoderConfig) => {
420
- if (decoderConfig.description) {
421
- // Stream is length-prefixed. Let's extract the size of the length prefix from the decoder config
422
- const bytes = toUint8Array(decoderConfig.description);
423
- const lengthSizeMinusOne = bytes[21] & 0b11;
424
- const lengthSize = (lengthSizeMinusOne + 1);
425
- return findNalUnitsInLengthPrefixed(packetData, lengthSize);
426
- }
427
- else {
428
- // Stream is in Annex B format
429
- return findNalUnitsInAnnexB(packetData);
430
- }
431
- };
432
- export const extractNalUnitTypeForHevc = (data) => {
433
- return (data[0] >> 1) & 0x3F;
434
- };
435
- /** Builds a HevcDecoderConfigurationRecord from an HEVC packet in Annex B format. */
436
- export const extractHevcDecoderConfigurationRecord = (packetData) => {
437
- try {
438
- const nalUnits = findNalUnitsInAnnexB(packetData);
439
- const vpsUnits = nalUnits.filter(unit => extractNalUnitTypeForHevc(unit) === HevcNalUnitType.VPS_NUT);
440
- const spsUnits = nalUnits.filter(unit => extractNalUnitTypeForHevc(unit) === HevcNalUnitType.SPS_NUT);
441
- const ppsUnits = nalUnits.filter(unit => extractNalUnitTypeForHevc(unit) === HevcNalUnitType.PPS_NUT);
442
- const seiUnits = nalUnits.filter(unit => extractNalUnitTypeForHevc(unit) === HevcNalUnitType.PREFIX_SEI_NUT
443
- || extractNalUnitTypeForHevc(unit) === HevcNalUnitType.SUFFIX_SEI_NUT);
444
- if (spsUnits.length === 0 || ppsUnits.length === 0)
445
- return null;
446
- const sps = spsUnits[0];
447
- const bitstream = new Bitstream(removeEmulationPreventionBytes(sps));
448
- bitstream.skipBits(16); // NAL header
449
- bitstream.readBits(4); // sps_video_parameter_set_id
450
- const sps_max_sub_layers_minus1 = bitstream.readBits(3);
451
- const sps_temporal_id_nesting_flag = bitstream.readBits(1);
452
- const { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc, } = parseProfileTierLevel(bitstream, sps_max_sub_layers_minus1);
453
- readExpGolomb(bitstream); // sps_seq_parameter_set_id
454
- const chroma_format_idc = readExpGolomb(bitstream);
455
- if (chroma_format_idc === 3)
456
- bitstream.skipBits(1); // separate_colour_plane_flag
457
- readExpGolomb(bitstream); // pic_width_in_luma_samples
458
- readExpGolomb(bitstream); // pic_height_in_luma_samples
459
- if (bitstream.readBits(1)) { // conformance_window_flag
460
- readExpGolomb(bitstream); // conf_win_left_offset
461
- readExpGolomb(bitstream); // conf_win_right_offset
462
- readExpGolomb(bitstream); // conf_win_top_offset
463
- readExpGolomb(bitstream); // conf_win_bottom_offset
464
- }
465
- const bit_depth_luma_minus8 = readExpGolomb(bitstream);
466
- const bit_depth_chroma_minus8 = readExpGolomb(bitstream);
467
- readExpGolomb(bitstream); // log2_max_pic_order_cnt_lsb_minus4
468
- const sps_sub_layer_ordering_info_present_flag = bitstream.readBits(1);
469
- const maxNum = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1;
470
- for (let i = maxNum; i <= sps_max_sub_layers_minus1; i++) {
471
- readExpGolomb(bitstream); // sps_max_dec_pic_buffering_minus1[i]
472
- readExpGolomb(bitstream); // sps_max_num_reorder_pics[i]
473
- readExpGolomb(bitstream); // sps_max_latency_increase_plus1[i]
474
- }
475
- readExpGolomb(bitstream); // log2_min_luma_coding_block_size_minus3
476
- readExpGolomb(bitstream); // log2_diff_max_min_luma_coding_block_size
477
- readExpGolomb(bitstream); // log2_min_luma_transform_block_size_minus2
478
- readExpGolomb(bitstream); // log2_diff_max_min_luma_transform_block_size
479
- readExpGolomb(bitstream); // max_transform_hierarchy_depth_inter
480
- readExpGolomb(bitstream); // max_transform_hierarchy_depth_intra
481
- if (bitstream.readBits(1)) { // scaling_list_enabled_flag
482
- if (bitstream.readBits(1)) {
483
- skipScalingListData(bitstream);
484
- }
485
- }
486
- bitstream.skipBits(1); // amp_enabled_flag
487
- bitstream.skipBits(1); // sample_adaptive_offset_enabled_flag
488
- if (bitstream.readBits(1)) { // pcm_enabled_flag
489
- bitstream.skipBits(4); // pcm_sample_bit_depth_luma_minus1
490
- bitstream.skipBits(4); // pcm_sample_bit_depth_chroma_minus1
491
- readExpGolomb(bitstream); // log2_min_pcm_luma_coding_block_size_minus3
492
- readExpGolomb(bitstream); // log2_diff_max_min_pcm_luma_coding_block_size
493
- bitstream.skipBits(1); // pcm_loop_filter_disabled_flag
494
- }
495
- const num_short_term_ref_pic_sets = readExpGolomb(bitstream);
496
- skipAllStRefPicSets(bitstream, num_short_term_ref_pic_sets);
497
- if (bitstream.readBits(1)) { // long_term_ref_pics_present_flag
498
- const num_long_term_ref_pics_sps = readExpGolomb(bitstream);
499
- for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
500
- readExpGolomb(bitstream); // lt_ref_pic_poc_lsb_sps[i]
501
- bitstream.skipBits(1); // used_by_curr_pic_lt_sps_flag[i]
502
- }
503
- }
504
- bitstream.skipBits(1); // sps_temporal_mvp_enabled_flag
505
- bitstream.skipBits(1); // strong_intra_smoothing_enabled_flag
506
- let min_spatial_segmentation_idc = 0;
507
- if (bitstream.readBits(1)) { // vui_parameters_present_flag
508
- min_spatial_segmentation_idc = parseVuiForMinSpatialSegmentationIdc(bitstream, sps_max_sub_layers_minus1);
509
- }
510
- // Parse PPS for parallelismType
511
- let parallelismType = 0;
512
- if (ppsUnits.length > 0) {
513
- const pps = ppsUnits[0];
514
- const ppsBitstream = new Bitstream(removeEmulationPreventionBytes(pps));
515
- ppsBitstream.skipBits(16); // NAL header
516
- readExpGolomb(ppsBitstream); // pps_pic_parameter_set_id
517
- readExpGolomb(ppsBitstream); // pps_seq_parameter_set_id
518
- ppsBitstream.skipBits(1); // dependent_slice_segments_enabled_flag
519
- ppsBitstream.skipBits(1); // output_flag_present_flag
520
- ppsBitstream.skipBits(3); // num_extra_slice_header_bits
521
- ppsBitstream.skipBits(1); // sign_data_hiding_enabled_flag
522
- ppsBitstream.skipBits(1); // cabac_init_present_flag
523
- readExpGolomb(ppsBitstream); // num_ref_idx_l0_default_active_minus1
524
- readExpGolomb(ppsBitstream); // num_ref_idx_l1_default_active_minus1
525
- readSignedExpGolomb(ppsBitstream); // init_qp_minus26
526
- ppsBitstream.skipBits(1); // constrained_intra_pred_flag
527
- ppsBitstream.skipBits(1); // transform_skip_enabled_flag
528
- if (ppsBitstream.readBits(1)) { // cu_qp_delta_enabled_flag
529
- readExpGolomb(ppsBitstream); // diff_cu_qp_delta_depth
530
- }
531
- readSignedExpGolomb(ppsBitstream); // pps_cb_qp_offset
532
- readSignedExpGolomb(ppsBitstream); // pps_cr_qp_offset
533
- ppsBitstream.skipBits(1); // pps_slice_chroma_qp_offsets_present_flag
534
- ppsBitstream.skipBits(1); // weighted_pred_flag
535
- ppsBitstream.skipBits(1); // weighted_bipred_flag
536
- ppsBitstream.skipBits(1); // transquant_bypass_enabled_flag
537
- const tiles_enabled_flag = ppsBitstream.readBits(1);
538
- const entropy_coding_sync_enabled_flag = ppsBitstream.readBits(1);
539
- if (!tiles_enabled_flag && !entropy_coding_sync_enabled_flag)
540
- parallelismType = 0;
541
- else if (tiles_enabled_flag && !entropy_coding_sync_enabled_flag)
542
- parallelismType = 2;
543
- else if (!tiles_enabled_flag && entropy_coding_sync_enabled_flag)
544
- parallelismType = 3;
545
- else
546
- parallelismType = 0;
547
- }
548
- const arrays = [
549
- ...(vpsUnits.length
550
- ? [
551
- {
552
- arrayCompleteness: 1,
553
- nalUnitType: HevcNalUnitType.VPS_NUT,
554
- nalUnits: vpsUnits,
555
- },
556
- ]
557
- : []),
558
- ...(spsUnits.length
559
- ? [
560
- {
561
- arrayCompleteness: 1,
562
- nalUnitType: HevcNalUnitType.SPS_NUT,
563
- nalUnits: spsUnits,
564
- },
565
- ]
566
- : []),
567
- ...(ppsUnits.length
568
- ? [
569
- {
570
- arrayCompleteness: 1,
571
- nalUnitType: HevcNalUnitType.PPS_NUT,
572
- nalUnits: ppsUnits,
573
- },
574
- ]
575
- : []),
576
- ...(seiUnits.length
577
- ? [
578
- {
579
- arrayCompleteness: 1,
580
- nalUnitType: extractNalUnitTypeForHevc(seiUnits[0]),
581
- nalUnits: seiUnits,
582
- },
583
- ]
584
- : []),
585
- ];
586
- const record = {
587
- configurationVersion: 1,
588
- generalProfileSpace: general_profile_space,
589
- generalTierFlag: general_tier_flag,
590
- generalProfileIdc: general_profile_idc,
591
- generalProfileCompatibilityFlags: general_profile_compatibility_flags,
592
- generalConstraintIndicatorFlags: general_constraint_indicator_flags,
593
- generalLevelIdc: general_level_idc,
594
- minSpatialSegmentationIdc: min_spatial_segmentation_idc,
595
- parallelismType,
596
- chromaFormatIdc: chroma_format_idc,
597
- bitDepthLumaMinus8: bit_depth_luma_minus8,
598
- bitDepthChromaMinus8: bit_depth_chroma_minus8,
599
- avgFrameRate: 0,
600
- constantFrameRate: 0,
601
- numTemporalLayers: sps_max_sub_layers_minus1 + 1,
602
- temporalIdNested: sps_temporal_id_nesting_flag,
603
- lengthSizeMinusOne: 3,
604
- arrays,
605
- };
606
- return record;
607
- }
608
- catch (error) {
609
- console.error('Error building HEVC Decoder Configuration Record:', error);
610
- return null;
611
- }
612
- };
613
- const parseProfileTierLevel = (bitstream, maxNumSubLayersMinus1) => {
614
- const general_profile_space = bitstream.readBits(2);
615
- const general_tier_flag = bitstream.readBits(1);
616
- const general_profile_idc = bitstream.readBits(5);
617
- let general_profile_compatibility_flags = 0;
618
- for (let i = 0; i < 32; i++) {
619
- general_profile_compatibility_flags = (general_profile_compatibility_flags << 1) | bitstream.readBits(1);
620
- }
621
- const general_constraint_indicator_flags = new Uint8Array(6);
622
- for (let i = 0; i < 6; i++) {
623
- general_constraint_indicator_flags[i] = bitstream.readBits(8);
624
- }
625
- const general_level_idc = bitstream.readBits(8);
626
- const sub_layer_profile_present_flag = [];
627
- const sub_layer_level_present_flag = [];
628
- for (let i = 0; i < maxNumSubLayersMinus1; i++) {
629
- sub_layer_profile_present_flag.push(bitstream.readBits(1));
630
- sub_layer_level_present_flag.push(bitstream.readBits(1));
631
- }
632
- if (maxNumSubLayersMinus1 > 0) {
633
- for (let i = maxNumSubLayersMinus1; i < 8; i++) {
634
- bitstream.skipBits(2); // reserved_zero_2bits
635
- }
636
- }
637
- for (let i = 0; i < maxNumSubLayersMinus1; i++) {
638
- if (sub_layer_profile_present_flag[i])
639
- bitstream.skipBits(88);
640
- if (sub_layer_level_present_flag[i])
641
- bitstream.skipBits(8);
642
- }
643
- return {
644
- general_profile_space,
645
- general_tier_flag,
646
- general_profile_idc,
647
- general_profile_compatibility_flags,
648
- general_constraint_indicator_flags,
649
- general_level_idc,
650
- };
651
- };
652
- const skipScalingListData = (bitstream) => {
653
- for (let sizeId = 0; sizeId < 4; sizeId++) {
654
- for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {
655
- const scaling_list_pred_mode_flag = bitstream.readBits(1);
656
- if (!scaling_list_pred_mode_flag) {
657
- readExpGolomb(bitstream); // scaling_list_pred_matrix_id_delta
658
- }
659
- else {
660
- const coefNum = Math.min(64, 1 << (4 + (sizeId << 1)));
661
- if (sizeId > 1) {
662
- readSignedExpGolomb(bitstream); // scaling_list_dc_coef_minus8
663
- }
664
- for (let i = 0; i < coefNum; i++) {
665
- readSignedExpGolomb(bitstream); // scaling_list_delta_coef
666
- }
667
- }
668
- }
669
- }
670
- };
671
- const skipAllStRefPicSets = (bitstream, num_short_term_ref_pic_sets) => {
672
- const NumDeltaPocs = [];
673
- for (let stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; stRpsIdx++) {
674
- NumDeltaPocs[stRpsIdx] = skipStRefPicSet(bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs);
675
- }
676
- };
677
- const skipStRefPicSet = (bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs) => {
678
- let NumDeltaPocsThis = 0;
679
- let inter_ref_pic_set_prediction_flag = 0;
680
- let RefRpsIdx = 0;
681
- if (stRpsIdx !== 0) {
682
- inter_ref_pic_set_prediction_flag = bitstream.readBits(1);
683
- }
684
- if (inter_ref_pic_set_prediction_flag) {
685
- if (stRpsIdx === num_short_term_ref_pic_sets) {
686
- const delta_idx_minus1 = readExpGolomb(bitstream);
687
- RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);
688
- }
689
- else {
690
- RefRpsIdx = stRpsIdx - 1;
691
- }
692
- bitstream.readBits(1); // delta_rps_sign
693
- readExpGolomb(bitstream); // abs_delta_rps_minus1
694
- // The number of iterations is NumDeltaPocs[RefRpsIdx] + 1
695
- const numDelta = NumDeltaPocs[RefRpsIdx] ?? 0;
696
- for (let j = 0; j <= numDelta; j++) {
697
- const used_by_curr_pic_flag = bitstream.readBits(1);
698
- if (!used_by_curr_pic_flag) {
699
- bitstream.readBits(1); // use_delta_flag
700
- }
701
- }
702
- NumDeltaPocsThis = NumDeltaPocs[RefRpsIdx];
703
- }
704
- else {
705
- const num_negative_pics = readExpGolomb(bitstream);
706
- const num_positive_pics = readExpGolomb(bitstream);
707
- for (let i = 0; i < num_negative_pics; i++) {
708
- readExpGolomb(bitstream); // delta_poc_s0_minus1[i]
709
- bitstream.readBits(1); // used_by_curr_pic_s0_flag[i]
710
- }
711
- for (let i = 0; i < num_positive_pics; i++) {
712
- readExpGolomb(bitstream); // delta_poc_s1_minus1[i]
713
- bitstream.readBits(1); // used_by_curr_pic_s1_flag[i]
714
- }
715
- NumDeltaPocsThis = num_negative_pics + num_positive_pics;
716
- }
717
- return NumDeltaPocsThis;
718
- };
719
- const parseVuiForMinSpatialSegmentationIdc = (bitstream, sps_max_sub_layers_minus1) => {
720
- if (bitstream.readBits(1)) { // aspect_ratio_info_present_flag
721
- const aspect_ratio_idc = bitstream.readBits(8);
722
- if (aspect_ratio_idc === 255) {
723
- bitstream.readBits(16); // sar_width
724
- bitstream.readBits(16); // sar_height
725
- }
726
- }
727
- if (bitstream.readBits(1)) { // overscan_info_present_flag
728
- bitstream.readBits(1); // overscan_appropriate_flag
729
- }
730
- if (bitstream.readBits(1)) { // video_signal_type_present_flag
731
- bitstream.readBits(3); // video_format
732
- bitstream.readBits(1); // video_full_range_flag
733
- if (bitstream.readBits(1)) {
734
- bitstream.readBits(8); // colour_primaries
735
- bitstream.readBits(8); // transfer_characteristics
736
- bitstream.readBits(8); // matrix_coeffs
737
- }
738
- }
739
- if (bitstream.readBits(1)) { // chroma_loc_info_present_flag
740
- readExpGolomb(bitstream); // chroma_sample_loc_type_top_field
741
- readExpGolomb(bitstream); // chroma_sample_loc_type_bottom_field
742
- }
743
- bitstream.readBits(1); // neutral_chroma_indication_flag
744
- bitstream.readBits(1); // field_seq_flag
745
- bitstream.readBits(1); // frame_field_info_present_flag
746
- if (bitstream.readBits(1)) { // default_display_window_flag
747
- readExpGolomb(bitstream); // def_disp_win_left_offset
748
- readExpGolomb(bitstream); // def_disp_win_right_offset
749
- readExpGolomb(bitstream); // def_disp_win_top_offset
750
- readExpGolomb(bitstream); // def_disp_win_bottom_offset
751
- }
752
- if (bitstream.readBits(1)) { // vui_timing_info_present_flag
753
- bitstream.readBits(32); // vui_num_units_in_tick
754
- bitstream.readBits(32); // vui_time_scale
755
- if (bitstream.readBits(1)) { // vui_poc_proportional_to_timing_flag
756
- readExpGolomb(bitstream); // vui_num_ticks_poc_diff_one_minus1
757
- }
758
- if (bitstream.readBits(1)) {
759
- skipHrdParameters(bitstream, true, sps_max_sub_layers_minus1);
760
- }
761
- }
762
- if (bitstream.readBits(1)) { // bitstream_restriction_flag
763
- bitstream.readBits(1); // tiles_fixed_structure_flag
764
- bitstream.readBits(1); // motion_vectors_over_pic_boundaries_flag
765
- bitstream.readBits(1); // restricted_ref_pic_lists_flag
766
- const min_spatial_segmentation_idc = readExpGolomb(bitstream);
767
- // skip the rest
768
- readExpGolomb(bitstream); // max_bytes_per_pic_denom
769
- readExpGolomb(bitstream); // max_bits_per_min_cu_denom
770
- readExpGolomb(bitstream); // log2_max_mv_length_horizontal
771
- readExpGolomb(bitstream); // log2_max_mv_length_vertical
772
- return min_spatial_segmentation_idc;
773
- }
774
- return 0;
775
- };
776
- const skipHrdParameters = (bitstream, commonInfPresentFlag, maxNumSubLayersMinus1) => {
777
- let nal_hrd_parameters_present_flag = false;
778
- let vcl_hrd_parameters_present_flag = false;
779
- let sub_pic_hrd_params_present_flag = false;
780
- if (commonInfPresentFlag) {
781
- nal_hrd_parameters_present_flag = bitstream.readBits(1) === 1;
782
- vcl_hrd_parameters_present_flag = bitstream.readBits(1) === 1;
783
- if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
784
- sub_pic_hrd_params_present_flag = bitstream.readBits(1) === 1;
785
- if (sub_pic_hrd_params_present_flag) {
786
- bitstream.readBits(8); // tick_divisor_minus2
787
- bitstream.readBits(5); // du_cpb_removal_delay_increment_length_minus1
788
- bitstream.readBits(1); // sub_pic_cpb_params_in_pic_timing_sei_flag
789
- bitstream.readBits(5); // dpb_output_delay_du_length_minus1
790
- }
791
- bitstream.readBits(4); // bit_rate_scale
792
- bitstream.readBits(4); // cpb_size_scale
793
- if (sub_pic_hrd_params_present_flag) {
794
- bitstream.readBits(4); // cpb_size_du_scale
795
- }
796
- bitstream.readBits(5); // initial_cpb_removal_delay_length_minus1
797
- bitstream.readBits(5); // au_cpb_removal_delay_length_minus1
798
- bitstream.readBits(5); // dpb_output_delay_length_minus1
799
- }
800
- }
801
- for (let i = 0; i <= maxNumSubLayersMinus1; i++) {
802
- const fixed_pic_rate_general_flag = bitstream.readBits(1) === 1;
803
- let fixed_pic_rate_within_cvs_flag = true; // Default assumption if general is true
804
- if (!fixed_pic_rate_general_flag) {
805
- fixed_pic_rate_within_cvs_flag = bitstream.readBits(1) === 1;
806
- }
807
- let low_delay_hrd_flag = false; // Default assumption
808
- if (fixed_pic_rate_within_cvs_flag) {
809
- readExpGolomb(bitstream); // elemental_duration_in_tc_minus1[i]
810
- }
811
- else {
812
- low_delay_hrd_flag = bitstream.readBits(1) === 1;
813
- }
814
- let CpbCnt = 1; // Default if low_delay is true
815
- if (!low_delay_hrd_flag) {
816
- const cpb_cnt_minus1 = readExpGolomb(bitstream); // cpb_cnt_minus1[i]
817
- CpbCnt = cpb_cnt_minus1 + 1;
818
- }
819
- if (nal_hrd_parameters_present_flag) {
820
- skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);
821
- }
822
- if (vcl_hrd_parameters_present_flag) {
823
- skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);
824
- }
825
- }
826
- };
827
- const skipSubLayerHrdParameters = (bitstream, CpbCnt, sub_pic_hrd_params_present_flag) => {
828
- for (let i = 0; i < CpbCnt; i++) {
829
- readExpGolomb(bitstream); // bit_rate_value_minus1[i]
830
- readExpGolomb(bitstream); // cpb_size_value_minus1[i]
831
- if (sub_pic_hrd_params_present_flag) {
832
- readExpGolomb(bitstream); // cpb_size_du_value_minus1[i]
833
- readExpGolomb(bitstream); // bit_rate_du_value_minus1[i]
834
- }
835
- bitstream.readBits(1); // cbr_flag[i]
836
- }
837
- };
838
- /** Serializes an HevcDecoderConfigurationRecord into the format specified in Section 8.3.3.1 of ISO 14496-15. */
839
- export const serializeHevcDecoderConfigurationRecord = (record) => {
840
- const bytes = [];
841
- bytes.push(record.configurationVersion);
842
- bytes.push(((record.generalProfileSpace & 0x3) << 6)
843
- | ((record.generalTierFlag & 0x1) << 5)
844
- | (record.generalProfileIdc & 0x1F));
845
- bytes.push((record.generalProfileCompatibilityFlags >>> 24) & 0xFF);
846
- bytes.push((record.generalProfileCompatibilityFlags >>> 16) & 0xFF);
847
- bytes.push((record.generalProfileCompatibilityFlags >>> 8) & 0xFF);
848
- bytes.push(record.generalProfileCompatibilityFlags & 0xFF);
849
- bytes.push(...record.generalConstraintIndicatorFlags);
850
- bytes.push(record.generalLevelIdc & 0xFF);
851
- bytes.push(0xF0 | ((record.minSpatialSegmentationIdc >> 8) & 0x0F)); // Reserved + high nibble
852
- bytes.push(record.minSpatialSegmentationIdc & 0xFF); // Low byte
853
- bytes.push(0xFC | (record.parallelismType & 0x03));
854
- bytes.push(0xFC | (record.chromaFormatIdc & 0x03));
855
- bytes.push(0xF8 | (record.bitDepthLumaMinus8 & 0x07));
856
- bytes.push(0xF8 | (record.bitDepthChromaMinus8 & 0x07));
857
- bytes.push((record.avgFrameRate >> 8) & 0xFF); // High byte
858
- bytes.push(record.avgFrameRate & 0xFF); // Low byte
859
- bytes.push(((record.constantFrameRate & 0x03) << 6)
860
- | ((record.numTemporalLayers & 0x07) << 3)
861
- | ((record.temporalIdNested & 0x01) << 2)
862
- | (record.lengthSizeMinusOne & 0x03));
863
- bytes.push(record.arrays.length & 0xFF);
864
- for (const arr of record.arrays) {
865
- bytes.push(((arr.arrayCompleteness & 0x01) << 7)
866
- | (0 << 6)
867
- | (arr.nalUnitType & 0x3F));
868
- bytes.push((arr.nalUnits.length >> 8) & 0xFF); // High byte
869
- bytes.push(arr.nalUnits.length & 0xFF); // Low byte
870
- for (const nal of arr.nalUnits) {
871
- bytes.push((nal.length >> 8) & 0xFF); // High byte
872
- bytes.push(nal.length & 0xFF); // Low byte
873
- for (let i = 0; i < nal.length; i++) {
874
- bytes.push(nal[i]);
875
- }
876
- }
877
- }
878
- return new Uint8Array(bytes);
879
- };
880
- export const extractVp9CodecInfoFromPacket = (packet) => {
881
- // eslint-disable-next-line @stylistic/max-len
882
- // https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.7-20170222-draft.pdf
883
- // http://downloads.webmproject.org/docs/vp9/vp9-bitstream_superframe-and-uncompressed-header_v1.0.pdf
884
- const bitstream = new Bitstream(packet);
885
- // Frame marker (0b10)
886
- const frameMarker = bitstream.readBits(2);
887
- if (frameMarker !== 2) {
888
- return null;
889
- }
890
- // Profile
891
- const profileLowBit = bitstream.readBits(1);
892
- const profileHighBit = bitstream.readBits(1);
893
- const profile = (profileHighBit << 1) + profileLowBit;
894
- // Skip reserved bit for profile 3
895
- if (profile === 3) {
896
- bitstream.skipBits(1);
897
- }
898
- // show_existing_frame
899
- const showExistingFrame = bitstream.readBits(1);
900
- if (showExistingFrame === 1) {
901
- return null;
902
- }
903
- // frame_type (0 = key frame)
904
- const frameType = bitstream.readBits(1);
905
- if (frameType !== 0) {
906
- return null;
907
- }
908
- // Skip show_frame and error_resilient_mode
909
- bitstream.skipBits(2);
910
- // Sync code (0x498342)
911
- const syncCode = bitstream.readBits(24);
912
- if (syncCode !== 0x498342) {
913
- return null;
914
- }
915
- // Color config
916
- let bitDepth = 8;
917
- if (profile >= 2) {
918
- const tenOrTwelveBit = bitstream.readBits(1);
919
- bitDepth = tenOrTwelveBit ? 12 : 10;
920
- }
921
- // Color space
922
- const colorSpace = bitstream.readBits(3);
923
- let chromaSubsampling = 0;
924
- let videoFullRangeFlag = 0;
925
- if (colorSpace !== 7) { // 7 is CS_RGB
926
- const colorRange = bitstream.readBits(1);
927
- videoFullRangeFlag = colorRange;
928
- if (profile === 1 || profile === 3) {
929
- const subsamplingX = bitstream.readBits(1);
930
- const subsamplingY = bitstream.readBits(1);
931
- // 0 = 4:2:0 vertical
932
- // 1 = 4:2:0 colocated
933
- // 2 = 4:2:2
934
- // 3 = 4:4:4
935
- chromaSubsampling = !subsamplingX && !subsamplingY
936
- ? 3 // 0,0 = 4:4:4
937
- : subsamplingX && !subsamplingY
938
- ? 2 // 1,0 = 4:2:2
939
- : 1; // 1,1 = 4:2:0 colocated (default)
940
- // Skip reserved bit
941
- bitstream.skipBits(1);
942
- }
943
- else {
944
- // For profile 0 and 2, always 4:2:0
945
- chromaSubsampling = 1; // Using colocated as default
946
- }
947
- }
948
- else {
949
- // RGB is always 4:4:4
950
- chromaSubsampling = 3;
951
- videoFullRangeFlag = 1;
952
- }
953
- // Parse frame size
954
- const widthMinusOne = bitstream.readBits(16);
955
- const heightMinusOne = bitstream.readBits(16);
956
- const width = widthMinusOne + 1;
957
- const height = heightMinusOne + 1;
958
- // Calculate level based on dimensions
959
- const pictureSize = width * height;
960
- let level = last(VP9_LEVEL_TABLE).level; // Default to highest level
961
- for (const entry of VP9_LEVEL_TABLE) {
962
- if (pictureSize <= entry.maxPictureSize) {
963
- level = entry.level;
964
- break;
965
- }
966
- }
967
- // Map color_space to standard values
968
- const matrixCoefficients = colorSpace === 7
969
- ? 0
970
- : colorSpace === 2
971
- ? 1
972
- : colorSpace === 1
973
- ? 6
974
- : 2;
975
- const colourPrimaries = colorSpace === 2
976
- ? 1
977
- : colorSpace === 1
978
- ? 6
979
- : 2;
980
- const transferCharacteristics = colorSpace === 2
981
- ? 1
982
- : colorSpace === 1
983
- ? 6
984
- : 2;
985
- return {
986
- profile,
987
- level,
988
- bitDepth,
989
- chromaSubsampling,
990
- videoFullRangeFlag,
991
- colourPrimaries,
992
- transferCharacteristics,
993
- matrixCoefficients,
994
- };
995
- };
996
- /** Iterates over all OBUs in an AV1 packet bistream. */
997
- export const iterateAv1PacketObus = function* (packet) {
998
- // https://aomediacodec.github.io/av1-spec/av1-spec.pdf
999
- const bitstream = new Bitstream(packet);
1000
- const readLeb128 = () => {
1001
- let value = 0;
1002
- for (let i = 0; i < 8; i++) {
1003
- const byte = bitstream.readAlignedByte();
1004
- value |= ((byte & 0x7f) << (i * 7));
1005
- if (!(byte & 0x80)) {
1006
- break;
1007
- }
1008
- // Spec requirement
1009
- if (i === 7 && (byte & 0x80)) {
1010
- return null;
1011
- }
1012
- }
1013
- // Spec requirement
1014
- if (value >= 2 ** 32 - 1) {
1015
- return null;
1016
- }
1017
- return value;
1018
- };
1019
- while (bitstream.getBitsLeft() >= 8) {
1020
- // Parse OBU header
1021
- bitstream.skipBits(1);
1022
- const obuType = bitstream.readBits(4);
1023
- const obuExtension = bitstream.readBits(1);
1024
- const obuHasSizeField = bitstream.readBits(1);
1025
- bitstream.skipBits(1);
1026
- // Skip extension header if present
1027
- if (obuExtension) {
1028
- bitstream.skipBits(8);
1029
- }
1030
- // Read OBU size if present
1031
- let obuSize;
1032
- if (obuHasSizeField) {
1033
- const obuSizeValue = readLeb128();
1034
- if (obuSizeValue === null)
1035
- return; // It was invalid
1036
- obuSize = obuSizeValue;
1037
- }
1038
- else {
1039
- // Calculate remaining bits and convert to bytes, rounding down
1040
- obuSize = Math.floor(bitstream.getBitsLeft() / 8);
1041
- }
1042
- assert(bitstream.pos % 8 === 0);
1043
- yield {
1044
- type: obuType,
1045
- data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize),
1046
- };
1047
- // Move to next OBU
1048
- bitstream.skipBits(obuSize * 8);
1049
- }
1050
- };
1051
- /**
1052
- * When AV1 codec information is not provided by the container, we can still try to extract the information by digging
1053
- * into the AV1 bitstream.
1054
- */
1055
- export const extractAv1CodecInfoFromPacket = (packet) => {
1056
- // https://aomediacodec.github.io/av1-spec/av1-spec.pdf
1057
- for (const { type, data } of iterateAv1PacketObus(packet)) {
1058
- if (type !== 1) {
1059
- continue; // 1 == OBU_SEQUENCE_HEADER
1060
- }
1061
- const bitstream = new Bitstream(data);
1062
- // Read sequence header fields
1063
- const seqProfile = bitstream.readBits(3);
1064
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1065
- const stillPicture = bitstream.readBits(1);
1066
- const reducedStillPictureHeader = bitstream.readBits(1);
1067
- let seqLevel = 0;
1068
- let seqTier = 0;
1069
- let bufferDelayLengthMinus1 = 0;
1070
- if (reducedStillPictureHeader) {
1071
- seqLevel = bitstream.readBits(5);
1072
- }
1073
- else {
1074
- // Parse timing_info_present_flag
1075
- const timingInfoPresentFlag = bitstream.readBits(1);
1076
- if (timingInfoPresentFlag) {
1077
- // Skip timing info (num_units_in_display_tick, time_scale, equal_picture_interval)
1078
- bitstream.skipBits(32); // num_units_in_display_tick
1079
- bitstream.skipBits(32); // time_scale
1080
- const equalPictureInterval = bitstream.readBits(1);
1081
- if (equalPictureInterval) {
1082
- // Skip num_ticks_per_picture_minus_1 (uvlc)
1083
- // Since this is variable length, we'd need to implement uvlc reading
1084
- // For now, we'll return null as this is rare
1085
- return null;
1086
- }
1087
- }
1088
- // Parse decoder_model_info_present_flag
1089
- const decoderModelInfoPresentFlag = bitstream.readBits(1);
1090
- if (decoderModelInfoPresentFlag) {
1091
- // Store buffer_delay_length_minus_1 instead of just skipping
1092
- bufferDelayLengthMinus1 = bitstream.readBits(5);
1093
- bitstream.skipBits(32); // num_units_in_decoding_tick
1094
- bitstream.skipBits(5); // buffer_removal_time_length_minus_1
1095
- bitstream.skipBits(5); // frame_presentation_time_length_minus_1
1096
- }
1097
- // Parse operating_points_cnt_minus_1
1098
- const operatingPointsCntMinus1 = bitstream.readBits(5);
1099
- // For each operating point
1100
- for (let i = 0; i <= operatingPointsCntMinus1; i++) {
1101
- // operating_point_idc[i]
1102
- bitstream.skipBits(12);
1103
- // seq_level_idx[i]
1104
- const seqLevelIdx = bitstream.readBits(5);
1105
- if (i === 0) {
1106
- seqLevel = seqLevelIdx;
1107
- }
1108
- if (seqLevelIdx > 7) {
1109
- // seq_tier[i]
1110
- const seqTierTemp = bitstream.readBits(1);
1111
- if (i === 0) {
1112
- seqTier = seqTierTemp;
1113
- }
1114
- }
1115
- if (decoderModelInfoPresentFlag) {
1116
- // decoder_model_present_for_this_op[i]
1117
- const decoderModelPresentForThisOp = bitstream.readBits(1);
1118
- if (decoderModelPresentForThisOp) {
1119
- const n = bufferDelayLengthMinus1 + 1;
1120
- bitstream.skipBits(n); // decoder_buffer_delay[op]
1121
- bitstream.skipBits(n); // encoder_buffer_delay[op]
1122
- bitstream.skipBits(1); // low_delay_mode_flag[op]
1123
- }
1124
- }
1125
- // initial_display_delay_present_flag
1126
- const initialDisplayDelayPresentFlag = bitstream.readBits(1);
1127
- if (initialDisplayDelayPresentFlag) {
1128
- // initial_display_delay_minus_1[i]
1129
- bitstream.skipBits(4);
1130
- }
1131
- }
1132
- }
1133
- const highBitdepth = bitstream.readBits(1);
1134
- let bitDepth = 8;
1135
- if (seqProfile === 2 && highBitdepth) {
1136
- const twelveBit = bitstream.readBits(1);
1137
- bitDepth = twelveBit ? 12 : 10;
1138
- }
1139
- else if (seqProfile <= 2) {
1140
- bitDepth = highBitdepth ? 10 : 8;
1141
- }
1142
- let monochrome = 0;
1143
- if (seqProfile !== 1) {
1144
- monochrome = bitstream.readBits(1);
1145
- }
1146
- let chromaSubsamplingX = 1;
1147
- let chromaSubsamplingY = 1;
1148
- let chromaSamplePosition = 0;
1149
- if (!monochrome) {
1150
- if (seqProfile === 0) {
1151
- chromaSubsamplingX = 1;
1152
- chromaSubsamplingY = 1;
1153
- }
1154
- else if (seqProfile === 1) {
1155
- chromaSubsamplingX = 0;
1156
- chromaSubsamplingY = 0;
1157
- }
1158
- else {
1159
- if (bitDepth === 12) {
1160
- chromaSubsamplingX = bitstream.readBits(1);
1161
- if (chromaSubsamplingX) {
1162
- chromaSubsamplingY = bitstream.readBits(1);
1163
- }
1164
- }
1165
- }
1166
- if (chromaSubsamplingX && chromaSubsamplingY) {
1167
- chromaSamplePosition = bitstream.readBits(2);
1168
- }
1169
- }
1170
- return {
1171
- profile: seqProfile,
1172
- level: seqLevel,
1173
- tier: seqTier,
1174
- bitDepth,
1175
- monochrome,
1176
- chromaSubsamplingX,
1177
- chromaSubsamplingY,
1178
- chromaSamplePosition,
1179
- };
1180
- }
1181
- return null;
1182
- };
1183
- export const parseOpusIdentificationHeader = (bytes) => {
1184
- const view = toDataView(bytes);
1185
- const outputChannelCount = view.getUint8(9);
1186
- const preSkip = view.getUint16(10, true);
1187
- const inputSampleRate = view.getUint32(12, true);
1188
- const outputGain = view.getInt16(16, true);
1189
- const channelMappingFamily = view.getUint8(18);
1190
- let channelMappingTable = null;
1191
- if (channelMappingFamily) {
1192
- channelMappingTable = bytes.subarray(19, 19 + 2 + outputChannelCount);
1193
- }
1194
- return {
1195
- outputChannelCount,
1196
- preSkip,
1197
- inputSampleRate,
1198
- outputGain,
1199
- channelMappingFamily,
1200
- channelMappingTable,
1201
- };
1202
- };
1203
- // From https://datatracker.ietf.org/doc/html/rfc6716, in 48 kHz samples
1204
- const OPUS_FRAME_DURATION_TABLE = [
1205
- 480, 960, 1920, 2880,
1206
- 480, 960, 1920, 2880,
1207
- 480, 960, 1920, 2880,
1208
- 480, 960,
1209
- 480, 960,
1210
- 120, 240, 480, 960,
1211
- 120, 240, 480, 960,
1212
- 120, 240, 480, 960,
1213
- 120, 240, 480, 960,
1214
- ];
1215
- export const parseOpusTocByte = (packet) => {
1216
- const config = packet[0] >> 3;
1217
- return {
1218
- durationInSamples: OPUS_FRAME_DURATION_TABLE[config],
1219
- };
1220
- };
1221
- // Based on vorbis_parser.c from FFmpeg.
1222
- export const parseModesFromVorbisSetupPacket = (setupHeader) => {
1223
- // Verify that this is a Setup header.
1224
- if (setupHeader.length < 7) {
1225
- throw new Error('Setup header is too short.');
1226
- }
1227
- if (setupHeader[0] !== 5) {
1228
- throw new Error('Wrong packet type in Setup header.');
1229
- }
1230
- const signature = String.fromCharCode(...setupHeader.slice(1, 7));
1231
- if (signature !== 'vorbis') {
1232
- throw new Error('Invalid packet signature in Setup header.');
1233
- }
1234
- // Reverse the entire buffer.
1235
- const bufSize = setupHeader.length;
1236
- const revBuffer = new Uint8Array(bufSize);
1237
- for (let i = 0; i < bufSize; i++) {
1238
- revBuffer[i] = setupHeader[bufSize - 1 - i];
1239
- }
1240
- // Initialize a Bitstream on the reversed buffer.
1241
- const bitstream = new Bitstream(revBuffer);
1242
- // --- Find the framing bit.
1243
- // In FFmpeg code, we scan until get_bits1() returns 1.
1244
- let gotFramingBit = 0;
1245
- while (bitstream.getBitsLeft() > 97) {
1246
- if (bitstream.readBits(1) === 1) {
1247
- gotFramingBit = bitstream.pos;
1248
- break;
1249
- }
1250
- }
1251
- if (gotFramingBit === 0) {
1252
- throw new Error('Invalid Setup header: framing bit not found.');
1253
- }
1254
- // --- Search backwards for a valid mode header.
1255
- // We try to “guess” the number of modes by reading a fixed pattern.
1256
- let modeCount = 0;
1257
- let gotModeHeader = false;
1258
- let lastModeCount = 0;
1259
- while (bitstream.getBitsLeft() >= 97) {
1260
- const tempPos = bitstream.pos;
1261
- const a = bitstream.readBits(8);
1262
- const b = bitstream.readBits(16);
1263
- const c = bitstream.readBits(16);
1264
- // If a > 63 or b or c nonzero, assume we’ve gone too far.
1265
- if (a > 63 || b !== 0 || c !== 0) {
1266
- bitstream.pos = tempPos;
1267
- break;
1268
- }
1269
- bitstream.skipBits(1);
1270
- modeCount++;
1271
- if (modeCount > 64) {
1272
- break;
1273
- }
1274
- const bsClone = bitstream.clone();
1275
- const candidate = bsClone.readBits(6) + 1;
1276
- if (candidate === modeCount) {
1277
- gotModeHeader = true;
1278
- lastModeCount = modeCount;
1279
- }
1280
- }
1281
- if (!gotModeHeader) {
1282
- throw new Error('Invalid Setup header: mode header not found.');
1283
- }
1284
- if (lastModeCount > 63) {
1285
- throw new Error(`Unsupported mode count: ${lastModeCount}.`);
1286
- }
1287
- const finalModeCount = lastModeCount;
1288
- // --- Reinitialize the bitstream.
1289
- bitstream.pos = 0;
1290
- // Skip the bits up to the found framing bit.
1291
- bitstream.skipBits(gotFramingBit);
1292
- // --- Now read, for each mode (in reverse order), 40 bits then one bit.
1293
- // That one bit is the mode blockflag.
1294
- const modeBlockflags = Array(finalModeCount).fill(0);
1295
- for (let i = finalModeCount - 1; i >= 0; i--) {
1296
- bitstream.skipBits(40);
1297
- modeBlockflags[i] = bitstream.readBits(1);
1298
- }
1299
- return { modeBlockflags };
1300
- };
1301
- /** Determines a packet's type (key or delta) by digging into the packet bitstream. */
1302
- export const determineVideoPacketType = (codec, decoderConfig, packetData) => {
1303
- switch (codec) {
1304
- case 'avc':
1305
- {
1306
- const nalUnits = extractAvcNalUnits(packetData, decoderConfig);
1307
- const isKeyframe = nalUnits.some(x => extractNalUnitTypeForAvc(x) === AvcNalUnitType.IDR);
1308
- return isKeyframe ? 'key' : 'delta';
1309
- }
1310
- ;
1311
- case 'hevc':
1312
- {
1313
- const nalUnits = extractHevcNalUnits(packetData, decoderConfig);
1314
- const isKeyframe = nalUnits.some((x) => {
1315
- const type = extractNalUnitTypeForHevc(x);
1316
- return HevcNalUnitType.BLA_W_LP <= type && type <= HevcNalUnitType.RSV_IRAP_VCL23;
1317
- });
1318
- return isKeyframe ? 'key' : 'delta';
1319
- }
1320
- ;
1321
- case 'vp8':
1322
- {
1323
- // VP8, once again, by far the easiest to deal with.
1324
- const frameType = packetData[0] & 0b1;
1325
- return frameType === 0 ? 'key' : 'delta';
1326
- }
1327
- ;
1328
- case 'vp9':
1329
- {
1330
- const bitstream = new Bitstream(packetData);
1331
- if (bitstream.readBits(2) !== 2) {
1332
- return null;
1333
- }
1334
- ;
1335
- const profileLowBit = bitstream.readBits(1);
1336
- const profileHighBit = bitstream.readBits(1);
1337
- const profile = (profileHighBit << 1) + profileLowBit;
1338
- // Skip reserved bit for profile 3
1339
- if (profile === 3) {
1340
- bitstream.skipBits(1);
1341
- }
1342
- const showExistingFrame = bitstream.readBits(1);
1343
- if (showExistingFrame) {
1344
- return null;
1345
- }
1346
- const frameType = bitstream.readBits(1);
1347
- return frameType === 0 ? 'key' : 'delta';
1348
- }
1349
- ;
1350
- case 'av1':
1351
- {
1352
- let reducedStillPictureHeader = false;
1353
- for (const { type, data } of iterateAv1PacketObus(packetData)) {
1354
- if (type === 1) { // OBU_SEQUENCE_HEADER
1355
- const bitstream = new Bitstream(data);
1356
- bitstream.skipBits(4);
1357
- reducedStillPictureHeader = !!bitstream.readBits(1);
1358
- }
1359
- else if (type === 3 // OBU_FRAME_HEADER
1360
- || type === 6 // OBU_FRAME
1361
- || type === 7 // OBU_REDUNDANT_FRAME_HEADER
1362
- ) {
1363
- if (reducedStillPictureHeader) {
1364
- return 'key';
1365
- }
1366
- const bitstream = new Bitstream(data);
1367
- const showExistingFrame = bitstream.readBits(1);
1368
- if (showExistingFrame) {
1369
- return null;
1370
- }
1371
- const frameType = bitstream.readBits(2);
1372
- return frameType === 0 ? 'key' : 'delta';
1373
- }
1374
- }
1375
- return null;
1376
- }
1377
- ;
1378
- default:
1379
- {
1380
- assertNever(codec);
1381
- assert(false);
1382
- }
1383
- ;
1384
- }
1385
- };
1386
- export var FlacBlockType;
1387
- (function (FlacBlockType) {
1388
- FlacBlockType[FlacBlockType["STREAMINFO"] = 0] = "STREAMINFO";
1389
- FlacBlockType[FlacBlockType["VORBIS_COMMENT"] = 4] = "VORBIS_COMMENT";
1390
- FlacBlockType[FlacBlockType["PICTURE"] = 6] = "PICTURE";
1391
- })(FlacBlockType || (FlacBlockType = {}));
1392
- export const readVorbisComments = (bytes, metadataTags) => {
1393
- // https://datatracker.ietf.org/doc/html/rfc7845#section-5.2
1394
- const commentView = toDataView(bytes);
1395
- let commentPos = 0;
1396
- const vendorStringLength = commentView.getUint32(commentPos, true);
1397
- commentPos += 4;
1398
- const vendorString = textDecoder.decode(bytes.subarray(commentPos, commentPos + vendorStringLength));
1399
- commentPos += vendorStringLength;
1400
- if (vendorStringLength > 0) {
1401
- // Expose the vendor string in the raw metadata
1402
- metadataTags.raw ??= {};
1403
- metadataTags.raw['vendor'] ??= vendorString;
1404
- }
1405
- const listLength = commentView.getUint32(commentPos, true);
1406
- commentPos += 4;
1407
- // Loop over all metadata tags
1408
- for (let i = 0; i < listLength; i++) {
1409
- const stringLength = commentView.getUint32(commentPos, true);
1410
- commentPos += 4;
1411
- const string = textDecoder.decode(bytes.subarray(commentPos, commentPos + stringLength));
1412
- commentPos += stringLength;
1413
- const separatorIndex = string.indexOf('=');
1414
- if (separatorIndex === -1) {
1415
- continue;
1416
- }
1417
- const key = string.slice(0, separatorIndex).toUpperCase();
1418
- const value = string.slice(separatorIndex + 1);
1419
- metadataTags.raw ??= {};
1420
- metadataTags.raw[key] ??= value;
1421
- switch (key) {
1422
- case 'TITLE':
1423
- {
1424
- metadataTags.title ??= value;
1425
- }
1426
- ;
1427
- break;
1428
- case 'DESCRIPTION':
1429
- {
1430
- metadataTags.description ??= value;
1431
- }
1432
- ;
1433
- break;
1434
- case 'ARTIST':
1435
- {
1436
- metadataTags.artist ??= value;
1437
- }
1438
- ;
1439
- break;
1440
- case 'ALBUM':
1441
- {
1442
- metadataTags.album ??= value;
1443
- }
1444
- ;
1445
- break;
1446
- case 'ALBUMARTIST':
1447
- {
1448
- metadataTags.albumArtist ??= value;
1449
- }
1450
- ;
1451
- break;
1452
- case 'COMMENT':
1453
- {
1454
- metadataTags.comment ??= value;
1455
- }
1456
- ;
1457
- break;
1458
- case 'LYRICS':
1459
- {
1460
- metadataTags.lyrics ??= value;
1461
- }
1462
- ;
1463
- break;
1464
- case 'TRACKNUMBER':
1465
- {
1466
- const parts = value.split('/');
1467
- const trackNum = Number.parseInt(parts[0], 10);
1468
- const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
1469
- if (Number.isInteger(trackNum) && trackNum > 0) {
1470
- metadataTags.trackNumber ??= trackNum;
1471
- }
1472
- if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
1473
- metadataTags.tracksTotal ??= tracksTotal;
1474
- }
1475
- }
1476
- ;
1477
- break;
1478
- case 'TRACKTOTAL':
1479
- {
1480
- const tracksTotal = Number.parseInt(value, 10);
1481
- if (Number.isInteger(tracksTotal) && tracksTotal > 0) {
1482
- metadataTags.tracksTotal ??= tracksTotal;
1483
- }
1484
- }
1485
- ;
1486
- break;
1487
- case 'DISCNUMBER':
1488
- {
1489
- const parts = value.split('/');
1490
- const discNum = Number.parseInt(parts[0], 10);
1491
- const discsTotal = parts[1] && Number.parseInt(parts[1], 10);
1492
- if (Number.isInteger(discNum) && discNum > 0) {
1493
- metadataTags.discNumber ??= discNum;
1494
- }
1495
- if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) {
1496
- metadataTags.discsTotal ??= discsTotal;
1497
- }
1498
- }
1499
- ;
1500
- break;
1501
- case 'DISCTOTAL':
1502
- {
1503
- const discsTotal = Number.parseInt(value, 10);
1504
- if (Number.isInteger(discsTotal) && discsTotal > 0) {
1505
- metadataTags.discsTotal ??= discsTotal;
1506
- }
1507
- }
1508
- ;
1509
- break;
1510
- case 'DATE':
1511
- {
1512
- const date = new Date(value);
1513
- if (!Number.isNaN(date.getTime())) {
1514
- metadataTags.date ??= date;
1515
- }
1516
- }
1517
- ;
1518
- break;
1519
- case 'GENRE':
1520
- {
1521
- metadataTags.genre ??= value;
1522
- }
1523
- ;
1524
- break;
1525
- case 'METADATA_BLOCK_PICTURE':
1526
- {
1527
- // https://datatracker.ietf.org/doc/rfc9639/ Section 8.8
1528
- const decoded = base64ToBytes(value);
1529
- const view = toDataView(decoded);
1530
- const pictureType = view.getUint32(0, false);
1531
- const mediaTypeLength = view.getUint32(4, false);
1532
- const mediaType = String.fromCharCode(...decoded.subarray(8, 8 + mediaTypeLength)); // ASCII
1533
- const descriptionLength = view.getUint32(8 + mediaTypeLength, false);
1534
- const description = textDecoder.decode(decoded.subarray(12 + mediaTypeLength, 12 + mediaTypeLength + descriptionLength));
1535
- const dataLength = view.getUint32(mediaTypeLength + descriptionLength + 28);
1536
- const data = decoded.subarray(mediaTypeLength + descriptionLength + 32, mediaTypeLength + descriptionLength + 32 + dataLength);
1537
- metadataTags.images ??= [];
1538
- metadataTags.images.push({
1539
- data,
1540
- mimeType: mediaType,
1541
- kind: pictureType === 3 ? 'coverFront' : pictureType === 4 ? 'coverBack' : 'unknown',
1542
- name: undefined,
1543
- description: description || undefined,
1544
- });
1545
- }
1546
- ;
1547
- break;
1548
- }
1549
- }
1550
- };
1551
- export const createVorbisComments = (headerBytes, tags, writeImages) => {
1552
- // https://datatracker.ietf.org/doc/html/rfc7845#section-5.2
1553
- const commentHeaderParts = [
1554
- headerBytes,
1555
- ];
1556
- const vendorString = 'Mediabunny';
1557
- const encodedVendorString = textEncoder.encode(vendorString);
1558
- let currentBuffer = new Uint8Array(4 + encodedVendorString.length);
1559
- let currentView = new DataView(currentBuffer.buffer);
1560
- currentView.setUint32(0, encodedVendorString.length, true);
1561
- currentBuffer.set(encodedVendorString, 4);
1562
- commentHeaderParts.push(currentBuffer);
1563
- const writtenTags = new Set();
1564
- const addCommentTag = (key, value) => {
1565
- const joined = `${key}=${value}`;
1566
- const encoded = textEncoder.encode(joined);
1567
- currentBuffer = new Uint8Array(4 + encoded.length);
1568
- currentView = new DataView(currentBuffer.buffer);
1569
- currentView.setUint32(0, encoded.length, true);
1570
- currentBuffer.set(encoded, 4);
1571
- commentHeaderParts.push(currentBuffer);
1572
- writtenTags.add(key);
1573
- };
1574
- for (const { key, value } of keyValueIterator(tags)) {
1575
- switch (key) {
1576
- case 'title':
1577
- {
1578
- addCommentTag('TITLE', value);
1579
- }
1580
- ;
1581
- break;
1582
- case 'description':
1583
- {
1584
- addCommentTag('DESCRIPTION', value);
1585
- }
1586
- ;
1587
- break;
1588
- case 'artist':
1589
- {
1590
- addCommentTag('ARTIST', value);
1591
- }
1592
- ;
1593
- break;
1594
- case 'album':
1595
- {
1596
- addCommentTag('ALBUM', value);
1597
- }
1598
- ;
1599
- break;
1600
- case 'albumArtist':
1601
- {
1602
- addCommentTag('ALBUMARTIST', value);
1603
- }
1604
- ;
1605
- break;
1606
- case 'genre':
1607
- {
1608
- addCommentTag('GENRE', value);
1609
- }
1610
- ;
1611
- break;
1612
- case 'date':
1613
- {
1614
- const rawVersion = tags.raw?.['DATE'] ?? tags.raw?.['date'];
1615
- if (rawVersion && typeof rawVersion === 'string') {
1616
- addCommentTag('DATE', rawVersion);
1617
- }
1618
- else {
1619
- addCommentTag('DATE', value.toISOString().slice(0, 10));
1620
- }
1621
- }
1622
- ;
1623
- break;
1624
- case 'comment':
1625
- {
1626
- addCommentTag('COMMENT', value);
1627
- }
1628
- ;
1629
- break;
1630
- case 'lyrics':
1631
- {
1632
- addCommentTag('LYRICS', value);
1633
- }
1634
- ;
1635
- break;
1636
- case 'trackNumber':
1637
- {
1638
- addCommentTag('TRACKNUMBER', value.toString());
1639
- }
1640
- ;
1641
- break;
1642
- case 'tracksTotal':
1643
- {
1644
- addCommentTag('TRACKTOTAL', value.toString());
1645
- }
1646
- ;
1647
- break;
1648
- case 'discNumber':
1649
- {
1650
- addCommentTag('DISCNUMBER', value.toString());
1651
- }
1652
- ;
1653
- break;
1654
- case 'discsTotal':
1655
- {
1656
- addCommentTag('DISCTOTAL', value.toString());
1657
- }
1658
- ;
1659
- break;
1660
- case 'images':
1661
- {
1662
- // For example, in .flac, we put the pictures in a different section,
1663
- // not in the Vorbis comment header.
1664
- if (!writeImages) {
1665
- break;
1666
- }
1667
- for (const image of value) {
1668
- // https://datatracker.ietf.org/doc/rfc9639/ Section 8.8
1669
- const pictureType = image.kind === 'coverFront' ? 3 : image.kind === 'coverBack' ? 4 : 0;
1670
- const encodedMediaType = new Uint8Array(image.mimeType.length);
1671
- for (let i = 0; i < image.mimeType.length; i++) {
1672
- encodedMediaType[i] = image.mimeType.charCodeAt(i);
1673
- }
1674
- const encodedDescription = textEncoder.encode(image.description ?? '');
1675
- const buffer = new Uint8Array(4 // Picture type
1676
- + 4 // MIME type length
1677
- + encodedMediaType.length // MIME type
1678
- + 4 // Description length
1679
- + encodedDescription.length // Description
1680
- + 16 // Width, height, color depth, number of colors
1681
- + 4 // Picture data length
1682
- + image.data.length);
1683
- const view = toDataView(buffer);
1684
- view.setUint32(0, pictureType, false);
1685
- view.setUint32(4, encodedMediaType.length, false);
1686
- buffer.set(encodedMediaType, 8);
1687
- view.setUint32(8 + encodedMediaType.length, encodedDescription.length, false);
1688
- buffer.set(encodedDescription, 12 + encodedMediaType.length);
1689
- // Skip a bunch of fields (width, height, color depth, number of colors)
1690
- view.setUint32(28 + encodedMediaType.length + encodedDescription.length, image.data.length, false);
1691
- buffer.set(image.data, 32 + encodedMediaType.length + encodedDescription.length);
1692
- const encoded = bytesToBase64(buffer);
1693
- addCommentTag('METADATA_BLOCK_PICTURE', encoded);
1694
- }
1695
- }
1696
- ;
1697
- break;
1698
- case 'raw':
1699
- {
1700
- // Handled later
1701
- }
1702
- ;
1703
- break;
1704
- default: assertNever(key);
1705
- }
1706
- }
1707
- if (tags.raw) {
1708
- for (const key in tags.raw) {
1709
- const value = tags.raw[key] ?? tags.raw[key.toLowerCase()];
1710
- if (key === 'vendor' || value == null || writtenTags.has(key)) {
1711
- continue;
1712
- }
1713
- if (typeof value === 'string') {
1714
- addCommentTag(key, value);
1715
- }
1716
- }
1717
- }
1718
- const listLengthBuffer = new Uint8Array(4);
1719
- toDataView(listLengthBuffer).setUint32(0, writtenTags.size, true);
1720
- commentHeaderParts.splice(2, 0, listLengthBuffer); // Insert after the header and vendor section
1721
- // Merge all comment header parts into a single buffer
1722
- const commentHeaderLength = commentHeaderParts.reduce((a, b) => a + b.length, 0);
1723
- const commentHeader = new Uint8Array(commentHeaderLength);
1724
- let pos = 0;
1725
- for (const part of commentHeaderParts) {
1726
- commentHeader.set(part, pos);
1727
- pos += part.length;
1728
- }
1729
- return commentHeader;
1730
- };