music-metadata 11.3.0 → 11.5.0

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 (42) hide show
  1. package/LICENSE.txt +9 -9
  2. package/README.md +859 -859
  3. package/lib/ParserFactory.d.ts +1 -1
  4. package/lib/ParserFactory.js +10 -1
  5. package/lib/aiff/AiffParser.js +1 -0
  6. package/lib/apev2/APEv2Parser.d.ts +1 -1
  7. package/lib/apev2/APEv2Parser.js +5 -4
  8. package/lib/asf/AsfObject.d.ts +1 -1
  9. package/lib/asf/AsfObject.js +1 -1
  10. package/lib/common/GenericTagMapper.d.ts +1 -1
  11. package/lib/common/GenericTagMapper.js +1 -1
  12. package/lib/common/MetadataCollector.d.ts +2 -0
  13. package/lib/common/MetadataCollector.js +5 -1
  14. package/lib/core.d.ts +2 -2
  15. package/lib/core.js +3 -6
  16. package/lib/dsdiff/DsdiffParser.js +1 -0
  17. package/lib/dsf/DsfParser.js +1 -0
  18. package/lib/ebml/EbmlIterator.js +1 -1
  19. package/lib/flac/FlacParser.js +1 -0
  20. package/lib/mp4/Atom.js +1 -1
  21. package/lib/mp4/AtomToken.d.ts +84 -2
  22. package/lib/mp4/AtomToken.js +141 -9
  23. package/lib/mp4/MP4Parser.d.ts +4 -0
  24. package/lib/mp4/MP4Parser.js +156 -57
  25. package/lib/mp4/MP4TagMapper.d.ts +1 -1
  26. package/lib/mp4/MP4TagMapper.js +1 -1
  27. package/lib/mpeg/MpegParser.js +1 -0
  28. package/lib/musepack/MusepackParser.js +1 -0
  29. package/lib/musepack/sv7/MpcSv7Parser.js +2 -2
  30. package/lib/musepack/sv8/MpcSv8Parser.js +2 -2
  31. package/lib/ogg/opus/OpusParser.d.ts +1 -1
  32. package/lib/ogg/opus/OpusParser.js +2 -1
  33. package/lib/ogg/speex/SpeexParser.d.ts +1 -1
  34. package/lib/ogg/speex/SpeexParser.js +2 -1
  35. package/lib/ogg/theora/TheoraParser.d.ts +3 -5
  36. package/lib/ogg/theora/TheoraParser.js +4 -5
  37. package/lib/ogg/vorbis/VorbisParser.d.ts +1 -1
  38. package/lib/ogg/vorbis/VorbisParser.js +2 -1
  39. package/lib/type.d.ts +9 -1
  40. package/lib/wav/WaveParser.js +1 -0
  41. package/lib/wavpack/WavPackParser.js +3 -2
  42. package/package.json +146 -146
@@ -1,6 +1,6 @@
1
1
  import { type MediaType } from 'media-typer';
2
2
  import { type INativeMetadataCollector } from './common/MetadataCollector.js';
3
- import type { IAudioMetadata, IOptions, ParserType } from './type.js';
3
+ import { type IAudioMetadata, type IOptions, type ParserType } from './type.js';
4
4
  import type { ITokenizer } from 'strtok3';
