@remotion/media-parser 4.0.191 → 4.0.193

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 (66) hide show
  1. package/boxes.json +1 -0
  2. package/dist/boxes/iso-base-media/esds/esds-descriptors.d.ts +21 -0
  3. package/dist/boxes/iso-base-media/esds/esds-descriptors.js +62 -0
  4. package/dist/boxes/iso-base-media/esds/esds.d.ts +15 -0
  5. package/dist/boxes/iso-base-media/esds/esds.js +27 -0
  6. package/dist/boxes/iso-base-media/ftype.d.ts +9 -0
  7. package/dist/boxes/iso-base-media/ftype.js +31 -0
  8. package/dist/boxes/iso-base-media/mdhd.d.ts +14 -0
  9. package/dist/boxes/iso-base-media/mdhd.js +33 -0
  10. package/dist/boxes/iso-base-media/process-box.js +30 -0
  11. package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -0
  12. package/dist/boxes/iso-base-media/stsd/samples.js +28 -8
  13. package/dist/boxes/webm/parse-webm-header.js +4 -4
  14. package/dist/boxes/webm/segments/track-entry.d.ts +30 -0
  15. package/dist/boxes/webm/segments/track-entry.js +59 -8
  16. package/dist/boxes/webm/segments.d.ts +2 -2
  17. package/dist/boxes/webm/segments.js +18 -0
  18. package/dist/buffer-iterator.d.ts +2 -1
  19. package/dist/buffer-iterator.js +29 -8
  20. package/dist/from-node.js +6 -2
  21. package/dist/from-web.js +6 -1
  22. package/dist/get-audio-codec.d.ts +4 -0
  23. package/dist/get-audio-codec.js +106 -0
  24. package/dist/get-dimensions.js +6 -2
  25. package/dist/get-fps.d.ts +8 -0
  26. package/dist/get-fps.js +117 -9
  27. package/dist/get-video-codec.d.ts +4 -0
  28. package/dist/get-video-codec.js +79 -0
  29. package/dist/get-video-metadata.d.ts +2 -0
  30. package/dist/get-video-metadata.js +44 -0
  31. package/dist/has-all-info.d.ts +1 -1
  32. package/dist/has-all-info.js +8 -0
  33. package/dist/options.d.ts +11 -3
  34. package/dist/parse-media.js +27 -6
  35. package/dist/parse-result.d.ts +3 -1
  36. package/dist/read-and-increment-offset.d.ts +28 -0
  37. package/dist/read-and-increment-offset.js +177 -0
  38. package/dist/reader.d.ts +5 -1
  39. package/package.json +2 -2
  40. package/src/boxes/iso-base-media/esds/esds-descriptors.ts +104 -0
  41. package/src/boxes/iso-base-media/esds/esds.ts +49 -0
  42. package/src/boxes/iso-base-media/mdhd.ts +56 -0
  43. package/src/boxes/iso-base-media/process-box.ts +35 -0
  44. package/src/boxes/iso-base-media/stsd/samples.ts +36 -8
  45. package/src/boxes/webm/parse-webm-header.ts +4 -4
  46. package/src/boxes/webm/segments/track-entry.ts +103 -11
  47. package/src/boxes/webm/segments.ts +43 -1
  48. package/src/buffer-iterator.ts +36 -10
  49. package/src/from-node.ts +6 -4
  50. package/src/from-web.ts +8 -1
  51. package/src/get-audio-codec.ts +143 -0
  52. package/src/get-dimensions.ts +11 -4
  53. package/src/get-fps.ts +175 -9
  54. package/src/get-video-codec.ts +104 -0
  55. package/src/has-all-info.ts +19 -2
  56. package/src/options.ts +43 -3
  57. package/src/parse-media.ts +35 -7
  58. package/src/parse-result.ts +5 -1
  59. package/src/reader.ts +5 -1
  60. package/src/test/matroska.test.ts +6 -7
  61. package/src/test/parse-esds.test.ts +75 -0
  62. package/src/test/parse-webm.test.ts +2 -0
  63. package/src/test/stream-local.test.ts +93 -5
  64. package/src/test/stream-remote.test.ts +41 -0
  65. package/src/test/stsd.test.ts +52 -5
  66. package/tsconfig.tsbuildinfo +1 -1
