@remotion/media-parser 4.0.194 → 4.0.196

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 (275) hide show
  1. package/dist/add-new-matroska-tracks.d.ts +8 -0
  2. package/dist/add-new-matroska-tracks.js +14 -0
  3. package/dist/av1-codec-string.d.ts +3 -0
  4. package/dist/av1-codec-string.js +91 -0
  5. package/dist/boxes/iso-base-media/esds/decoder-specific-config.d.ts +14 -0
  6. package/dist/boxes/iso-base-media/esds/decoder-specific-config.js +38 -0
  7. package/dist/boxes/iso-base-media/esds/esds-descriptors.d.ts +8 -0
  8. package/dist/boxes/iso-base-media/esds/esds-descriptors.js +22 -2
  9. package/dist/boxes/iso-base-media/ftype.d.ts +9 -0
  10. package/dist/boxes/iso-base-media/ftype.js +31 -0
  11. package/dist/boxes/iso-base-media/make-track.d.ts +3 -0
  12. package/dist/boxes/iso-base-media/make-track.js +112 -0
  13. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +6 -2
  14. package/dist/boxes/iso-base-media/mdat/mdat.js +90 -6
  15. package/dist/boxes/iso-base-media/moov/moov.d.ts +4 -2
  16. package/dist/boxes/iso-base-media/moov/moov.js +4 -2
  17. package/dist/boxes/iso-base-media/mvhd.js +7 -1
  18. package/dist/boxes/iso-base-media/process-box.d.ts +20 -3
  19. package/dist/boxes/iso-base-media/process-box.js +322 -34
  20. package/dist/boxes/iso-base-media/stsd/av1c.d.ts +9 -0
  21. package/dist/boxes/iso-base-media/stsd/av1c.js +10 -0
  22. package/dist/boxes/iso-base-media/stsd/avcc-hvcc.d.ts +20 -0
  23. package/dist/boxes/iso-base-media/stsd/avcc-hvcc.js +73 -0
  24. package/dist/boxes/iso-base-media/stsd/avcc.d.ts +10 -0
  25. package/dist/boxes/iso-base-media/stsd/avcc.js +21 -0
  26. package/dist/boxes/iso-base-media/stsd/colr.d.ts +11 -0
  27. package/dist/boxes/iso-base-media/stsd/colr.js +35 -0
  28. package/dist/boxes/iso-base-media/stsd/ctts.d.ts +19 -0
  29. package/dist/boxes/iso-base-media/stsd/ctts.js +30 -0
  30. package/dist/boxes/iso-base-media/stsd/hvcc.d.ts +11 -0
  31. package/dist/boxes/iso-base-media/stsd/hvcc.js +17 -0
  32. package/dist/boxes/iso-base-media/stsd/keys.js +1 -0
  33. package/dist/boxes/iso-base-media/stsd/mebx.d.ts +4 -2
  34. package/dist/boxes/iso-base-media/stsd/mebx.js +4 -2
  35. package/dist/boxes/iso-base-media/stsd/pasp.d.ts +12 -0
  36. package/dist/boxes/iso-base-media/stsd/pasp.js +17 -0
  37. package/dist/boxes/iso-base-media/stsd/samples.d.ts +12 -5
  38. package/dist/boxes/iso-base-media/stsd/samples.js +27 -10
  39. package/dist/boxes/iso-base-media/stsd/stsd.d.ts +4 -2
  40. package/dist/boxes/iso-base-media/stsd/stsd.js +6 -2
  41. package/dist/boxes/iso-base-media/stsd/stss.d.ts +13 -0
  42. package/dist/boxes/iso-base-media/stsd/stss.js +28 -0
  43. package/dist/boxes/iso-base-media/stsd/stsz.d.ts +9 -3
  44. package/dist/boxes/iso-base-media/stsd/stsz.js +14 -2
  45. package/dist/boxes/iso-base-media/stsd/stts.d.ts +15 -0
  46. package/dist/boxes/iso-base-media/stsd/stts.js +35 -0
  47. package/dist/boxes/iso-base-media/tkhd.d.ts +3 -0
  48. package/dist/boxes/iso-base-media/tkhd.js +41 -14
  49. package/dist/boxes/iso-base-media/trak/trak.d.ts +4 -2
  50. package/dist/boxes/iso-base-media/trak/trak.js +4 -2
  51. package/dist/boxes/iso-base-media/void-box.d.ts +4 -0
  52. package/dist/boxes/iso-base-media/void-box.js +2 -0
  53. package/dist/boxes/webm/allowed-partial-segments.d.ts +1 -0
  54. package/dist/boxes/webm/allowed-partial-segments.js +4 -0
  55. package/dist/boxes/webm/av1-codec-private.d.ts +2 -0
  56. package/dist/boxes/webm/av1-codec-private.js +95 -0
  57. package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.d.ts +14 -0
  58. package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.js +67 -0
  59. package/dist/boxes/webm/bitstream/av1/bitstream-frame.d.ts +11 -0
  60. package/dist/boxes/webm/bitstream/av1/bitstream-frame.js +14 -0
  61. package/dist/boxes/webm/bitstream/av1/chroma-sample-position.d.ts +6 -0
  62. package/dist/boxes/webm/bitstream/av1/chroma-sample-position.js +9 -0
  63. package/dist/boxes/webm/bitstream/av1/color-config.d.ts +16 -0
  64. package/dist/boxes/webm/bitstream/av1/color-config.js +103 -0
  65. package/dist/boxes/webm/bitstream/av1/color-primaries.d.ts +14 -0
  66. package/dist/boxes/webm/bitstream/av1/color-primaries.js +17 -0
  67. package/dist/boxes/webm/bitstream/av1/decoder-model-info.d.ts +9 -0
  68. package/dist/boxes/webm/bitstream/av1/decoder-model-info.js +17 -0
  69. package/dist/boxes/webm/bitstream/av1/header-segment.d.ts +51 -0
  70. package/dist/boxes/webm/bitstream/av1/header-segment.js +183 -0
  71. package/dist/boxes/webm/bitstream/av1/matrix-coefficients.d.ts +17 -0
  72. package/dist/boxes/webm/bitstream/av1/matrix-coefficients.js +20 -0
  73. package/dist/boxes/webm/bitstream/av1/operating-parameters-info.d.ts +10 -0
  74. package/dist/boxes/webm/bitstream/av1/operating-parameters-info.js +15 -0
  75. package/dist/boxes/webm/bitstream/av1/temporal-point-info.d.ts +5 -0
  76. package/dist/boxes/webm/bitstream/av1/temporal-point-info.js +8 -0
  77. package/dist/boxes/webm/bitstream/av1/timing-info.d.ts +8 -0
  78. package/dist/boxes/webm/bitstream/av1/timing-info.js +20 -0
  79. package/dist/boxes/webm/bitstream/av1/transfer-characteristics.d.ts +21 -0
  80. package/dist/boxes/webm/bitstream/av1/transfer-characteristics.js +24 -0
  81. package/dist/boxes/webm/bitstream/av1/uvlc.d.ts +2 -0
  82. package/dist/boxes/webm/bitstream/av1/uvlc.js +20 -0
  83. package/dist/boxes/webm/bitstream/av1.d.ts +20 -0
  84. package/dist/boxes/webm/bitstream/av1.js +118 -0
  85. package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.d.ts +0 -0
  86. package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.js +1 -0
  87. package/dist/boxes/webm/description.d.ts +2 -0
  88. package/dist/boxes/webm/description.js +83 -0
  89. package/dist/boxes/webm/get-ready-tracks.d.ts +3 -0
  90. package/dist/boxes/webm/get-ready-tracks.js +29 -0
  91. package/dist/boxes/webm/get-track.d.ts +6 -0
  92. package/dist/boxes/webm/get-track.js +183 -0
  93. package/dist/boxes/webm/parse-webm-header.d.ts +2 -1
  94. package/dist/boxes/webm/parse-webm-header.js +2 -2
  95. package/dist/boxes/webm/segments/all-segments.d.ts +269 -0
  96. package/dist/boxes/webm/segments/all-segments.js +277 -0
  97. package/dist/boxes/webm/segments/duration.d.ts +1 -1
  98. package/dist/boxes/webm/segments/duration.js +13 -9
  99. package/dist/boxes/webm/segments/info.d.ts +2 -1
  100. package/dist/boxes/webm/segments/info.js +12 -4
  101. package/dist/boxes/webm/segments/main.d.ts +0 -2
  102. package/dist/boxes/webm/segments/main.js +0 -11
  103. package/dist/boxes/webm/segments/muxing.d.ts +1 -1
  104. package/dist/boxes/webm/segments/muxing.js +1 -2
  105. package/dist/boxes/webm/segments/parse-children.d.ts +11 -1
  106. package/dist/boxes/webm/segments/parse-children.js +82 -6
  107. package/dist/boxes/webm/segments/seek-head.d.ts +2 -1
  108. package/dist/boxes/webm/segments/seek-head.js +12 -3
  109. package/dist/boxes/webm/segments/seek-position.d.ts +1 -1
  110. package/dist/boxes/webm/segments/seek-position.js +1 -2
  111. package/dist/boxes/webm/segments/seek.d.ts +7 -2
  112. package/dist/boxes/webm/segments/seek.js +22 -9
  113. package/dist/boxes/webm/segments/track-entry.d.ts +102 -22
  114. package/dist/boxes/webm/segments/track-entry.js +269 -57
  115. package/dist/boxes/webm/segments/tracks.d.ts +2 -1
  116. package/dist/boxes/webm/segments/tracks.js +12 -4
  117. package/dist/boxes/webm/segments/void.d.ts +1 -1
  118. package/dist/boxes/webm/segments/void.js +1 -2
  119. package/dist/boxes/webm/segments/writing.d.ts +1 -1
  120. package/dist/boxes/webm/segments/writing.js +1 -2
  121. package/dist/boxes/webm/segments.d.ts +8 -4
  122. package/dist/boxes/webm/segments.js +176 -40
  123. package/dist/boxes/webm/traversal.d.ts +7 -0
  124. package/dist/boxes/webm/traversal.js +25 -0
  125. package/dist/buffer-iterator.d.ts +15 -1
  126. package/dist/buffer-iterator.js +115 -46
  127. package/dist/combine-uint8array.d.ts +1 -0
  128. package/dist/combine-uint8array.js +13 -0
  129. package/dist/from-fetch.d.ts +2 -0
  130. package/dist/from-fetch.js +54 -0
  131. package/dist/from-node.js +6 -0
  132. package/dist/from-web-file.d.ts +2 -0
  133. package/dist/from-web-file.js +34 -0
  134. package/dist/from-web.js +6 -9
  135. package/dist/get-audio-codec.d.ts +19 -0
  136. package/dist/get-audio-codec.js +153 -60
  137. package/dist/get-dimensions.d.ts +8 -2
  138. package/dist/get-dimensions.js +13 -91
  139. package/dist/get-duration.js +4 -7
  140. package/dist/get-fps.d.ts +4 -3
  141. package/dist/get-fps.js +17 -116
  142. package/dist/get-sample-aspect-ratio.d.ts +37 -0
  143. package/dist/get-sample-aspect-ratio.js +137 -0
  144. package/dist/get-sample-positions.d.ts +11 -1
  145. package/dist/get-sample-positions.js +31 -2
  146. package/dist/get-tracks.d.ts +44 -5
  147. package/dist/get-tracks.js +66 -48
  148. package/dist/get-video-codec.d.ts +2 -0
  149. package/dist/get-video-codec.js +47 -31
  150. package/dist/get-video-metadata.d.ts +2 -0
  151. package/dist/get-video-metadata.js +44 -0
  152. package/dist/has-all-info.d.ts +2 -1
  153. package/dist/has-all-info.js +14 -5
  154. package/dist/index.d.ts +1 -0
  155. package/dist/make-hvc1-codec-strings.d.ts +2 -0
  156. package/dist/make-hvc1-codec-strings.js +47 -0
  157. package/dist/options.d.ts +26 -4
  158. package/dist/parse-media.js +62 -17
  159. package/dist/parse-result.d.ts +16 -5
  160. package/dist/parse-video.d.ts +11 -2
  161. package/dist/parse-video.js +14 -6
  162. package/dist/parser-context.d.ts +8 -0
  163. package/dist/parser-context.js +2 -0
  164. package/dist/parser-state.d.ts +23 -0
  165. package/dist/parser-state.js +112 -0
  166. package/dist/read-and-increment-offset.d.ts +28 -0
  167. package/dist/read-and-increment-offset.js +177 -0
  168. package/dist/reader.d.ts +2 -2
  169. package/dist/traversal.d.ts +37 -0
  170. package/dist/traversal.js +244 -1
  171. package/dist/understand-vorbis.d.ts +1 -0
  172. package/dist/understand-vorbis.js +12 -0
  173. package/dist/webcodec-sample-types.d.ts +21 -0
  174. package/dist/webcodec-sample-types.js +2 -0
  175. package/package.json +10 -2
  176. package/src/add-new-matroska-tracks.ts +23 -0
  177. package/src/boxes/iso-base-media/esds/decoder-specific-config.ts +61 -0
  178. package/src/boxes/iso-base-media/esds/esds-descriptors.ts +33 -2
  179. package/src/boxes/iso-base-media/make-track.ts +157 -0
  180. package/src/boxes/iso-base-media/mdat/mdat.ts +131 -0
  181. package/src/boxes/iso-base-media/moov/moov.ts +8 -3
  182. package/src/boxes/iso-base-media/mvhd.ts +7 -1
  183. package/src/boxes/iso-base-media/process-box.ts +388 -38
  184. package/src/boxes/iso-base-media/stsd/av1c.ts +19 -0
  185. package/src/boxes/iso-base-media/stsd/avcc.ts +36 -0
  186. package/src/boxes/iso-base-media/stsd/colr.ts +49 -0
  187. package/src/boxes/iso-base-media/stsd/ctts.ts +55 -0
  188. package/src/boxes/iso-base-media/stsd/hvcc.ts +32 -0
  189. package/src/boxes/iso-base-media/stsd/keys.ts +2 -0
  190. package/src/boxes/iso-base-media/stsd/mebx.ts +8 -3
  191. package/src/boxes/iso-base-media/stsd/pasp.ts +32 -0
  192. package/src/boxes/iso-base-media/stsd/samples.ts +43 -16
  193. package/src/boxes/iso-base-media/stsd/stco.ts +50 -0
  194. package/src/boxes/iso-base-media/stsd/stsc.ts +61 -0
  195. package/src/boxes/iso-base-media/stsd/stsd.ts +10 -3
  196. package/src/boxes/iso-base-media/stsd/stss.ts +47 -0
  197. package/src/boxes/iso-base-media/stsd/stsz.ts +75 -0
  198. package/src/boxes/iso-base-media/tkhd.ts +63 -16
  199. package/src/boxes/iso-base-media/trak/trak.ts +8 -3
  200. package/src/boxes/iso-base-media/void-box.ts +4 -0
  201. package/src/boxes/webm/allowed-partial-segments.ts +1 -0
  202. package/src/boxes/webm/av1-codec-private.ts +113 -0
  203. package/src/boxes/webm/description.ts +101 -0
  204. package/src/boxes/webm/get-ready-tracks.ts +36 -0
  205. package/src/boxes/webm/get-track.ts +250 -0
  206. package/src/boxes/webm/parse-webm-header.ts +6 -2
  207. package/src/boxes/webm/segments/all-segments.ts +285 -0
  208. package/src/boxes/webm/segments/duration.ts +15 -8
  209. package/src/boxes/webm/segments/info.ts +18 -4
  210. package/src/boxes/webm/segments/main.ts +0 -13
  211. package/src/boxes/webm/segments/muxing.ts +1 -1
  212. package/src/boxes/webm/segments/parse-children.ts +132 -9
  213. package/src/boxes/webm/segments/seek-head.ts +17 -4
  214. package/src/boxes/webm/segments/seek-position.ts +1 -2
  215. package/src/boxes/webm/segments/seek.ts +31 -9
  216. package/src/boxes/webm/segments/track-entry.ts +475 -65
  217. package/src/boxes/webm/segments/tracks.ts +17 -7
  218. package/src/boxes/webm/segments/void.ts +4 -2
  219. package/src/boxes/webm/segments/writing.ts +1 -1
  220. package/src/boxes/webm/segments.ts +266 -42
  221. package/src/boxes/webm/traversal.ts +32 -0
  222. package/src/buffer-iterator.ts +140 -45
  223. package/src/combine-uint8array.ts +13 -0
  224. package/src/{from-web.ts → from-fetch.ts} +9 -1
  225. package/src/from-node.ts +8 -0
  226. package/src/from-web-file.ts +39 -0
  227. package/src/get-audio-codec.ts +213 -84
  228. package/src/get-dimensions.ts +25 -132
  229. package/src/get-duration.ts +4 -8
  230. package/src/get-fps.ts +27 -172
  231. package/src/get-sample-aspect-ratio.ts +204 -0
  232. package/src/get-sample-positions.ts +93 -0
  233. package/src/get-tracks.ts +147 -0
  234. package/src/get-video-codec.ts +62 -47
  235. package/src/has-all-info.ts +35 -5
  236. package/src/index.ts +8 -0
  237. package/src/make-hvc1-codec-strings.ts +55 -0
  238. package/src/options.ts +48 -9
  239. package/src/parse-media.ts +90 -21
  240. package/src/parse-result.ts +28 -4
  241. package/src/parse-video.ts +30 -7
  242. package/src/parser-context.ts +9 -0
  243. package/src/parser-state.ts +151 -0
  244. package/src/reader.ts +2 -2
  245. package/src/test/aspect-ratio.test.ts +42 -0
  246. package/src/test/av1.test.ts +108 -0
  247. package/src/test/duration.test.ts +5 -21
  248. package/src/test/matroska.test.ts +350 -31
  249. package/src/test/mvhd.test.ts +6 -1
  250. package/src/test/parse-esds.test.ts +29 -6
  251. package/src/test/parse-stco.test.ts +59 -0
  252. package/src/test/parse-stsc.test.ts +104 -0
  253. package/src/test/parse-stsz.test.ts +57 -0
  254. package/src/test/parse-stts.test.ts +1 -1
  255. package/src/test/parse-video.test.ts +23 -35
  256. package/src/test/parse-webm.test.ts +5 -5
  257. package/src/test/stream-local.test.ts +542 -46
  258. package/src/test/stream-remote.test.ts +15 -13
  259. package/src/test/stream-samples.test.ts +259 -0
  260. package/src/test/stsd.test.ts +60 -8
  261. package/src/test/tkhd.test.ts +4 -1
  262. package/src/traversal.ts +496 -0
  263. package/src/webcodec-sample-types.ts +30 -0
  264. package/tsconfig.json +5 -1
  265. package/tsconfig.tsbuildinfo +1 -1
  266. package/boxes.json +0 -1
  267. package/dist/boxes/iso-base-media/esds-descriptors.d.ts +0 -21
  268. package/dist/boxes/iso-base-media/esds-descriptors.js +0 -62
  269. package/dist/boxes/iso-base-media/esds.d.ts +0 -15
  270. package/dist/boxes/iso-base-media/esds.js +0 -27
  271. package/dist/get-codec.d.ts +0 -4
  272. package/dist/get-codec.js +0 -22
  273. /package/dist/{get-samples.d.ts → boxes/webm/bitstream/av1/frame.d.ts} +0 -0
  274. /package/dist/{get-samples.js → boxes/webm/bitstream/av1/frame.js} +0 -0
  275. /package/src/boxes/iso-base-media/{stts → stsd}/stts.ts +0 -0