5
5
  export interface IParserLoader {
6
6
  /**
@@ -3,6 +3,7 @@ import ContentType from 'content-type';
3
3
  import { parse as mimeTypeParse } from 'media-typer';
4
4
  import initDebug from 'debug';
5
5
  import { MetadataCollector } from './common/MetadataCollector.js';
6
+ import { TrackType } from './type.js';
6
7
  import { mpegParserLoader } from './mpeg/MpegLoader.js';
7
8
  import { CouldNotDetermineFileTypeError, UnsupportedFileTypeError } from './ParseError.js';
8
9
  import { apeParserLoader } from './apev2/Apev2Loader.js';
@@ -89,6 +90,14 @@ export class ParserFactory {
89
90
  const parser = new ParserImpl(metadata, tokenizer, opts ?? {});
90
91
  debug(`Parser ${parserLoader.parserType} loaded`);
91
92
  await parser.parse();
93
+ if (metadata.format.trackInfo) {
94
+ if (metadata.format.hasAudio === undefined) {
95
+ metadata.setFormat('hasAudio', !!metadata.format.trackInfo.find(track => track.type === TrackType.audio));
96
+ }
97
+ if (metadata.format.hasVideo === undefined) {
98
+ metadata.setFormat('hasVideo', !!metadata.format.trackInfo.find(track => track.type === TrackType.video));
99
+ }
100
+ }
92
101
  return metadata.toCommonMetadata();
93
102
  }
94
103
  /**
@@ -108,7 +117,7 @@ export class ParserFactory {
108
117
  try {
109
118
  mime = parseHttpContentType(httpContentType);
110
119
  }
111
- catch (err) {
120
+ catch (_err) {
112
121
  debug(`Invalid HTTP Content-Type header value: ${httpContentType}`);
113
122
  return;
114
123
  }
@@ -38,6 +38,7 @@ export class AIFFParser extends BasicParser {
38
38
  throw new AiffContentError(`Unsupported AIFF type: ${type}`);
39
39
  }
40
40
  this.metadata.setFormat('lossless', !this.isCompressed);
41
+ this.metadata.setAudioOnly();
41
42
  try {
42
43
  while (!this.tokenizer.fileInfo.size || this.tokenizer.fileInfo.size - this.tokenizer.position >= iff.Header.len) {
43
44
  debug(`Reading AIFF chunk at offset=${this.tokenizer.position}`);
@@ -18,8 +18,8 @@ declare const ApeContentError_base: {
18
18
  };
19
19
  export declare class ApeContentError extends ApeContentError_base {
20
20
  }
21
+ export declare function tryParseApeHeader(metadata: INativeMetadataCollector, tokenizer: strtok3.ITokenizer, options: IOptions): Promise<void>;
21
22
  export declare class APEv2Parser extends BasicParser {
22
- static tryParseApeHeader(metadata: INativeMetadataCollector, tokenizer: strtok3.ITokenizer, options: IOptions): Promise<void>;
23
23
  /**
24
24
  * Calculate the media file duration
25
25
  * @param ah ApeHeader
@@ -11,15 +11,15 @@ const tagFormat = 'APEv2';
11
11
  const preamble = 'APETAGEX';
12
12
  export class ApeContentError extends makeUnexpectedFileContentError('APEv2') {
13
13
  }
14
+ export function tryParseApeHeader(metadata, tokenizer, options) {
15
+ const apeParser = new APEv2Parser(metadata, tokenizer, options);
16
+ return apeParser.tryParseApeHeader();
17
+ }
14
18
  export class APEv2Parser extends BasicParser {
15
19
  constructor() {
16
20
  super(...arguments);
17
21
  this.ape = {};
18
22
  }
19
- static tryParseApeHeader(metadata, tokenizer, options) {
20
- const apeParser = new APEv2Parser(metadata, tokenizer, options);
21
- return apeParser.tryParseApeHeader();
22
- }
23
23
  /**
24
24
  * Calculate the media file duration
25
25
  * @param ah ApeHeader
@@ -96,6 +96,7 @@ export class APEv2Parser extends BasicParser {
96
96
  this.ape.descriptor = descriptor;
97
97
  const lenExp = descriptor.descriptorBytes - DescriptorParser.len;
98
98
  const header = await (lenExp > 0 ? this.parseDescriptorExpansion(lenExp) : this.parseHeader());
99
+ this.metadata.setAudioOnly();
99
100
  await this.tokenizer.ignore(header.forwardBytes);
100
101
  return this.tryParseApeHeader();
101
102
  }
@@ -82,7 +82,7 @@ export declare abstract class State<T> implements IGetToken<T> {
82
82
  protected postProcessTag(tags: ITag[], name: string, valueType: number, data: AnyTagValue): void;
83
83
  }
84
84
  export declare class IgnoreObjectState extends State<unknown> {
85
- get(buf: Uint8Array, off: number): null;
85
+ get(_buf: Uint8Array, _off: number): null;
86
86
  }
87
87
  /**
88
88
  * Interface for: 3.2: File Properties Object (mandatory, one only)
@@ -83,7 +83,7 @@ export class State {
83
83
  }
84
84
  // ToDo: use ignore type
85
85
  export class IgnoreObjectState extends State {
86
- get(buf, off) {
86
+ get(_buf, _off) {
87
87
  return null;
88
88
  }
89
89
  }
@@ -47,5 +47,5 @@ export declare class CommonTagMapper implements IGenericTagMapper {
47
47
  * @param tag Tag e.g. {"©alb", "Buena Vista Social Club")
48
48
  * @param warnings Used to register warnings
49
49
  */
50
- protected postMap(tag: ITag, warnings: IWarningCollector): void;
50
+ protected postMap(_tag: ITag, _warnings: IWarningCollector): void;
51
51
  }
