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.
- package/LICENSE.txt +9 -9
- package/README.md +859 -859
- package/lib/ParserFactory.d.ts +1 -1
- package/lib/ParserFactory.js +10 -1
- package/lib/aiff/AiffParser.js +1 -0
- package/lib/apev2/APEv2Parser.d.ts +1 -1
- package/lib/apev2/APEv2Parser.js +5 -4
- package/lib/asf/AsfObject.d.ts +1 -1
- package/lib/asf/AsfObject.js +1 -1
- package/lib/common/GenericTagMapper.d.ts +1 -1
- package/lib/common/GenericTagMapper.js +1 -1
- package/lib/common/MetadataCollector.d.ts +2 -0
- package/lib/common/MetadataCollector.js +5 -1
- package/lib/core.d.ts +2 -2
- package/lib/core.js +3 -6
- package/lib/dsdiff/DsdiffParser.js +1 -0
- package/lib/dsf/DsfParser.js +1 -0
- package/lib/ebml/EbmlIterator.js +1 -1
- package/lib/flac/FlacParser.js +1 -0
- package/lib/mp4/Atom.js +1 -1
- package/lib/mp4/AtomToken.d.ts +84 -2
- package/lib/mp4/AtomToken.js +141 -9
- package/lib/mp4/MP4Parser.d.ts +4 -0
- package/lib/mp4/MP4Parser.js +156 -57
- package/lib/mp4/MP4TagMapper.d.ts +1 -1
- package/lib/mp4/MP4TagMapper.js +1 -1
- package/lib/mpeg/MpegParser.js +1 -0
- package/lib/musepack/MusepackParser.js +1 -0
- package/lib/musepack/sv7/MpcSv7Parser.js +2 -2
- package/lib/musepack/sv8/MpcSv8Parser.js +2 -2
- package/lib/ogg/opus/OpusParser.d.ts +1 -1
- package/lib/ogg/opus/OpusParser.js +2 -1
- package/lib/ogg/speex/SpeexParser.d.ts +1 -1
- package/lib/ogg/speex/SpeexParser.js +2 -1
- package/lib/ogg/theora/TheoraParser.d.ts +3 -5
- package/lib/ogg/theora/TheoraParser.js +4 -5
- package/lib/ogg/vorbis/VorbisParser.d.ts +1 -1
- package/lib/ogg/vorbis/VorbisParser.js +2 -1
- package/lib/type.d.ts +9 -1
- package/lib/wav/WaveParser.js +1 -0
- package/lib/wavpack/WavPackParser.js +3 -2
- package/package.json +146 -146
package/lib/ParserFactory.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type MediaType } from 'media-typer';
|
|
2
2
|
import { type INativeMetadataCollector } from './common/MetadataCollector.js';
|
|
3
|
-
import type
|
|
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
|
/**
|
package/lib/ParserFactory.js
CHANGED
|
@@ -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 (
|
|
120
|
+
catch (_err) {
|
|
112
121
|
debug(`Invalid HTTP Content-Type header value: ${httpContentType}`);
|
|
113
122
|
return;
|
|
114
123
|
}
|
package/lib/aiff/AiffParser.js
CHANGED
|
@@ -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
|
package/lib/apev2/APEv2Parser.js
CHANGED
|
@@ -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
|
}
|
package/lib/asf/AsfObject.d.ts
CHANGED
|
@@ -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(
|
|
85
|
+
get(_buf: Uint8Array, _off: number): null;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Interface for: 3.2: File Properties Object (mandatory, one only)
|
package/lib/asf/AsfObject.js
CHANGED
|
@@ -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(
|
|
50
|
+
protected postMap(_tag: ITag, _warnings: IWarningCollector): void;
|
|
51
51
|
}
|
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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':
|
package/lib/dsf/DsfParser.js
CHANGED
|
@@ -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");
|
package/lib/ebml/EbmlIterator.js
CHANGED
package/lib/flac/FlacParser.js
CHANGED
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
|
package/lib/mp4/AtomToken.d.ts
CHANGED
|
@@ -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 {};
|
package/lib/mp4/AtomToken.js
CHANGED
|
@@ -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
|
package/lib/mp4/MP4Parser.d.ts
CHANGED
|
@@ -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
|