@@ -1,7 +1,17 @@
1
1
  /* eslint-disable max-depth */
2
+ import type {TrakBox} from './boxes/iso-base-media/trak/trak';
3
+ import {parseAv1PrivateData} from './boxes/webm/av1-codec-private';
2
4
  import {trakBoxContainsVideo} from './get-fps';
5
+ import {
6
+ getAv1CBox,
7
+ getAvccBox,
8
+ getColrBox,
9
+ getHvccBox,
10
+ getVideoSample,
11
+ } from './get-sample-aspect-ratio';
3
12
  import type {KnownVideoCodecs} from './options';
4
13
  import type {AnySegment} from './parse-result';
14
+ import {getMoovBox, getStsdBox, getTraks} from './traversal';
5
15
 
6
16
  export const hasVideoCodec = (boxes: AnySegment[]): boolean => {
7
17
  try {
@@ -11,54 +21,55 @@ export const hasVideoCodec = (boxes: AnySegment[]): boolean => {
11
21
  }
12
22
  };
13
23
 
24
+ export const getVideoCodecString = (trakBox: TrakBox): string | null => {
25
+ const videoSample = getVideoSample(trakBox);
26
+ const avccBox = getAvccBox(trakBox);
27
+ const hvccBox = getHvccBox(trakBox);
28
+ const av1cBox = getAv1CBox(trakBox);
29
+
30
+ if (!videoSample) {
31
+ return null;
32
+ }
33
+
34
+ if (avccBox) {
35
+ return `${videoSample.format}.${avccBox.configurationString}`;
36
+ }
37
+
38
+ if (hvccBox) {
39
+ return `${videoSample.format}.${hvccBox.configurationString}`;
40
+ }
41
+
42
+ if (av1cBox) {
43
+ const colrAtom = getColrBox(videoSample);
44
+ return parseAv1PrivateData(av1cBox.privateData, colrAtom);
45
+ }
46
+
47
+ return videoSample.format;
48
+ };
49
+
14
50
  export const getVideoCodec = (boxes: AnySegment[]): KnownVideoCodecs | null => {
15
- const moovBox = boxes.find((b) => b.type === 'moov-box');
16
- if (moovBox && moovBox.type === 'moov-box') {
17
- const trakBox = moovBox.children.find(
18
- (b) => b.type === 'trak-box' && trakBoxContainsVideo(b),
19
- );
20
- if (trakBox && trakBox.type === 'trak-box') {
21
- const mdiaBox = trakBox.children.find(
22
- (b) => b.type === 'regular-box' && b.boxType === 'mdia',
23
- );
24
- if (
25
- mdiaBox &&
26
- mdiaBox.type === 'regular-box' &&
27
- mdiaBox.boxType === 'mdia'
28
- ) {
29
- const minfBox = mdiaBox?.children.find(
30
- (b) => b.type === 'regular-box' && b.boxType === 'minf',
31
- );
32
- if (
33
- minfBox &&
34
- minfBox.type === 'regular-box' &&
35
- minfBox.boxType === 'minf'
36
- ) {
37
- const stblBox = minfBox?.children.find(
38
- (b) => b.type === 'regular-box' && b.boxType === 'stbl',
39
- );
40
- if (stblBox && stblBox.type === 'regular-box') {
41
- const stsdBox = stblBox?.children.find(
42
- (b) => b.type === 'stsd-box',
43
- );
44
- if (stsdBox && stsdBox.type === 'stsd-box') {
45
- const videoSample = stsdBox.samples.find(
46
- (s) => s.type === 'video',
47
- );
48
- if (videoSample && videoSample.type === 'video') {
49
- if (videoSample.format === 'hvc1') {
50
- return 'h265';
51
- }
52
-
53
- if (videoSample.format === 'avc1') {
54
- return 'h264';
55
- }
56
-
57
- if (videoSample.format === 'ap4h') {
58
- return 'prores';
59
- }
60
- }
61
- }
51
+ const moovBox = getMoovBox(boxes);
52
+ if (moovBox) {
53
+ const trakBox = getTraks(moovBox).filter((t) => trakBoxContainsVideo(t))[0];
54
+ if (trakBox) {
55
+ const stsdBox = getStsdBox(trakBox);
56
+ if (stsdBox && stsdBox.type === 'stsd-box') {
57
+ const videoSample = stsdBox.samples.find((s) => s.type === 'video');
58
+ if (videoSample && videoSample.type === 'video') {
59
+ if (videoSample.format === 'hvc1') {
60
+ return 'h265';
61
+ }
62
+
63
+ if (videoSample.format === 'avc1') {
64
+ return 'h264';
65
+ }
66
+
67
+ if (videoSample.format === 'av01') {
68
+ return 'av1';
69
+ }
70
+
71
+ if (videoSample.format === 'ap4h') {
72
+ return 'prores';
62
73
  }
63
74
  }
64
75
  }
@@ -96,6 +107,10 @@ export const getVideoCodec = (boxes: AnySegment[]): KnownVideoCodecs | null => {
96
107
  if (trackType.codec === 'V_MPEG4/ISO/AVC') {
97
108
  return 'h264';
98
109
  }
110
+
111
+ if (trackType.codec === 'V_MPEGH/ISO/HEVC') {
112
+ return 'h265';
113
+ }
99
114
  }
100
115
  }
101
116
  }
@@ -2,13 +2,27 @@ import {hasAudioCodec} from './get-audio-codec';
2
2
  import {hasDimensions} from './get-dimensions';
3
3
  import {hasDuration} from './get-duration';
4
4
  import {hasFps} from './get-fps';
5
+ import {hasTracks} from './get-tracks';
5
6
  import {hasVideoCodec} from './get-video-codec';
6
7
  import type {Options} from './options';
7
8
  import type {ParseResult} from './parse-result';
9
+ import type {ParserState} from './parser-state';
8
10
 
9
11
  export const hasAllInfo = (
10
- options: Options<boolean, boolean, boolean, boolean, boolean, boolean>,
12
+ options: Options<
13
+ boolean,
14
+ boolean,
15
+ boolean,
16
+ boolean,
17
+ boolean,
18
+ boolean,
19
+ boolean,
20
+ boolean,
21
+ boolean,
22
+ boolean
23
+ >,
11
24
  parseResult: ParseResult,
25
+ state: ParserState,
12
26
  ) => {
13
27
  const keys = Object.entries(options)
14
28
  .filter(([, value]) => value)
@@ -18,6 +32,10 @@ export const hasAllInfo = (
18
32
  true,
19
33
  true,
20
34
  true,
35
+ true,
36
+ true,
37
+ true,
38
+ true,
21
39
  true
22
40
  >)[];
23
41
 
@@ -30,8 +48,12 @@ export const hasAllInfo = (
30
48
  return hasDuration(parseResult.segments);
31
49
  }
32
50
 
33
- if (key === 'dimensions') {
34
- return hasDimensions(parseResult.segments);
51
+ if (
52
+ key === 'dimensions' ||
53
+ key === 'rotation' ||
54
+ key === 'unrotatedDimension'
55
+ ) {
56
+ return hasDimensions(parseResult.segments, state);
35
57
  }
36
58
 
37
59
  if (key === 'fps') {
@@ -39,11 +61,19 @@ export const hasAllInfo = (
39
61
  }
40
62
 
41
63
  if (key === 'videoCodec') {
42
- return hasVideoCodec(parseResult.segments) !== null;
64
+ return hasVideoCodec(parseResult.segments);
43
65
  }
44
66
 
45
67
  if (key === 'audioCodec') {
46
- return hasAudioCodec(parseResult.segments) !== null;
68
+ return hasAudioCodec(parseResult.segments);
69
+ }
70
+
71
+ if (key === 'tracks') {
72
+ return hasTracks(parseResult.segments);
73
+ }
74
+
75
+ if (key === 'internalStats') {
76
+ return false;
47
77
  }
48
78
 
49
79
  throw new Error(`Unknown key: ${key satisfies never}`);
package/src/index.ts CHANGED
@@ -1 +1,9 @@
1
1
  export {parseMedia} from './parse-media';
2
+ export {
3
+ AudioSample,
4
+ OnAudioSample,
5
+ OnAudioTrack,
6
+ OnVideoSample,
7
+ OnVideoTrack,
8
+ VideoSample,
9
+ } from './webcodec-sample-types';
@@ -0,0 +1,55 @@
1
+ import type {BufferIterator} from './buffer-iterator';
2
+
3
+ export const getHvc1CodecString = (data: BufferIterator) => {
4
+ const configurationVersion = data.getUint8();
5
+ if (configurationVersion !== 1) {
6
+ throw new Error(`Unsupported HVCC version ${configurationVersion}`);
7
+ }
8
+
9
+ const generalProfileSpaceTierFlagAndIdc = data.getUint8();
10
+ let generalProfileCompatibility = data.getUint32();
11
+ // unsigned int(2) general_profile_space;
12
+ // unsigned int(1) general_tier_flag;
13
+ // unsigned int(5) general_profile_idc;
14
+
15
+ const generalProfileSpace = generalProfileSpaceTierFlagAndIdc >> 6;
16
+ const generalTierFlag = generalProfileSpaceTierFlagAndIdc >> 5;
17
+ const generalProfileIdc = generalProfileSpaceTierFlagAndIdc >> 0;
18
+
19
+ // general_constraint_indicator_flags(48)
20
+ const generalConstraintIndicator = data.getSlice(6);
21
+ const generalLevelIdc = data.getUint8();
22
+
23
+ let reversedGeneralProfileSpace = 0;
24
+ for (let i = 0; i < 32; i++) {
25
+ reversedGeneralProfileSpace |= generalProfileCompatibility & 1;
26
+ if (i === 31) break;
27
+
28
+ reversedGeneralProfileSpace <<= 1;
29
+ generalProfileCompatibility >>= 1;
30
+ }
31
+
32
+ const profileSpaceChar =
33
+ generalProfileSpace === 0
34
+ ? ''
35
+ : generalProfileSpace === 1
36
+ ? 'A'
37
+ : generalProfileSpace === 2
38
+ ? 'B'
39
+ : 'C';
40
+
41
+ const generalTierChar = generalTierFlag === 0 ? 'L' : 'H';
42
+
43
+ let hasByte = false;
44
+
45
+ let generalConstraintString = '';
46
+ for (let i = 5; i >= 0; i--) {
47
+ if (generalConstraintIndicator[i] || hasByte) {
48
+ generalConstraintString =
49
+ generalConstraintIndicator[i].toString(16) + generalConstraintString;
50
+ hasByte = true;
51
+ }
52
+ }
53
+
54
+ return `${profileSpaceChar}${generalProfileIdc.toString(16)}.${reversedGeneralProfileSpace.toString(16)}.${generalTierChar}${generalLevelIdc}.${generalConstraintString}`;
55
+ };
package/src/options.ts CHANGED
@@ -1,6 +1,10 @@
1
+ import type {} from './boxes/iso-base-media/mdat/mdat';
1
2
  import type {Dimensions} from './get-dimensions';
3
+ import type {AudioTrack, VideoTrack} from './get-tracks';
2
4
  import type {AnySegment} from './parse-result';
5
+ import type {InternalStats} from './parser-state';
3
6
  import type {ReaderInterface} from './reader';
7
+ import type {OnAudioTrack, OnVideoTrack} from './webcodec-sample-types';
4
8
 
5
9
  export type KnownVideoCodecs =
6
10
  | 'h264'
@@ -16,6 +20,7 @@ export type KnownAudioCodecs =
16
20
  | 'aiff'
17
21
  | 'opus'
18
22
  | 'pcm'
23
+ | 'vorbis'
19
24
  | 'unknown';
20
25
 
21
26
  export type Options<
@@ -25,6 +30,10 @@ export type Options<
25
30
  EnableFps extends boolean,
26
31
  EnableVideoCodec extends boolean,
27
32
  EnableAudioCodec extends boolean,
33
+ EnableTracks extends boolean,
34
+ EnableRotation extends boolean,
35
+ EnableUnrotatedDimensions extends boolean,
36
+ EnableInternalStats extends boolean,
28
37
  > = {
29
38
  dimensions?: EnableDimensions;
30
39
  durationInSeconds?: EnableDuration;
@@ -32,6 +41,10 @@ export type Options<
32
41
  fps?: EnableFps;
33
42
  videoCodec?: EnableVideoCodec;
34
43
  audioCodec?: EnableAudioCodec;
44
+ tracks?: EnableTracks;
45
+ rotation?: EnableRotation;
46
+ unrotatedDimension?: EnableUnrotatedDimensions;
47
+ internalStats?: EnableInternalStats;
35
48
  };
36
49
 
37
50
  export type Metadata<
@@ -41,12 +54,24 @@ export type Metadata<
41
54
  EnableFps extends boolean,
42
55
  EnableVideoCodec extends boolean,
43
56
  EnableAudioCodec extends boolean,
57
+ EnableTracks extends boolean,
58
+ EnableRotation extends boolean,
59
+ EnableUnrotatedDimensions extends boolean,
60
+ EnableInternalStats extends boolean,
44
61
  > = (EnableDimensions extends true ? {dimensions: Dimensions} : {}) &
45
62
  (EnableDuration extends true ? {durationInSeconds: number | null} : {}) &
46
63
  (EnableBoxes extends true ? {boxes: AnySegment[]} : {}) &
47
64
  (EnableFps extends true ? {fps: number | null} : {}) &
48
65
  (EnableVideoCodec extends true ? {videoCodec: KnownVideoCodecs | null} : {}) &
49
- (EnableAudioCodec extends true ? {audioCodec: KnownAudioCodecs | null} : {});
66
+ (EnableAudioCodec extends true ? {audioCodec: KnownAudioCodecs | null} : {}) &
67
+ (EnableTracks extends true
68
+ ? {videoTracks: VideoTrack[]; audioTracks: AudioTrack[]}
69
+ : {}) &
70
+ (EnableRotation extends true ? {rotation: number | null} : {}) &
71
+ (EnableUnrotatedDimensions extends true
72
+ ? {unrotatedDimension: Dimensions}
73
+ : {}) &
74
+ (EnableInternalStats extends true ? {internalStats: InternalStats} : {});
50
75
 
51
76
  export type ParseMedia = <
52
77
  EnableDimensions extends boolean,
@@ -55,24 +80,38 @@ export type ParseMedia = <
55
80
  EnableFps extends boolean,
56
81
  EnableVideoCodec extends boolean,
57
82
  EnableAudioCodec extends boolean,
58
- >(
59
- src: string,
60
- options: Options<
83
+ EnableTracks extends boolean,
84
+ EnableRotation extends boolean,
85
+ EnableUnrotatedDimensions extends boolean,
86
+ EnableInternalStats extends boolean,
87
+ >(options: {
88
+ src: string | File;
89
+ fields?: Options<
61
90
  EnableDimensions,
62
91
  EnableDuration,
63
92
  EnableBoxes,
64
93
  EnableFps,
65
94
  EnableVideoCodec,
66
- EnableAudioCodec
67
- >,
68
- readerInterface?: ReaderInterface,
69
- ) => Promise<
95
+ EnableAudioCodec,
96
+ EnableTracks,
97
+ EnableRotation,
98
+ EnableUnrotatedDimensions,
99
+ EnableInternalStats
100
+ >;
101
+ reader?: ReaderInterface;
102
+ onAudioTrack?: OnAudioTrack;
103
+ onVideoTrack?: OnVideoTrack;
104
+ }) => Promise<
70
105
  Metadata<
71
106
  EnableDimensions,
72
107
  EnableDuration,
73
108
  EnableBoxes,
74
109
  EnableFps,
75
110
  EnableVideoCodec,
76
- EnableAudioCodec
111
+ EnableAudioCodec,
112
+ EnableTracks,
113
+ EnableRotation,
114
+ EnableUnrotatedDimensions,
115
+ EnableInternalStats
77
116
  >
78
117
  >;
@@ -1,51 +1,92 @@
1
+ /* eslint-disable max-depth */
1
2
  import type {BufferIterator} from './buffer-iterator';
2
3
  import {getArrayBufferIterator} from './buffer-iterator';
3
- import {webReader} from './from-web';
4
+ import {fetchReader} from './from-fetch';
4
5
  import {getAudioCodec} from './get-audio-codec';
5
6
  import {getDimensions} from './get-dimensions';
6
7
  import {getDuration} from './get-duration';
7
8
  import {getFps} from './get-fps';
9
+ import {getTracks} from './get-tracks';
8
10
  import {getVideoCodec} from './get-video-codec';
9
11
  import {hasAllInfo} from './has-all-info';
10
12
  import type {Metadata, ParseMedia} from './options';
11
13
  import type {ParseResult} from './parse-result';
12
14
  import {parseVideo} from './parse-video';
15
+ import type {ParserContext} from './parser-context';
16
+ import {makeParserState} from './parser-state';
13
17
 
14
- export const parseMedia: ParseMedia = async (
18
+ export const parseMedia: ParseMedia = async ({
15
19
  src,
16
- options,
17
- readerInterface = webReader,
18
- ) => {
20
+ fields,
21
+ reader: readerInterface = fetchReader,
22
+ onAudioTrack,
23
+ onVideoTrack,
24
+ }) => {
19
25
  const {reader, contentLength} = await readerInterface.read(src, null);
20
26
  let currentReader = reader;
21
27
 
22
- const returnValue = {} as Metadata<true, true, true, true, true, true>;
28
+ const returnValue = {} as Metadata<
29
+ true,
30
+ true,
31
+ true,
32
+ true,
33
+ true,
34
+ true,
35
+ true,
36
+ true,
37
+ true,
38
+ true
39
+ >;
23
40
 
24
41
  let iterator: BufferIterator | null = null;
25
42
  let parseResult: ParseResult | null = null;
26
43
 
44
+ const state = makeParserState({
45
+ hasAudioCallbacks: onAudioTrack !== null,
46
+ hasVideoCallbacks: onVideoTrack !== null,
47
+ });
48
+
49
+ const options: ParserContext = {
50
+ canSkipVideoData: !(onAudioTrack || onVideoTrack),
51
+ onAudioTrack: onAudioTrack ?? null,
52
+ onVideoTrack: onVideoTrack ?? null,
53
+ parserState: state,
54
+ };
55
+
27
56
  while (parseResult === null || parseResult.status === 'incomplete') {
28
57
  const result = await currentReader.read();
29
- if (result.done) {
30
- break;
31
- }
32
58
 
33
59
  if (iterator) {
34
- iterator.addData(result.value);
60
+ if (!result.done) {
61
+ iterator.addData(result.value);
62
+ }
35
63
  } else {
64
+ if (result.done) {
65
+ throw new Error('Unexpectedly reached EOF');
66
+ }
67
+
36
68
  iterator = getArrayBufferIterator(
37
69
  result.value,
38
70
  contentLength ?? undefined,
39
71
  );
40
72
  }
41
73
 
42
- if (parseResult) {
43
- parseResult = parseResult.continueParsing();
74
+ if (parseResult && parseResult.status === 'incomplete') {
75
+ parseResult = await parseResult.continueParsing();
44
76
  } else {
45
- parseResult = parseVideo(iterator);
77
+ parseResult = await parseVideo({
78
+ iterator,
79
+ options,
80
+ });
46
81
  }
47
82
 
48
- if (hasAllInfo(options, parseResult)) {
83
+ // TODO Better: Check if no active listeners are registered
84
+ // Also maybe check for canSkipVideoData
85
+ if (
86
+ hasAllInfo(fields ?? {}, parseResult, state) &&
87
+ !onVideoTrack &&
88
+ !onAudioTrack
89
+ ) {
49
90
  if (!currentReader.closed) {
50
91
  currentReader.cancel(new Error('has all information'));
51
92
  }
@@ -75,29 +116,57 @@ export const parseMedia: ParseMedia = async (
75
116
  throw new Error('Could not parse video');
76
117
  }
77
118
 
78
- if (options.dimensions) {
79
- returnValue.dimensions = getDimensions(parseResult.segments);
119
+ if (fields?.dimensions) {
120
+ const dimensions = getDimensions(parseResult.segments, state);
121
+ returnValue.dimensions = {
122
+ width: dimensions.width,
123
+ height: dimensions.height,
124
+ };
80
125
  }
81
126
 
82
- if (options.durationInSeconds) {
127
+ if (fields?.unrotatedDimension) {
128
+ const dimensions = getDimensions(parseResult.segments, state);
129
+ returnValue.unrotatedDimension = {
130
+ width: dimensions.unrotatedWidth,
131
+ height: dimensions.unrotatedHeight,
132
+ };
133
+ }
134
+
135
+ if (fields?.rotation) {
136
+ const dimensions = getDimensions(parseResult.segments, state);
137
+ returnValue.rotation = dimensions.rotation;
138
+ }
139
+
140
+ if (fields?.durationInSeconds) {
83
141
  returnValue.durationInSeconds = getDuration(parseResult.segments);
84
142
  }
85
143
 
86
- if (options.fps) {
144
+ if (fields?.fps) {
87
145
  returnValue.fps = getFps(parseResult.segments);
88
146
  }
89
147
 
90
- if (options.videoCodec) {
148
+ if (fields?.videoCodec) {
91
149
  returnValue.videoCodec = getVideoCodec(parseResult.segments);
92
150
  }
93
151
 
94
- if (options.audioCodec) {
152
+ if (fields?.audioCodec) {
95
153
  returnValue.audioCodec = getAudioCodec(parseResult.segments);
96
154
  }
97
155
 
98
- if (options.boxes) {
156
+ if (fields?.tracks) {
157
+ const {audioTracks, videoTracks} = getTracks(parseResult.segments, state);
158
+ returnValue.audioTracks = audioTracks;
159
+ returnValue.videoTracks = videoTracks;
160
+ }
161
+
162
+ if (fields?.boxes) {
99
163
  returnValue.boxes = parseResult.segments;
100
164
  }
101
165
 
166
+ if (fields?.internalStats) {
167
+ returnValue.internalStats = state.getInternalStats();
168
+ }
169
+
170
+ iterator?.destroy();
102
171
  return returnValue;
103
172
  };
@@ -1,18 +1,30 @@
1
1
  import type {BaseBox} from './boxes/iso-base-media/base-type';
2
2
  import type {EsdsBox} from './boxes/iso-base-media/esds/esds';
3
3
  import type {FtypBox} from './boxes/iso-base-media/ftyp';
4
+ import type {MdatBox} from './boxes/iso-base-media/mdat/mdat';
4
5
  import type {MdhdBox} from './boxes/iso-base-media/mdhd';
5
6
  import type {MoovBox} from './boxes/iso-base-media/moov/moov';
6
7
  import type {MvhdBox} from './boxes/iso-base-media/mvhd';
8
+ import type {Av1CBox} from './boxes/iso-base-media/stsd/av1c';
9
+ import type {AvccBox} from './boxes/iso-base-media/stsd/avcc';
10
+ import type {ColorParameterBox} from './boxes/iso-base-media/stsd/colr';
11
+ import type {CttsBox} from './boxes/iso-base-media/stsd/ctts';
12
+ import type {HvccBox} from './boxes/iso-base-media/stsd/hvcc';
7
13
  import type {KeysBox} from './boxes/iso-base-media/stsd/keys';
8
14
  import type {MebxBox} from './boxes/iso-base-media/stsd/mebx';
15
+ import type {PaspBox} from './boxes/iso-base-media/stsd/pasp';
16
+ import type {StcoBox} from './boxes/iso-base-media/stsd/stco';
17
+ import type {StscBox} from './boxes/iso-base-media/stsd/stsc';
9
18
  import type {StsdBox} from './boxes/iso-base-media/stsd/stsd';
10
- import type {SttsBox} from './boxes/iso-base-media/stts/stts';
19
+ import type {StssBox} from './boxes/iso-base-media/stsd/stss';
20
+ import type {StszBox} from './boxes/iso-base-media/stsd/stsz';
21
+ import type {SttsBox} from './boxes/iso-base-media/stsd/stts';
11
22
  import type {TkhdBox} from './boxes/iso-base-media/tkhd';
12
23
  import type {TrakBox} from './boxes/iso-base-media/trak/trak';
24
+ import type {VoidBox} from './boxes/iso-base-media/void-box';
13
25
  import type {MatroskaSegment} from './boxes/webm/segments';
14
26
 
15
- interface RegularBox extends BaseBox {
27
+ export interface RegularBox extends BaseBox {
16
28
  boxType: string;
17
29
  boxSize: number;
18
30
  children: AnySegment[];
@@ -32,7 +44,19 @@ export type IsoBaseMediaBox =
32
44
  | TrakBox
33
45
  | SttsBox
34
46
  | MdhdBox
35
- | EsdsBox;
47
+ | EsdsBox
48
+ | MdatBox
49
+ | StszBox
50
+ | StcoBox
51
+ | StscBox
52
+ | AvccBox
53
+ | HvccBox
54
+ | VoidBox
55
+ | StssBox
56
+ | PaspBox
57
+ | CttsBox
58
+ | Av1CBox
59
+ | ColorParameterBox;
36
60
 
37
61
  export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
38
62
 
@@ -45,5 +69,5 @@ export type ParseResult =
45
69
  status: 'incomplete';
46
70
  segments: AnySegment[];
47
71
  skipTo: number | null;
48
- continueParsing: () => ParseResult;
72
+ continueParsing: () => Promise<ParseResult>;
49
73
  };
@@ -2,6 +2,13 @@ import {parseBoxes} from './boxes/iso-base-media/process-box';
2
2
  import {parseWebm} from './boxes/webm/parse-webm-header';
3
3
  import type {BufferIterator} from './buffer-iterator';
4
4
  import type {IsoBaseMediaBox, ParseResult} from './parse-result';
5
+ import type {ParserContext} from './parser-context';
6
+
7
+ export type PartialMdatBox = {
8
+ type: 'partial-mdat-box';
9
+ boxSize: number;
10
+ fileOffset: number;
11
+ };
5
12
 
6
13
  export type BoxAndNext =
7
14
  | {
@@ -12,18 +19,28 @@ export type BoxAndNext =
12
19
  }
13
20
  | {
14
21
  type: 'incomplete';
15
- };
22
+ }
23
+ | PartialMdatBox;
16
24
 
17
- export const parseVideo = (iterator: BufferIterator): ParseResult => {
25
+ export const parseVideo = ({
26
+ iterator,
27
+ options,
28
+ }: {
29
+ iterator: BufferIterator;
30
+ options: ParserContext;
31
+ }): Promise<ParseResult> => {
18
32
  if (iterator.bytesRemaining() === 0) {
19
- return {
33
+ return Promise.resolve({
20
34
  status: 'incomplete',
21
35
  segments: [],
22
36
  continueParsing: () => {
23
- return parseVideo(iterator);
37
+ return parseVideo({
38
+ iterator,
39
+ options,
40
+ });
24
41
  },
25
42
  skipTo: null,
26
- };
43
+ });
27
44
  }
28
45
 
29
46
  if (iterator.isIsoBaseMedia()) {
@@ -32,12 +49,18 @@ export const parseVideo = (iterator: BufferIterator): ParseResult => {
32
49
  maxBytes: Infinity,
33
50
  allowIncompleteBoxes: true,
34
51
  initialBoxes: [],
52
+ options,
53
+ continueMdat: false,
35
54
  });
36
55
  }
37
56
 
38
57
  if (iterator.isWebm()) {
39
- return parseWebm(iterator);
58
+ return Promise.resolve(parseWebm(iterator, options));
59
+ }
60
+
61
+ if (iterator.isMp3()) {
62
+ return Promise.reject(new Error('MP3 files are not yet supported'));
40
63
  }
41
64
 
42
- throw new Error('Unknown video format');
65
+ return Promise.reject(new Error('Unknown video format'));
43
66
  };