@@ -44,7 +44,7 @@ export class CommonTagMapper {
44
44
  * @param tag Tag e.g. {"©alb", "Buena Vista Social Club")
45
45
  * @param warnings Used to register warnings
46
46
  */
47
- postMap(tag, warnings) {
47
+ postMap(_tag, _warnings) {
48
48
  return;
49
49
  }
50
50
  }
@@ -24,6 +24,7 @@ export interface INativeMetadataCollector extends IWarningCollector {
24
24
  setFormat(key: FormatId, value: AnyTagValue): void;
25
25
  addTag(tagType: TagType, tagId: string, value: AnyTagValue): Promise<void>;
26
26
  addStreamInfo(streamInfo: ITrackInfo): void;
27
+ setAudioOnly(): void;
27
28
  }
28
29
  /**
29
30
  * Provided to the parser to uodate the metadata result.
@@ -51,6 +52,7 @@ export declare class MetadataCollector implements INativeMetadataCollector {
51
52
  hasAny(): boolean;
52
53
  addStreamInfo(streamInfo: ITrackInfo): void;
53
54
  setFormat(key: FormatId, value: AnyTagValue): void;
55
+ setAudioOnly(): void;
54
56
  addTag(tagType: TagType, tagId: string, value: AnyTagValue): Promise<void>;
55
57
  addWarning(warning: string): void;
56
58
  postMap(tagType: TagType | 'artificial', tag: IGenericTag): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { TrackTypeValueToKeyMap } from '../type.js';
1
+ import { TrackTypeValueToKeyMap, } from '../type.js';
2
2
  import initDebug from 'debug';
3
3
  import { isSingleton, isUnique } from './GenericTagTypes.js';
4
4
  import { CombinedTagMapper } from './CombinedTagMapper.js';
@@ -61,6 +61,10 @@ export class MetadataCollector {
61
61
  this.opts.observer({ metadata: this, tag: { type: 'format', id: key, value } });
62
62
  }
63
63
  }
64
+ setAudioOnly() {
65
+ this.setFormat('hasAudio', true);
66
+ this.setFormat('hasVideo', false);
67
+ }
64
68
  async addTag(tagType, tagId, value) {
65
69
  debug(`tag ${tagType}.${tagId} = ${value}`);
66
70
  if (!this.native[tagType]) {
package/lib/core.d.ts CHANGED
@@ -63,12 +63,12 @@ export declare function scanAppendingHeaders(tokenizer: IRandomAccessTokenizer,
63
63
  * Implementation only available when loaded as Node.js
64
64
  * This method will throw an Error, always.
65
65
  */
66
- export declare function parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>;
66
+ export declare function parseFile(_filePath: string, _options?: IOptions): Promise<IAudioMetadata>;
67
67
  /**
68
68
  * Implementation only available when loaded as Node.js
69
69
  * This method will throw an Error, always.
70
70
  */
71
- export declare function parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
71
+ export declare function parseStream(_stream: Readable, _fileInfo?: IFileInfo | string, _options?: IOptions): Promise<IAudioMetadata>;
72
72
  /**
73
73
  * Return a list of supported mime-types
74
74
  */
package/lib/core.js CHANGED
@@ -69,10 +69,7 @@ export function parseFromTokenizer(tokenizer, options) {
69
69
  export function orderTags(nativeTags) {
70
70
  const tags = {};
71
71
  for (const { id, value } of nativeTags) {
72
- if (!tags[id]) {
73
- tags[id] = [];
74
- }
75
- tags[id].push(value);
72
+ (tags[id] || (tags[id] = [])).push(value);
76
73
  }
77
74
  return tags;
78
75
  }
@@ -109,14 +106,14 @@ export async function scanAppendingHeaders(tokenizer, options = {}) {
109
106
  * Implementation only available when loaded as Node.js
110
107
  * This method will throw an Error, always.
111
108
  */
112
- export async function parseFile(filePath, options = {}) {
109
+ export async function parseFile(_filePath, _options = {}) {
113
110
  throw new Error('This function require a Node engine. To load Web API File objects use parseBlob instead.');
114
111
  }
115
112
  /**
116
113
  * Implementation only available when loaded as Node.js
117
114
  * This method will throw an Error, always.
118
115
  */
119
- export async function parseStream(stream, fileInfo, options = {}) {
116
+ export async function parseStream(_stream, _fileInfo, _options = {}) {
120
117
  throw new Error('This function require a Node engine.');
121
118
  }
122
119
  /**
@@ -20,6 +20,7 @@ export class DsdiffParser extends BasicParser {
20
20
  const header = await this.tokenizer.readToken(ChunkHeader64);
21
21
  if (header.chunkID !== 'FRM8')
22
22
  throw new DsdiffContentParseError('Unexpected chunk-ID');
23
+ this.metadata.setAudioOnly();
23
24
  const type = (await this.tokenizer.readToken(FourCcToken)).trim();
24
25
  switch (type) {
25
26
  case 'DSD':
@@ -18,6 +18,7 @@ export class DsfParser extends AbstractID3Parser {
18
18
  throw new DsdContentParseError('Invalid chunk signature');
19
19
  this.metadata.setFormat('container', 'DSF');
20
20
  this.metadata.setFormat('lossless', true);
21
+ this.metadata.setAudioOnly();
21
22
  const dsdChunk = await this.tokenizer.readToken(DsdChunk);
22
23
  if (dsdChunk.metadataPointer === BigInt(0)) {
23
24
  debug("No ID3v2 tag present");
@@ -194,7 +194,7 @@ function readUIntBeAsBigInt(buf, len) {
194
194
  normalizedNumber.set(cleanNumber, 8 - len);
195
195
  return Token.UINT64_BE.get(normalizedNumber, 0);
196
196
  }
197
- catch (error) {
197
+ catch (_error) {
198
198
  return BigInt(-1);
199
199
  }
200
200
  }
@@ -34,6 +34,7 @@ export class FlacParser extends AbstractID3Parser {
34
34
  if (fourCC.toString() !== 'fLaC') {
35
35
  throw new FlacContentError('Invalid FLAC preamble');
36
36
  }
37
+ this.metadata.setAudioOnly();
37
38
  let blockHeader;
38
39
  do {
39
40
  // Read block header
package/lib/mp4/Atom.js CHANGED
@@ -43,13 +43,13 @@ export class Atom {
43
43
  // "Container" atoms, contains nested atoms
44
44
  case 'moov': // The Movie Atom: contains other atoms
45
45
  case 'udta': // User defined atom
46
- case 'trak':
47
46
  case 'mdia': // Media atom
48
47
  case 'minf': // Media Information Atom
49
48
  case 'stbl': // The Sample Table Atom
50
49
  case '<id>':
51
50
  case 'ilst':
52
51
  case 'tref':
52
+ case 'moof':
53
53
  return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining));
54
54
  case 'meta': { // Metadata Atom, ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
55
55
  // meta has 4 bytes of padding, ignore
@@ -121,7 +121,6 @@ export declare const Header: IToken<IAtomHeader>;
121
121
  */
122
122
  export declare const ExtendedSize: IToken<bigint>;
123
123
  export declare const ftyp: IGetToken<IAtomFtyp>;
124
- export declare const tkhd: IGetToken<IAtomFtyp>;
125
124
  /**
126
125
  * Token: Movie Header Atom
127
126
  */
@@ -269,7 +268,7 @@ export interface ITrackHeaderAtom extends IVersionAndFlags {
269
268
  volume: number;
270
269
  }
271
270
  /**
272
- * Track Header Atoms structure
271
+ * Track Header Atoms structure (`tkhd`)
273
272
  * Ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25550
274
273
  */
275
274
  export declare class TrackHeaderAtom implements IGetToken<ITrackHeaderAtom> {
@@ -402,4 +401,87 @@ export declare class ChapterText implements IGetToken<string> {
402
401
  constructor(len: number);
403
402
  get(buf: Uint8Array, off: number): string;
404
403
  }
404
+ interface ITrackFragmentHeaderBoxFlags {
405
+ baseDataOffsetPresent: boolean;
406
+ sampleDescriptionIndexPresent: boolean;
407
+ defaultSampleDurationPresent: boolean;
408
+ defaultSampleSizePresent: boolean;
409
+ defaultSampleFlagsPresent: boolean;
410
+ defaultDurationIsEmpty: boolean;
411
+ defaultBaseIsMoof: boolean;
412
+ }
413
+ export interface ITrackFragmentHeaderBox {
414
+ version: number;
415
+ flags: ITrackFragmentHeaderBoxFlags;
416
+ trackId: number;
417
+ baseDataOffset?: bigint;
418
+ sampleDescriptionIndex?: number;
419
+ defaultSampleDuration?: number;
420
+ defaultSampleSize?: number;
421
+ defaultSampleFlags?: number;
422
+ }
423
+ /**
424
+ * Sample-size ('tfhd') TrackFragmentHeaderBox
425
+ */
426
+ export declare class TrackFragmentHeaderBox implements IGetToken<ITrackFragmentHeaderBox> {
427
+ len: number;
428
+ constructor(len: number);
429
+ get(buf: Uint8Array, off: number): ITrackFragmentHeaderBox;
430
+ }
431
+ interface ITrackRunBoxFlags {
432
+ dataOffsetPresent: boolean;
433
+ firstSampleFlagsPresent: boolean;
434
+ sampleDurationPresent: boolean;
435
+ sampleSizePresent: boolean;
436
+ sampleFlagsPresent: boolean;
437
+ sampleCompositionTimeOffsetsPresent: boolean;
438
+ }
439
+ /**
440
+ * trun atom
441
+ */
442
+ export interface ITrackRunBox {
443
+ version: number;
444
+ flags: ITrackRunBoxFlags;
445
+ samples: ITrackRunBoxSample[];
446
+ sampleCount: number;
447
+ dataOffset?: number;
448
+ firstSampleFlags?: number;
449
+ }
450
+ interface ITrackRunBoxSample {
451
+ sampleDuration?: number;
452
+ sampleSize?: number;
453
+ sampleFlags?: number;
454
+ sampleCompositionTimeOffset?: number;
455
+ }
456
+ /**
457
+ * Sample-size ('trun') TrackRunBox
458
+ */
459
+ export declare class TrackRunBox implements IGetToken<ITrackRunBox> {
460
+ len: number;
461
+ constructor(len: number);
462
+ get(buf: Uint8Array, off: number): ITrackRunBox;
463
+ }
464
+ export interface IHandlerBox {
465
+ version: number;
466
+ flags: number;
467
+ componentType: string;
468
+ handlerType: string;
469
+ componentName: string;
470
+ }
471
+ /**
472
+ * HandlerBox (`hdlr`)
473
+ */
474
+ export declare class HandlerBox implements IGetToken<IHandlerBox> {
475
+ len: number;
476
+ constructor(len: number);
477
+ get(buf: Uint8Array, off: number): IHandlerBox;
478
+ }
479
+ /**
480
+ * Chapter Track Reference Box (`chap`)
481
+ */
482
+ export declare class ChapterTrackReferenceBox implements IGetToken<number[]> {
483
+ len: number;
484
+ constructor(len: number);
485
+ get(buf: Uint8Array, off: number): number[];
486
+ }
405
487
  export {};
@@ -2,6 +2,7 @@ import * as Token from 'token-types';
2
2
  import initDebug from 'debug';
3
3
  import { FourCcToken } from '../common/FourCC.js';
4
4
  import { makeUnexpectedFileContentError } from '../ParseError.js';
5
+ import * as util from '../common/Util.js';
5
6
  const debug = initDebug('music-metadata:parser:MP4:atom');
6
7
  export class Mp4ContentError extends makeUnexpectedFileContentError('MP4') {
7
8
  }
@@ -33,14 +34,6 @@ export const ftyp = {
33
34
  };
34
35
  }
35
36
  };
36
- export const tkhd = {
37
- len: 4,
38
- get: (buf, off) => {
39
- return {
40
- type: new Token.StringType(4, 'ascii').get(buf, off)
41
- };
42
- }
43
- };
44
37
  /**
45
38
  * Token: Movie Header Atom
46
39
  */
@@ -173,7 +166,7 @@ export class NameAtom {
173
166
  }
174
167
  }
175
168
  /**
176
- * Track Header Atoms structure
169
+ * Track Header Atoms structure (`tkhd`)
177
170
  * Ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25550
178
171
  */
179
172
  export class TrackHeaderAtom {
@@ -390,4 +383,143 @@ function readTokenTable(buf, token, off, remainingLen, numberOfEntries) {
390
383
  }
391
384
  return entries;
392
385
  }
386
+ /**
387
+ * Sample-size ('tfhd') TrackFragmentHeaderBox
388
+ */
389
+ export class TrackFragmentHeaderBox {
390
+ constructor(len) {
391
+ this.len = len;
392
+ }
393
+ get(buf, off) {
394
+ const flagOffset = off + 1;
395
+ const header = {
396
+ version: Token.INT8.get(buf, off),
397
+ flags: {
398
+ baseDataOffsetPresent: util.getBit(buf, flagOffset + 2, 0),
399
+ sampleDescriptionIndexPresent: util.getBit(buf, flagOffset + 2, 1),
400
+ defaultSampleDurationPresent: util.getBit(buf, flagOffset + 2, 3),
401
+ defaultSampleSizePresent: util.getBit(buf, flagOffset + 2, 4),
402
+ defaultSampleFlagsPresent: util.getBit(buf, flagOffset + 2, 5),
403
+ defaultDurationIsEmpty: util.getBit(buf, flagOffset, 0),
404
+ defaultBaseIsMoof: util.getBit(buf, flagOffset, 1)
405
+ },
406
+ trackId: Token.UINT32_BE.get(buf, 4)
407
+ };
408
+ let dynOffset = 8;
409
+ if (header.flags.baseDataOffsetPresent) {
410
+ header.baseDataOffset = Token.UINT64_BE.get(buf, dynOffset);
411
+ dynOffset += 8;
412
+ }
413
+ if (header.flags.sampleDescriptionIndexPresent) {
414
+ header.sampleDescriptionIndex = Token.UINT32_BE.get(buf, dynOffset);
415
+ dynOffset += 4;
416
+ }
417
+ if (header.flags.defaultSampleDurationPresent) {
418
+ header.defaultSampleDuration = Token.UINT32_BE.get(buf, dynOffset);
419
+ dynOffset += 4;
420
+ }
421
+ if (header.flags.defaultSampleSizePresent) {
422
+ header.defaultSampleSize = Token.UINT32_BE.get(buf, dynOffset);
423
+ dynOffset += 4;
424
+ }
425
+ if (header.flags.defaultSampleFlagsPresent) {
426
+ header.defaultSampleFlags = Token.UINT32_BE.get(buf, dynOffset);
427
+ }
428
+ return header;
429
+ }
430
+ }
431
+ /**
432
+ * Sample-size ('trun') TrackRunBox
433
+ */
434
+ export class TrackRunBox {
435
+ constructor(len) {
436
+ this.len = len;
437
+ }
438
+ get(buf, off) {
439
+ const flagOffset = off + 1;
440
+ const trun = {
441
+ version: Token.INT8.get(buf, off),
442
+ flags: {
443
+ dataOffsetPresent: util.getBit(buf, flagOffset + 2, 0),
444
+ firstSampleFlagsPresent: util.getBit(buf, flagOffset + 2, 2),
445
+ sampleDurationPresent: util.getBit(buf, flagOffset + 1, 0),
446
+ sampleSizePresent: util.getBit(buf, flagOffset + 1, 1),
447
+ sampleFlagsPresent: util.getBit(buf, flagOffset + 1, 2),
448
+ sampleCompositionTimeOffsetsPresent: util.getBit(buf, flagOffset + 1, 3)
449
+ },
450
+ sampleCount: Token.UINT32_BE.get(buf, off + 4),
451
+ samples: []
452
+ };
453
+ let dynOffset = off + 8;
454
+ if (trun.flags.dataOffsetPresent) {
455
+ trun.dataOffset = Token.UINT32_BE.get(buf, dynOffset);
456
+ dynOffset += 4;
457
+ }
458
+ if (trun.flags.firstSampleFlagsPresent) {
459
+ trun.firstSampleFlags = Token.UINT32_BE.get(buf, dynOffset);
460
+ dynOffset += 4;
461
+ }
462
+ for (let n = 0; n < trun.sampleCount; ++n) {
463
+ if (dynOffset >= this.len) {
464
+ debug("TrackRunBox size mismatch");
465
+ break;
466
+ }
467
+ const sample = {};
468
+ if (trun.flags.sampleDurationPresent) {
469
+ sample.sampleDuration = Token.UINT32_BE.get(buf, dynOffset);
470
+ dynOffset += 4;
471
+ }
472
+ if (trun.flags.sampleSizePresent) {
473
+ sample.sampleSize = Token.UINT32_BE.get(buf, dynOffset);
474
+ dynOffset += 4;
475
+ }
476
+ if (trun.flags.sampleFlagsPresent) {
477
+ sample.sampleFlags = Token.UINT32_BE.get(buf, dynOffset);
478
+ dynOffset += 4;
479
+ }
480
+ if (trun.flags.sampleCompositionTimeOffsetsPresent) {
481
+ sample.sampleCompositionTimeOffset = Token.UINT32_BE.get(buf, dynOffset);
482
+ dynOffset += 4;
483
+ }
484
+ trun.samples.push(sample);
485
+ }
486
+ return trun;
487
+ }
488
+ }
489
+ /**
490
+ * HandlerBox (`hdlr`)
491
+ */
492
+ export class HandlerBox {
493
+ constructor(len) {
494
+ this.len = len;
495
+ }
496
+ get(buf, off) {
497
+ const _flagOffset = off + 1;
498
+ const charTypeToken = new Token.StringType(4, 'utf-8');
499
+ return {
500
+ version: Token.INT8.get(buf, off),
501
+ flags: Token.UINT24_BE.get(buf, off + 1),
502
+ componentType: charTypeToken.get(buf, off + 4),
503
+ handlerType: charTypeToken.get(buf, off + 8),
504
+ componentName: new Token.StringType(this.len - 28, 'utf-8').get(buf, off + 28),
505
+ };
506
+ }
507
+ }
508
+ /**
509
+ * Chapter Track Reference Box (`chap`)
510
+ */
511
+ export class ChapterTrackReferenceBox {
512
+ constructor(len) {
513
+ this.len = len;
514
+ }
515
+ get(buf, off) {
516
+ let dynOffset = 0;
517
+ const trackIds = [];
518
+ while (dynOffset < this.len) {
519
+ trackIds.push(Token.UINT32_BE.get(buf, off + dynOffset));
520
+ dynOffset += 4;
521
+ }
522
+ return trackIds;
523
+ }
524
+ }
393
525
  //# sourceMappingURL=AtomToken.js.map
@@ -4,6 +4,8 @@ export declare class MP4Parser extends BasicParser {
4
4
  private static read_BE_Integer;
5
5
  private audioLengthInBytes;
6
6
  private tracks;
7
+ private hasVideoTrack;
8
+ private hasAudioTrack;
7
9
  parse(): Promise<void>;
8
10
  handleAtom(atom: Atom, remaining: number): Promise<void>;
9
11
  private getTrackDescription;
@@ -17,6 +19,8 @@ export declare class MP4Parser extends BasicParser {
17
19
  */
18
20
  private parseMetadataItemData;
19
21
  private parseValueAtom;
22
+ private parseTrackBox;
23
+ private parseTrackFragmentBox;
20
24
  private atomParsers;
21
25
  /**
22
26
  * @param sampleDescription