@@ -54,8 +54,12 @@ const matchesPattern = (pattern) => {
54
54
  const makeOffsetCounter = () => {
55
55
  return new OffsetCounter(0);
56
56
  };
57
- const getArrayBufferIterator = (initialData) => {
58
- let data = initialData;
57
+ const getArrayBufferIterator = (initialData, maxBytes) => {
58
+ const buf = new ArrayBuffer(initialData.byteLength, {
59
+ maxByteLength: maxBytes !== null && maxBytes !== void 0 ? maxBytes : 1000000000,
60
+ });
61
+ let data = new Uint8Array(buf);
62
+ data.set(initialData);
59
63
  let view = new DataView(data.buffer);
60
64
  const counter = makeOffsetCounter();
61
65
  const getSlice = (amount) => {
@@ -71,15 +75,24 @@ const getArrayBufferIterator = (initialData) => {
71
75
  const getFourByteNumber = () => {
72
76
  return ((getUint8() << 24) | (getUint8() << 16) | (getUint8() << 8) | getUint8());
73
77
  };
78
+ const getPaddedFourByteNumber = () => {
79
+ let lastInt = 128;
80
+ while (((lastInt = getUint8()), lastInt === 128)) {
81
+ // Do nothing
82
+ }
83
+ return lastInt;
84
+ };
74
85
  const getUint32 = () => {
75
86
  const val = view.getUint32(counter.getDiscardedOffset());
76
87
  counter.increment(4);
77
88
  return val;
78
89
  };
79
90
  const addData = (newData) => {
80
- const newArray = new Uint8Array(data.buffer.byteLength + newData.byteLength);
81
- newArray.set(data);
82
- newArray.set(new Uint8Array(newData), data.byteLength);
91
+ const oldLength = buf.byteLength;
92
+ const newLength = oldLength + newData.byteLength;
93
+ buf.resize(newLength);
94
+ const newArray = new Uint8Array(buf);
95
+ newArray.set(newData, oldLength);
83
96
  data = newArray;
84
97
  view = new DataView(data.buffer);
85
98
  };
@@ -98,9 +111,9 @@ const getArrayBufferIterator = (initialData) => {
98
111
  const removeBytesRead = () => {
99
112
  const bytesToRemove = counter.getDiscardedOffset();
100
113
  counter.discardBytes(bytesToRemove);
101
- const newArray = new Uint8Array(data.buffer.byteLength - bytesToRemove);
102
- newArray.set(data.slice(bytesToRemove));
103
- data = newArray;
114
+ const newData = data.slice(bytesToRemove);
115
+ data.set(newData);
116
+ buf.resize(newData.byteLength);
104
117
  view = new DataView(data.buffer);
105
118
  };
106
119
  return {
@@ -120,6 +133,7 @@ const getArrayBufferIterator = (initialData) => {
120
133
  const atom = getSlice(4);
121
134
  return new TextDecoder().decode(atom);
122
135
  },
136
+ getPaddedFourByteNumber,
123
137
  getMatroskaSegmentId: () => {
124
138
  const first = getSlice(1);
125
139
  const firstOneString = `0x${Array.from(new Uint8Array(first))
@@ -139,6 +153,10 @@ const getArrayBufferIterator = (initialData) => {
139
153
  '0xe0',
140
154
  '0xb0',
141
155
  '0xba',
156
+ '0x9a',
157
+ '0xe1',
158
+ '0xbf',
159
+ '0x88',
142
160
  ];
143
161
  if (knownIdsWithOneLength.includes(firstOneString)) {
144
162
  return firstOneString;
@@ -155,6 +173,9 @@ const getArrayBufferIterator = (initialData) => {
155
173
  '0x4489',
156
174
  '0x55ee',
157
175
  '0x55b0',
176
+ '0x7ba9',
177
+ '0x63a2',
178
+ '0x73a4',
158
179
  ];
159
180
  const firstTwoString = `${firstOneString}${Array.from(new Uint8Array(firstTwo))
160
181
  .map((b) => {
package/dist/from-node.js CHANGED
@@ -5,12 +5,16 @@ const fs_1 = require("fs");
5
5
  const promises_1 = require("node:fs/promises");
6
6
  const stream_1 = require("stream");
7
7
  exports.nodeReader = {
8
- read: (src, range) => {
8
+ read: async (src, range) => {
9
9
  const stream = (0, fs_1.createReadStream)(src, {
10
10
  start: range === null ? 0 : range[0],
11
11
  end: range === null ? Infinity : range[1],
12
12
  });
13
- return Promise.resolve(stream_1.Readable.toWeb(stream).getReader());
13
+ const stats = await (0, promises_1.stat)(src);
14
+ return {
15
+ reader: stream_1.Readable.toWeb(stream).getReader(),
16
+ contentLength: stats.size,
17
+ };
14
18
  },
15
19
  getLength: async (src) => {
16
20
  const stats = await (0, promises_1.stat)(src);
package/dist/from-web.js CHANGED
@@ -21,8 +21,13 @@ exports.webReader = {
21
21
  if (!res.body) {
22
22
  throw new Error('No body');
23
23
  }
24
+ const length = res.headers.get('content-length');
25
+ if (!length) {
26
+ throw new Error('No content-length');
27
+ }
28
+ const contentLength = length === null ? null : parseInt(length, 10);
24
29
  const reader = res.body.getReader();
25
- return reader;
30
+ return { reader, contentLength };
26
31
  },
27
32
  getLength: async (src) => {
28
33
  const res = await fetch(src, {
@@ -0,0 +1,4 @@
1
+ import type { KnownAudioCodecs } from './options';
2
+ import type { AnySegment } from './parse-result';
3
+ export declare const hasAudioCodec: (boxes: AnySegment[]) => boolean;
4
+ export declare const getAudioCodec: (boxes: AnySegment[]) => KnownAudioCodecs | null;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAudioCodec = exports.hasAudioCodec = void 0;
4
+ const get_fps_1 = require("./get-fps");
5
+ const hasAudioCodec = (boxes) => {
6
+ try {
7
+ return (0, exports.getAudioCodec)(boxes) !== null;
8
+ }
9
+ catch (e) {
10
+ return false;
11
+ }
12
+ };
13
+ exports.hasAudioCodec = hasAudioCodec;
14
+ const onEsdsBox = (child) => {
15
+ if (child && child.type === 'esds-box') {
16
+ const descriptor = child.descriptors.find((d) => d.type === 'decoder-config-descriptor');
17
+ if (descriptor && descriptor.type === 'decoder-config-descriptor') {
18
+ return descriptor.objectTypeIndication;
19
+ }
20
+ }
21
+ return null;
22
+ };
23
+ const onSample = (sample) => {
24
+ if (!sample) {
25
+ return null;
26
+ }
27
+ if (sample.type !== 'audio') {
28
+ return null;
29
+ }
30
+ if (sample.format === 'sowt') {
31
+ return 'aiff';
32
+ }
33
+ const child = sample.children.find((c) => c.type === 'esds-box');
34
+ if (child && child.type === 'esds-box') {
35
+ const ret = onEsdsBox(child);
36
+ if (ret) {
37
+ return ret;
38
+ }
39
+ }
40
+ };
41
+ const getAudioCodec = (boxes) => {
42
+ const moovBox = boxes.find((b) => b.type === 'moov-box');
43
+ if (moovBox && moovBox.type === 'moov-box') {
44
+ const trakBox = moovBox.children.find((b) => b.type === 'trak-box' && (0, get_fps_1.trakBoxContainsAudio)(b));
45
+ if (trakBox && trakBox.type === 'trak-box') {
46
+ const mdiaBox = trakBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'mdia');
47
+ if (mdiaBox &&
48
+ mdiaBox.type === 'regular-box' &&
49
+ mdiaBox.boxType === 'mdia') {
50
+ const minfBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'minf');
51
+ if (minfBox &&
52
+ minfBox.type === 'regular-box' &&
53
+ minfBox.boxType === 'minf') {
54
+ const stblBox = minfBox === null || minfBox === void 0 ? void 0 : minfBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'stbl');
55
+ if (stblBox && stblBox.type === 'regular-box') {
56
+ const stsdBox = stblBox === null || stblBox === void 0 ? void 0 : stblBox.children.find((b) => b.type === 'stsd-box');
57
+ if (stsdBox && stsdBox.type === 'stsd-box') {
58
+ const sample = stsdBox.samples.find((s) => s.type === 'audio');
59
+ if (sample && sample.type === 'audio') {
60
+ const ret = onSample(sample);
61
+ if (ret) {
62
+ return ret;
63
+ }
64
+ const waveBox = sample.children.find((b) => b.type === 'regular-box' && b.boxType === 'wave');
65
+ if (waveBox &&
66
+ waveBox.type === 'regular-box' &&
67
+ waveBox.boxType === 'wave') {
68
+ const esdsBox = waveBox.children.find((b) => b.type === 'esds-box');
69
+ if (esdsBox && esdsBox.type === 'esds-box') {
70
+ const ret2 = onEsdsBox(esdsBox);
71
+ if (ret2) {
72
+ return ret2;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ const mainSegment = boxes.find((b) => b.type === 'main-segment');
84
+ if (!mainSegment || mainSegment.type !== 'main-segment') {
85
+ return null;
86
+ }
87
+ const tracksSegment = mainSegment.children.find((b) => b.type === 'tracks-segment');
88
+ if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
89
+ return null;
90
+ }
91
+ for (const track of tracksSegment.children) {
92
+ if (track.type === 'track-entry-segment') {
93
+ const trackType = track.children.find((b) => b.type === 'codec-segment');
94
+ if (trackType && trackType.type === 'codec-segment') {
95
+ if (trackType.codec === 'A_OPUS') {
96
+ return 'opus';
97
+ }
98
+ if (trackType.codec === 'A_PCM/INT/LIT') {
99
+ return 'pcm';
100
+ }
101
+ }
102
+ }
103
+ }
104
+ return null;
105
+ };
106
+ exports.getAudioCodec = getAudioCodec;
@@ -6,8 +6,12 @@ const getDimensionsFromMatroska = (segments) => {
6
6
  if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
7
7
  throw new Error('No tracks segment');
8
8
  }
9
- // TODO: What if there are multiple video tracks, or audio track is first?
10
- const trackEntrySegment = tracksSegment.children.find((b) => b.type === 'track-entry-segment');
9
+ const trackEntrySegment = tracksSegment.children.find((b) => {
10
+ if (b.type !== 'track-entry-segment') {
11
+ return false;
12
+ }
13
+ return (b.children.find((c) => c.type === 'codec-segment' && c.codec.startsWith('V_')) !== undefined);
14
+ });
11
15
  if (!trackEntrySegment || trackEntrySegment.type !== 'track-entry-segment') {
12
16
  throw new Error('No track entry segment');
13
17
  }
package/dist/get-fps.d.ts CHANGED
@@ -1,3 +1,11 @@
1
1
  import type { AnySegment } from './parse-result';
2
+ type TimescaleAndDuration = {
3
+ timescale: number;
4
+ duration: number;
5
+ };
6
+ export declare const trakBoxContainsAudio: (trakBox: AnySegment) => boolean;
7
+ export declare const trakBoxContainsVideo: (trakBox: AnySegment) => boolean;
8
+ export declare const getTimescaleAndDuration: (boxes: AnySegment[]) => TimescaleAndDuration | null;
2
9
  export declare const getFps: (segments: AnySegment[]) => number | null;
3
10
  export declare const hasFps: (boxes: AnySegment[]) => boolean;
11
+ export {};
package/dist/get-fps.js CHANGED
@@ -1,16 +1,122 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasFps = exports.getFps = void 0;
4
- const calculateFps = (sttsBox, timeScale) => {
5
- let sum = 0;
3
+ exports.hasFps = exports.getFps = exports.getTimescaleAndDuration = exports.trakBoxContainsVideo = exports.trakBoxContainsAudio = void 0;
4
+ const calculateFps = ({ sttsBox, timeScale, durationInSamples, }) => {
6
5
  let totalSamples = 0;
7
6
  for (const sample of sttsBox.sampleDistribution) {
8
- sum += sample.sampleCount * sample.sampleDelta;
9
7
  totalSamples += sample.sampleCount;
10
8
  }
11
- return timeScale / (sum / totalSamples);
9
+ const durationInSeconds = durationInSamples / timeScale;
10
+ const fps = totalSamples / durationInSeconds;
11
+ return fps;
12
12
  };
13
+ const trakBoxContainsAudio = (trakBox) => {
14
+ if (trakBox.type !== 'trak-box') {
15
+ return false;
16
+ }
17
+ const { children } = trakBox;
18
+ const mediaBoxes = children.filter((c) => c.type === 'regular-box' && c.boxType === 'mdia');
19
+ if (!mediaBoxes || mediaBoxes.length === 0) {
20
+ return false;
21
+ }
22
+ const firstMediaBox = mediaBoxes[0];
23
+ if (firstMediaBox.type !== 'regular-box' ||
24
+ firstMediaBox.boxType !== 'mdia') {
25
+ return false;
26
+ }
27
+ const minf = firstMediaBox.children.find((c) => c.type === 'regular-box' && c.boxType === 'minf');
28
+ if (!minf || minf.type !== 'regular-box' || minf.boxType !== 'minf') {
29
+ return false;
30
+ }
31
+ const stbl = minf.children.find((c) => c.type === 'regular-box' && c.boxType === 'stbl');
32
+ if (!stbl || stbl.type !== 'regular-box' || stbl.boxType !== 'stbl') {
33
+ return false;
34
+ }
35
+ const stsd = stbl.children.find((c) => c.type === 'stsd-box');
36
+ if (!stsd || stsd.type !== 'stsd-box') {
37
+ return false;
38
+ }
39
+ const videoSample = stsd.samples.find((s) => s.type === 'audio');
40
+ if (!videoSample || videoSample.type !== 'audio') {
41
+ return false;
42
+ }
43
+ return true;
44
+ };
45
+ exports.trakBoxContainsAudio = trakBoxContainsAudio;
46
+ const trakBoxContainsVideo = (trakBox) => {
47
+ if (trakBox.type !== 'trak-box') {
48
+ return false;
49
+ }
50
+ const { children } = trakBox;
51
+ const mediaBoxes = children.filter((c) => c.type === 'regular-box' && c.boxType === 'mdia');
52
+ if (!mediaBoxes || mediaBoxes.length === 0) {
53
+ return false;
54
+ }
55
+ const firstMediaBox = mediaBoxes[0];
56
+ if (firstMediaBox.type !== 'regular-box' ||
57
+ firstMediaBox.boxType !== 'mdia') {
58
+ return false;
59
+ }
60
+ const minf = firstMediaBox.children.find((c) => c.type === 'regular-box' && c.boxType === 'minf');
61
+ if (!minf || minf.type !== 'regular-box' || minf.boxType !== 'minf') {
62
+ return false;
63
+ }
64
+ const stbl = minf.children.find((c) => c.type === 'regular-box' && c.boxType === 'stbl');
65
+ if (!stbl || stbl.type !== 'regular-box' || stbl.boxType !== 'stbl') {
66
+ return false;
67
+ }
68
+ const stsd = stbl.children.find((c) => c.type === 'stsd-box');
69
+ if (!stsd || stsd.type !== 'stsd-box') {
70
+ return false;
71
+ }
72
+ const videoSample = stsd.samples.find((s) => s.type === 'video');
73
+ if (!videoSample || videoSample.type !== 'video') {
74
+ return false;
75
+ }
76
+ return true;
77
+ };
78
+ exports.trakBoxContainsVideo = trakBoxContainsVideo;
79
+ const getTimescaleAndDuration = (boxes) => {
80
+ const moovBox = boxes.find((s) => s.type === 'moov-box');
81
+ if (!moovBox || moovBox.type !== 'moov-box') {
82
+ return null;
83
+ }
84
+ const { children } = moovBox;
85
+ const trackBoxes = children.filter((c) => c.type === 'trak-box');
86
+ if (!trackBoxes || trackBoxes.length === 0) {
87
+ return null;
88
+ }
89
+ const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
90
+ if (!trackBox || trackBox.type !== 'trak-box') {
91
+ return null;
92
+ }
93
+ const trackBoxChildren = trackBox.children;
94
+ if (!trackBoxChildren || trackBoxChildren.length === 0) {
95
+ return null;
96
+ }
97
+ const mdiaBox = trackBoxChildren.find((c) => c.type === 'regular-box' && c.boxType === 'mdia');
98
+ if (!mdiaBox ||
99
+ mdiaBox.type !== 'regular-box' ||
100
+ mdiaBox.boxType !== 'mdia') {
101
+ return null;
102
+ }
103
+ const mdhdBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((c) => c.type === 'mdhd-box');
104
+ if (mdhdBox && mdhdBox.type === 'mdhd-box') {
105
+ return { timescale: mdhdBox.timescale, duration: mdhdBox.duration };
106
+ }
107
+ const mvhdBox = moovBox.children.find((c) => c.type === 'mvhd-box');
108
+ if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
109
+ return null;
110
+ }
111
+ const { timeScale, durationInUnits } = mvhdBox;
112
+ return { timescale: timeScale, duration: durationInUnits };
113
+ };
114
+ exports.getTimescaleAndDuration = getTimescaleAndDuration;
13
115
  const getFps = (segments) => {
116
+ const timescaleAndDuration = (0, exports.getTimescaleAndDuration)(segments);
117
+ if (!timescaleAndDuration) {
118
+ return null;
119
+ }
14
120
  const moovBox = segments.find((s) => s.type === 'moov-box');
15
121
  if (!moovBox || moovBox.type !== 'moov-box') {
16
122
  return null;
@@ -19,14 +125,12 @@ const getFps = (segments) => {
19
125
  if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
20
126
  return null;
21
127
  }
22
- const { timeScale } = mvhdBox;
23
128
  const { children } = moovBox;
24
129
  const trackBoxes = children.filter((c) => c.type === 'trak-box');
25
130
  if (!trackBoxes || trackBoxes.length === 0) {
26
131
  return null;
27
132
  }
28
- // TODO: What if the video track is not the first track?
29
- const trackBox = trackBoxes[0];
133
+ const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
30
134
  if (!trackBox || trackBox.type !== 'trak-box') {
31
135
  return null;
32
136
  }
@@ -56,7 +160,11 @@ const getFps = (segments) => {
56
160
  if (!sttsBox || sttsBox.type !== 'stts-box') {
57
161
  return null;
58
162
  }
59
- return calculateFps(sttsBox, timeScale);
163
+ return calculateFps({
164
+ sttsBox,
165
+ timeScale: timescaleAndDuration.timescale,
166
+ durationInSamples: timescaleAndDuration.duration,
167
+ });
60
168
  };
61
169
  exports.getFps = getFps;
62
170
  const hasFps = (boxes) => {
@@ -0,0 +1,4 @@
1
+ import type { KnownVideoCodecs } from './options';
2
+ import type { AnySegment } from './parse-result';
3
+ export declare const hasVideoCodec: (boxes: AnySegment[]) => boolean;
4
+ export declare const getVideoCodec: (boxes: AnySegment[]) => KnownVideoCodecs | null;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getVideoCodec = exports.hasVideoCodec = void 0;
4
+ /* eslint-disable max-depth */
5
+ const get_fps_1 = require("./get-fps");
6
+ const hasVideoCodec = (boxes) => {
7
+ try {
8
+ return (0, exports.getVideoCodec)(boxes) !== null;
9
+ }
10
+ catch (e) {
11
+ return false;
12
+ }
13
+ };
14
+ exports.hasVideoCodec = hasVideoCodec;
15
+ const getVideoCodec = (boxes) => {
16
+ const moovBox = boxes.find((b) => b.type === 'moov-box');
17
+ if (moovBox && moovBox.type === 'moov-box') {
18
+ const trakBox = moovBox.children.find((b) => b.type === 'trak-box' && (0, get_fps_1.trakBoxContainsVideo)(b));
19
+ if (trakBox && trakBox.type === 'trak-box') {
20
+ const mdiaBox = trakBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'mdia');
21
+ if (mdiaBox &&
22
+ mdiaBox.type === 'regular-box' &&
23
+ mdiaBox.boxType === 'mdia') {
24
+ const minfBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'minf');
25
+ if (minfBox &&
26
+ minfBox.type === 'regular-box' &&
27
+ minfBox.boxType === 'minf') {
28
+ const stblBox = minfBox === null || minfBox === void 0 ? void 0 : minfBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'stbl');
29
+ if (stblBox && stblBox.type === 'regular-box') {
30
+ const stsdBox = stblBox === null || stblBox === void 0 ? void 0 : stblBox.children.find((b) => b.type === 'stsd-box');
31
+ if (stsdBox && stsdBox.type === 'stsd-box') {
32
+ const videoSample = stsdBox.samples.find((s) => s.type === 'video');
33
+ if (videoSample && videoSample.type === 'video') {
34
+ if (videoSample.format === 'hvc1') {
35
+ return 'h265';
36
+ }
37
+ if (videoSample.format === 'avc1') {
38
+ return 'h264';
39
+ }
40
+ if (videoSample.format === 'ap4h') {
41
+ return 'prores';
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ const mainSegment = boxes.find((b) => b.type === 'main-segment');
51
+ if (!mainSegment || mainSegment.type !== 'main-segment') {
52
+ return null;
53
+ }
54
+ const tracksSegment = mainSegment.children.find((b) => b.type === 'tracks-segment');
55
+ if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
56
+ return null;
57
+ }
58
+ for (const track of tracksSegment.children) {
59
+ if (track.type === 'track-entry-segment') {
60
+ const trackType = track.children.find((b) => b.type === 'codec-segment');
61
+ if (trackType && trackType.type === 'codec-segment') {
62
+ if (trackType.codec === 'V_VP8') {
63
+ return 'vp8';
64
+ }
65
+ if (trackType.codec === 'V_VP9') {
66
+ return 'vp9';
67
+ }
68
+ if (trackType.codec === 'V_AV1') {
69
+ return 'av1';
70
+ }
71
+ if (trackType.codec === 'V_MPEG4/ISO/AVC') {
72
+ return 'h264';
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return null;
78
+ };
79
+ exports.getVideoCodec = getVideoCodec;
@@ -0,0 +1,2 @@
1
+ import type { ParseMedia } from './options';
2
+ export declare const parseMedia: ParseMedia;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMedia = void 0;
4
+ const buffer_iterator_1 = require("./buffer-iterator");
5
+ const from_web_1 = require("./from-web");
6
+ const get_dimensions_1 = require("./get-dimensions");
7
+ const get_duration_1 = require("./get-duration");
8
+ const get_fps_1 = require("./get-fps");
9
+ const has_all_info_1 = require("./has-all-info");
10
+ const parse_video_1 = require("./parse-video");
11
+ const parseMedia = async (src, options, readerInterface = from_web_1.webReader) => {
12
+ const reader = await readerInterface.read(src, null);
13
+ const returnValue = {};
14
+ const iterator = (0, buffer_iterator_1.getArrayBufferIterator)(new Uint8Array([]));
15
+ let parseResult = (0, parse_video_1.parseVideo)(iterator);
16
+ while (parseResult.status === 'incomplete') {
17
+ const result = await reader.read();
18
+ if (result.done) {
19
+ break;
20
+ }
21
+ iterator.addData(result.value);
22
+ parseResult = parseResult.continueParsing();
23
+ if ((0, has_all_info_1.hasAllInfo)(options, parseResult)) {
24
+ if (!reader.closed) {
25
+ reader.cancel(new Error('has all information'));
26
+ }
27
+ break;
28
+ }
29
+ }
30
+ if (options.dimensions) {
31
+ returnValue.dimensions = (0, get_dimensions_1.getDimensions)(parseResult.segments);
32
+ }
33
+ if (options.durationInSeconds) {
34
+ returnValue.durationInSeconds = (0, get_duration_1.getDuration)(parseResult.segments);
35
+ }
36
+ if (options.fps) {
37
+ returnValue.fps = (0, get_fps_1.getFps)(parseResult.segments);
38
+ }
39
+ if (options.boxes) {
40
+ returnValue.boxes = parseResult.segments;
41
+ }
42
+ return returnValue;
43
+ };
44
+ exports.parseMedia = parseMedia;
@@ -1,3 +1,3 @@
1
1
  import type { Options } from './options';
2
2
  import type { ParseResult } from './parse-result';
3
- export declare const hasAllInfo: (options: Options<boolean, boolean, boolean, boolean>, parseResult: ParseResult) => boolean;
3
+ export declare const hasAllInfo: (options: Options<boolean, boolean, boolean, boolean, boolean, boolean>, parseResult: ParseResult) => boolean;
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasAllInfo = void 0;
4
+ const get_audio_codec_1 = require("./get-audio-codec");
4
5
  const get_dimensions_1 = require("./get-dimensions");
5
6
  const get_duration_1 = require("./get-duration");
6
7
  const get_fps_1 = require("./get-fps");
8
+ const get_video_codec_1 = require("./get-video-codec");
7
9
  const hasAllInfo = (options, parseResult) => {
8
10
  const keys = Object.entries(options)
9
11
  .filter(([, value]) => value)
@@ -21,6 +23,12 @@ const hasAllInfo = (options, parseResult) => {
21
23
  if (key === 'fps') {
22
24
  return (0, get_fps_1.hasFps)(parseResult.segments) !== null;
23
25
  }
26
+ if (key === 'videoCodec') {
27
+ return (0, get_video_codec_1.hasVideoCodec)(parseResult.segments) !== null;
28
+ }
29
+ if (key === 'audioCodec') {
30
+ return (0, get_audio_codec_1.hasAudioCodec)(parseResult.segments) !== null;
31
+ }
24
32
  throw new Error(`Unknown key: ${key}`);
25
33
  });
26
34
  };
package/dist/options.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import type { Dimensions } from './get-dimensions';
2
2
  import type { AnySegment } from './parse-result';
3
3
  import type { ReaderInterface } from './reader';
4
- export type Options<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean> = {
4
+ export type KnownVideoCodecs = 'h264' | 'h265' | 'vp8' | 'vp9' | 'av1' | 'prores';
5
+ export type KnownAudioCodecs = 'aac' | 'mp3' | 'aiff' | 'opus' | 'pcm' | 'unknown';
6
+ export type Options<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean> = {
5
7
  dimensions?: EnableDimensions;
6
8
  durationInSeconds?: EnableDuration;
7
9
  boxes?: EnableBoxes;
8
10
  fps?: EnableFps;
11
+ videoCodec?: EnableVideoCodec;
12
+ audioCodec?: EnableAudioCodec;
9
13
  };
10
- export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean> = (EnableDimensions extends true ? {
14
+ export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean> = (EnableDimensions extends true ? {
11
15
  dimensions: Dimensions;
12
16
  } : {}) & (EnableDuration extends true ? {
13
17
  durationInSeconds: number | null;
@@ -15,5 +19,9 @@ export type Metadata<EnableDimensions extends boolean, EnableDuration extends bo
15
19
  boxes: AnySegment[];
16
20
  } : {}) & (EnableFps extends true ? {
17
21
  fps: number | null;
22
+ } : {}) & (EnableVideoCodec extends true ? {
23
+ videoCodec: KnownVideoCodecs | null;
24
+ } : {}) & (EnableAudioCodec extends true ? {
25
+ audioCodec: KnownAudioCodecs | null;
18
26
  } : {});
19
- export type ParseMedia = <EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean>(src: string, options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>, readerInterface?: ReaderInterface) => Promise<Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>>;
27
+ export type ParseMedia = <EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean>(src: string, options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec, EnableAudioCodec>, readerInterface?: ReaderInterface) => Promise<Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec, EnableAudioCodec>>;