@kenzuya/mediabunny 1.26.0 → 1.28.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bundles/{mediabunny.mjs → mediabunny.js} +21963 -21388
- package/dist/bundles/mediabunny.min.js +490 -0
- package/dist/modules/shared/mp3-misc.d.ts.map +1 -1
- package/dist/modules/src/adts/adts-demuxer.d.ts +6 -6
- package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
- package/dist/modules/src/adts/adts-muxer.d.ts +4 -4
- package/dist/modules/src/adts/adts-muxer.d.ts.map +1 -1
- package/dist/modules/src/adts/adts-reader.d.ts +1 -1
- package/dist/modules/src/adts/adts-reader.d.ts.map +1 -1
- package/dist/modules/src/avi/avi-demuxer.d.ts +44 -0
- package/dist/modules/src/avi/avi-demuxer.d.ts.map +1 -0
- package/dist/modules/src/avi/avi-misc.d.ts +88 -0
- package/dist/modules/src/avi/avi-misc.d.ts.map +1 -0
- package/dist/modules/src/avi/avi-muxer.d.ts +45 -0
- package/dist/modules/src/avi/avi-muxer.d.ts.map +1 -0
- package/dist/modules/src/avi/riff-writer.d.ts +26 -0
- package/dist/modules/src/avi/riff-writer.d.ts.map +1 -0
- package/dist/modules/src/codec-data.d.ts +8 -3
- package/dist/modules/src/codec-data.d.ts.map +1 -1
- package/dist/modules/src/codec.d.ts +10 -10
- package/dist/modules/src/codec.d.ts.map +1 -1
- package/dist/modules/src/conversion.d.ts +33 -16
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/custom-coder.d.ts +8 -8
- package/dist/modules/src/custom-coder.d.ts.map +1 -1
- package/dist/modules/src/demuxer.d.ts +3 -3
- package/dist/modules/src/demuxer.d.ts.map +1 -1
- package/dist/modules/src/encode.d.ts +8 -8
- package/dist/modules/src/encode.d.ts.map +1 -1
- package/dist/modules/src/flac/flac-demuxer.d.ts +7 -7
- package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
- package/dist/modules/src/flac/flac-misc.d.ts +3 -3
- package/dist/modules/src/flac/flac-misc.d.ts.map +1 -1
- package/dist/modules/src/flac/flac-muxer.d.ts +5 -5
- package/dist/modules/src/flac/flac-muxer.d.ts.map +1 -1
- package/dist/modules/src/id3.d.ts +3 -3
- package/dist/modules/src/id3.d.ts.map +1 -1
- package/dist/modules/src/index.d.ts +20 -20
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/input-format.d.ts +22 -0
- package/dist/modules/src/input-format.d.ts.map +1 -1
- package/dist/modules/src/input-track.d.ts +8 -8
- package/dist/modules/src/input-track.d.ts.map +1 -1
- package/dist/modules/src/input.d.ts +12 -12
- package/dist/modules/src/isobmff/isobmff-boxes.d.ts +2 -2
- package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts +12 -12
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-misc.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts +11 -11
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-reader.d.ts +2 -2
- package/dist/modules/src/isobmff/isobmff-reader.d.ts.map +1 -1
- package/dist/modules/src/matroska/ebml.d.ts +3 -3
- package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.d.ts +13 -13
- package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-input.d.ts +33 -0
- package/dist/modules/src/matroska/matroska-input.d.ts.map +1 -0
- package/dist/modules/src/matroska/matroska-misc.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-muxer.d.ts +5 -5
- package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
- package/dist/modules/src/media-sink.d.ts +5 -5
- package/dist/modules/src/media-sink.d.ts.map +1 -1
- package/dist/modules/src/media-source.d.ts +22 -4
- package/dist/modules/src/media-source.d.ts.map +1 -1
- package/dist/modules/src/metadata.d.ts +2 -2
- package/dist/modules/src/metadata.d.ts.map +1 -1
- package/dist/modules/src/misc.d.ts +5 -4
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-demuxer.d.ts +7 -7
- package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-muxer.d.ts +4 -4
- package/dist/modules/src/mp3/mp3-muxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-reader.d.ts +2 -2
- package/dist/modules/src/mp3/mp3-reader.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-writer.d.ts +1 -1
- package/dist/modules/src/mp3/mp3-writer.d.ts.map +1 -1
- package/dist/modules/src/muxer.d.ts +4 -4
- package/dist/modules/src/muxer.d.ts.map +1 -1
- package/dist/modules/src/node.d.ts +1 -1
- package/dist/modules/src/ogg/ogg-demuxer.d.ts +7 -7
- package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
- package/dist/modules/src/ogg/ogg-misc.d.ts +1 -1
- package/dist/modules/src/ogg/ogg-misc.d.ts.map +1 -1
- package/dist/modules/src/ogg/ogg-muxer.d.ts +5 -5
- package/dist/modules/src/ogg/ogg-muxer.d.ts.map +1 -1
- package/dist/modules/src/ogg/ogg-reader.d.ts +1 -1
- package/dist/modules/src/ogg/ogg-reader.d.ts.map +1 -1
- package/dist/modules/src/output-format.d.ts +51 -6
- package/dist/modules/src/output-format.d.ts.map +1 -1
- package/dist/modules/src/output.d.ts +13 -13
- package/dist/modules/src/output.d.ts.map +1 -1
- package/dist/modules/src/packet.d.ts +1 -1
- package/dist/modules/src/packet.d.ts.map +1 -1
- package/dist/modules/src/pcm.d.ts.map +1 -1
- package/dist/modules/src/reader.d.ts +2 -2
- package/dist/modules/src/reader.d.ts.map +1 -1
- package/dist/modules/src/sample.d.ts +57 -15
- package/dist/modules/src/sample.d.ts.map +1 -1
- package/dist/modules/src/source.d.ts +3 -3
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/subtitles.d.ts +1 -1
- package/dist/modules/src/subtitles.d.ts.map +1 -1
- package/dist/modules/src/target.d.ts +2 -2
- package/dist/modules/src/target.d.ts.map +1 -1
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/dist/modules/src/wave/riff-writer.d.ts +1 -1
- package/dist/modules/src/wave/riff-writer.d.ts.map +1 -1
- package/dist/modules/src/wave/wave-demuxer.d.ts +6 -6
- package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
- package/dist/modules/src/wave/wave-muxer.d.ts +4 -4
- package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
- package/dist/modules/src/writer.d.ts +1 -1
- package/dist/modules/src/writer.d.ts.map +1 -1
- package/dist/packages/eac3/eac3.wasm +0 -0
- package/dist/packages/eac3/mediabunny-eac3.js +1058 -0
- package/dist/packages/eac3/mediabunny-eac3.min.js +44 -0
- package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.js +694 -0
- package/dist/packages/mp3-encoder/mediabunny-mp3-encoder.min.js +58 -0
- package/dist/packages/mpeg4/mediabunny-mpeg4.js +1198 -0
- package/dist/packages/mpeg4/mediabunny-mpeg4.min.js +44 -0
- package/dist/packages/mpeg4/xvid.wasm +0 -0
- package/package.json +18 -57
- package/dist/bundles/mediabunny.cjs +0 -26140
- package/dist/bundles/mediabunny.min.cjs +0 -147
- package/dist/bundles/mediabunny.min.mjs +0 -146
- package/dist/mediabunny.d.ts +0 -3319
- package/dist/modules/shared/mp3-misc.js +0 -147
- package/dist/modules/src/adts/adts-demuxer.js +0 -239
- package/dist/modules/src/adts/adts-muxer.js +0 -80
- package/dist/modules/src/adts/adts-reader.js +0 -63
- package/dist/modules/src/codec-data.js +0 -1730
- package/dist/modules/src/codec.js +0 -869
- package/dist/modules/src/conversion.js +0 -1459
- package/dist/modules/src/custom-coder.js +0 -117
- package/dist/modules/src/demuxer.js +0 -12
- package/dist/modules/src/encode.js +0 -442
- package/dist/modules/src/flac/flac-demuxer.js +0 -504
- package/dist/modules/src/flac/flac-misc.js +0 -135
- package/dist/modules/src/flac/flac-muxer.js +0 -222
- package/dist/modules/src/id3.js +0 -848
- package/dist/modules/src/index.js +0 -28
- package/dist/modules/src/input-format.js +0 -480
- package/dist/modules/src/input-track.js +0 -372
- package/dist/modules/src/input.js +0 -188
- package/dist/modules/src/isobmff/isobmff-boxes.js +0 -1480
- package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -2618
- package/dist/modules/src/isobmff/isobmff-misc.js +0 -20
- package/dist/modules/src/isobmff/isobmff-muxer.js +0 -966
- package/dist/modules/src/isobmff/isobmff-reader.js +0 -72
- package/dist/modules/src/matroska/ebml.js +0 -653
- package/dist/modules/src/matroska/matroska-demuxer.js +0 -2133
- package/dist/modules/src/matroska/matroska-misc.js +0 -20
- package/dist/modules/src/matroska/matroska-muxer.js +0 -1017
- package/dist/modules/src/media-sink.js +0 -1736
- package/dist/modules/src/media-source.js +0 -1825
- package/dist/modules/src/metadata.js +0 -193
- package/dist/modules/src/misc.js +0 -623
- package/dist/modules/src/mp3/mp3-demuxer.js +0 -285
- package/dist/modules/src/mp3/mp3-muxer.js +0 -123
- package/dist/modules/src/mp3/mp3-reader.js +0 -26
- package/dist/modules/src/mp3/mp3-writer.js +0 -78
- package/dist/modules/src/muxer.js +0 -50
- package/dist/modules/src/node.js +0 -9
- package/dist/modules/src/ogg/ogg-demuxer.js +0 -763
- package/dist/modules/src/ogg/ogg-misc.js +0 -78
- package/dist/modules/src/ogg/ogg-muxer.js +0 -353
- package/dist/modules/src/ogg/ogg-reader.js +0 -65
- package/dist/modules/src/output-format.js +0 -527
- package/dist/modules/src/output.js +0 -300
- package/dist/modules/src/packet.js +0 -182
- package/dist/modules/src/pcm.js +0 -85
- package/dist/modules/src/reader.js +0 -236
- package/dist/modules/src/sample.js +0 -1056
- package/dist/modules/src/source.js +0 -1182
- package/dist/modules/src/subtitles.js +0 -575
- package/dist/modules/src/target.js +0 -140
- package/dist/modules/src/wave/riff-writer.js +0 -30
- package/dist/modules/src/wave/wave-demuxer.js +0 -447
- package/dist/modules/src/wave/wave-muxer.js +0 -318
- package/dist/modules/src/writer.js +0 -370
- package/src/adts/adts-demuxer.ts +0 -331
- package/src/adts/adts-muxer.ts +0 -111
- package/src/adts/adts-reader.ts +0 -85
- package/src/codec-data.ts +0 -2078
- package/src/codec.ts +0 -1092
- package/src/conversion.ts +0 -2112
- package/src/custom-coder.ts +0 -197
- package/src/demuxer.ts +0 -24
- package/src/encode.ts +0 -739
- package/src/flac/flac-demuxer.ts +0 -730
- package/src/flac/flac-misc.ts +0 -164
- package/src/flac/flac-muxer.ts +0 -320
- package/src/id3.ts +0 -925
- package/src/index.ts +0 -221
- package/src/input-format.ts +0 -541
- package/src/input-track.ts +0 -529
- package/src/input.ts +0 -235
- package/src/isobmff/isobmff-boxes.ts +0 -1719
- package/src/isobmff/isobmff-demuxer.ts +0 -3190
- package/src/isobmff/isobmff-misc.ts +0 -29
- package/src/isobmff/isobmff-muxer.ts +0 -1348
- package/src/isobmff/isobmff-reader.ts +0 -91
- package/src/matroska/ebml.ts +0 -730
- package/src/matroska/matroska-demuxer.ts +0 -2481
- package/src/matroska/matroska-misc.ts +0 -29
- package/src/matroska/matroska-muxer.ts +0 -1276
- package/src/media-sink.ts +0 -2179
- package/src/media-source.ts +0 -2243
- package/src/metadata.ts +0 -320
- package/src/misc.ts +0 -798
- package/src/mp3/mp3-demuxer.ts +0 -383
- package/src/mp3/mp3-muxer.ts +0 -166
- package/src/mp3/mp3-reader.ts +0 -34
- package/src/mp3/mp3-writer.ts +0 -120
- package/src/muxer.ts +0 -88
- package/src/node.ts +0 -11
- package/src/ogg/ogg-demuxer.ts +0 -1053
- package/src/ogg/ogg-misc.ts +0 -116
- package/src/ogg/ogg-muxer.ts +0 -497
- package/src/ogg/ogg-reader.ts +0 -93
- package/src/output-format.ts +0 -945
- package/src/output.ts +0 -488
- package/src/packet.ts +0 -263
- package/src/pcm.ts +0 -112
- package/src/reader.ts +0 -323
- package/src/sample.ts +0 -1461
- package/src/source.ts +0 -1688
- package/src/subtitles.ts +0 -711
- package/src/target.ts +0 -204
- package/src/tsconfig.json +0 -16
- package/src/wave/riff-writer.ts +0 -36
- package/src/wave/wave-demuxer.ts +0 -529
- package/src/wave/wave-muxer.ts +0 -371
- package/src/writer.ts +0 -490
package/src/wave/wave-demuxer.ts
DELETED
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
3
|
-
*
|
|
4
|
-
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
-
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { AudioCodec } from '../codec';
|
|
10
|
-
import { Demuxer } from '../demuxer';
|
|
11
|
-
import { Input } from '../input';
|
|
12
|
-
import { InputAudioTrack, InputAudioTrackBacking } from '../input-track';
|
|
13
|
-
import { PacketRetrievalOptions } from '../media-sink';
|
|
14
|
-
import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
|
|
15
|
-
import { assert, UNDETERMINED_LANGUAGE } from '../misc';
|
|
16
|
-
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
|
17
|
-
import { readAscii, readBytes, Reader, readU16, readU32, readU64 } from '../reader';
|
|
18
|
-
import { parseId3V2Tag, readId3V2Header } from '../id3';
|
|
19
|
-
|
|
20
|
-
export enum WaveFormat {
|
|
21
|
-
PCM = 0x0001,
|
|
22
|
-
IEEE_FLOAT = 0x0003,
|
|
23
|
-
ALAW = 0x0006,
|
|
24
|
-
MULAW = 0x0007,
|
|
25
|
-
EXTENSIBLE = 0xFFFE,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class WaveDemuxer extends Demuxer {
|
|
29
|
-
reader: Reader;
|
|
30
|
-
|
|
31
|
-
metadataPromise: Promise<void> | null = null;
|
|
32
|
-
dataStart = -1;
|
|
33
|
-
dataSize = -1;
|
|
34
|
-
audioInfo: {
|
|
35
|
-
format: number;
|
|
36
|
-
numberOfChannels: number;
|
|
37
|
-
sampleRate: number;
|
|
38
|
-
sampleSizeInBytes: number;
|
|
39
|
-
blockSizeInBytes: number;
|
|
40
|
-
} | null = null;
|
|
41
|
-
|
|
42
|
-
tracks: InputAudioTrack[] = [];
|
|
43
|
-
lastKnownPacketIndex = 0;
|
|
44
|
-
metadataTags: MetadataTags = {};
|
|
45
|
-
|
|
46
|
-
constructor(input: Input) {
|
|
47
|
-
super(input);
|
|
48
|
-
|
|
49
|
-
this.reader = input._reader;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async readMetadata() {
|
|
53
|
-
return this.metadataPromise ??= (async () => {
|
|
54
|
-
let slice = this.reader.requestSlice(0, 12);
|
|
55
|
-
if (slice instanceof Promise) slice = await slice;
|
|
56
|
-
assert(slice);
|
|
57
|
-
|
|
58
|
-
const riffType = readAscii(slice, 4);
|
|
59
|
-
const littleEndian = riffType !== 'RIFX';
|
|
60
|
-
|
|
61
|
-
const isRf64 = riffType === 'RF64';
|
|
62
|
-
|
|
63
|
-
const outerChunkSize = readU32(slice, littleEndian);
|
|
64
|
-
|
|
65
|
-
let totalFileSize = isRf64
|
|
66
|
-
? this.reader.fileSize
|
|
67
|
-
: Math.min(outerChunkSize + 8, this.reader.fileSize ?? Infinity);
|
|
68
|
-
const format = readAscii(slice, 4);
|
|
69
|
-
|
|
70
|
-
if (format !== 'WAVE') {
|
|
71
|
-
throw new Error('Invalid WAVE file - wrong format');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let chunksRead = 0;
|
|
75
|
-
let dataChunkSize: number | null = null;
|
|
76
|
-
let currentPos = slice.filePos;
|
|
77
|
-
|
|
78
|
-
while (totalFileSize === null || currentPos < totalFileSize) {
|
|
79
|
-
let slice = this.reader.requestSlice(currentPos, 8);
|
|
80
|
-
if (slice instanceof Promise) slice = await slice;
|
|
81
|
-
if (!slice) break;
|
|
82
|
-
|
|
83
|
-
const chunkId = readAscii(slice, 4);
|
|
84
|
-
const chunkSize = readU32(slice, littleEndian);
|
|
85
|
-
const startPos = slice.filePos;
|
|
86
|
-
|
|
87
|
-
if (isRf64 && chunksRead === 0 && chunkId !== 'ds64') {
|
|
88
|
-
throw new Error('Invalid RF64 file: First chunk must be "ds64".');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (chunkId === 'fmt ') {
|
|
92
|
-
await this.parseFmtChunk(startPos, chunkSize, littleEndian);
|
|
93
|
-
} else if (chunkId === 'data') {
|
|
94
|
-
dataChunkSize ??= chunkSize;
|
|
95
|
-
|
|
96
|
-
this.dataStart = slice.filePos;
|
|
97
|
-
this.dataSize = Math.min(dataChunkSize, (totalFileSize ?? Infinity) - this.dataStart);
|
|
98
|
-
|
|
99
|
-
if (this.reader.fileSize === null) {
|
|
100
|
-
break; // Stop once we hit the data chunk
|
|
101
|
-
}
|
|
102
|
-
} else if (chunkId === 'ds64') {
|
|
103
|
-
// File and data chunk sizes are defined in here instead
|
|
104
|
-
|
|
105
|
-
let ds64Slice = this.reader.requestSlice(startPos, chunkSize);
|
|
106
|
-
if (ds64Slice instanceof Promise) ds64Slice = await ds64Slice;
|
|
107
|
-
if (!ds64Slice) break;
|
|
108
|
-
|
|
109
|
-
const riffChunkSize = readU64(ds64Slice, littleEndian);
|
|
110
|
-
dataChunkSize = readU64(ds64Slice, littleEndian);
|
|
111
|
-
|
|
112
|
-
totalFileSize = Math.min(riffChunkSize + 8, this.reader.fileSize ?? Infinity);
|
|
113
|
-
} else if (chunkId === 'LIST') {
|
|
114
|
-
await this.parseListChunk(startPos, chunkSize, littleEndian);
|
|
115
|
-
} else if (chunkId === 'ID3 ' || chunkId === 'id3 ') {
|
|
116
|
-
await this.parseId3Chunk(startPos, chunkSize);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
currentPos = startPos + chunkSize + (chunkSize & 1); // Handle padding
|
|
120
|
-
chunksRead++;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (!this.audioInfo) {
|
|
124
|
-
throw new Error('Invalid WAVE file - missing "fmt " chunk');
|
|
125
|
-
}
|
|
126
|
-
if (this.dataStart === -1) {
|
|
127
|
-
throw new Error('Invalid WAVE file - missing "data" chunk');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const blockSize = this.audioInfo.blockSizeInBytes;
|
|
131
|
-
this.dataSize = Math.floor(this.dataSize / blockSize) * blockSize;
|
|
132
|
-
|
|
133
|
-
this.tracks.push(new InputAudioTrack(this.input, new WaveAudioTrackBacking(this)));
|
|
134
|
-
})();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private async parseFmtChunk(startPos: number, size: number, littleEndian: boolean) {
|
|
138
|
-
let slice = this.reader.requestSlice(startPos, size);
|
|
139
|
-
if (slice instanceof Promise) slice = await slice;
|
|
140
|
-
if (!slice) return; // File too short
|
|
141
|
-
|
|
142
|
-
let formatTag = readU16(slice, littleEndian);
|
|
143
|
-
const numChannels = readU16(slice, littleEndian);
|
|
144
|
-
const sampleRate = readU32(slice, littleEndian);
|
|
145
|
-
slice.skip(4); // Bytes per second
|
|
146
|
-
const blockAlign = readU16(slice, littleEndian);
|
|
147
|
-
|
|
148
|
-
let bitsPerSample: number;
|
|
149
|
-
|
|
150
|
-
if (size === 14) { // Plain WAVEFORMAT
|
|
151
|
-
bitsPerSample = 8;
|
|
152
|
-
} else {
|
|
153
|
-
bitsPerSample = readU16(slice, littleEndian);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Handle WAVEFORMATEXTENSIBLE
|
|
157
|
-
if (size >= 18 && formatTag !== 0x0165) {
|
|
158
|
-
const cbSize = readU16(slice, littleEndian);
|
|
159
|
-
const remainingSize = size - 18;
|
|
160
|
-
const extensionSize = Math.min(remainingSize, cbSize);
|
|
161
|
-
|
|
162
|
-
if (extensionSize >= 22 && formatTag === WaveFormat.EXTENSIBLE) {
|
|
163
|
-
// Parse WAVEFORMATEXTENSIBLE
|
|
164
|
-
slice.skip(2 + 4);
|
|
165
|
-
const subFormat = readBytes(slice, 16);
|
|
166
|
-
|
|
167
|
-
// Get actual format from subFormat GUID
|
|
168
|
-
formatTag = subFormat[0]! | (subFormat[1]! << 8);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (formatTag === WaveFormat.MULAW || formatTag === WaveFormat.ALAW) {
|
|
173
|
-
bitsPerSample = 8;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
this.audioInfo = {
|
|
177
|
-
format: formatTag,
|
|
178
|
-
numberOfChannels: numChannels,
|
|
179
|
-
sampleRate,
|
|
180
|
-
sampleSizeInBytes: Math.ceil(bitsPerSample / 8),
|
|
181
|
-
blockSizeInBytes: blockAlign,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private async parseListChunk(startPos: number, size: number, littleEndian: boolean) {
|
|
186
|
-
let slice = this.reader.requestSlice(startPos, size);
|
|
187
|
-
if (slice instanceof Promise) slice = await slice;
|
|
188
|
-
if (!slice) return; // File too short
|
|
189
|
-
|
|
190
|
-
const infoType = readAscii(slice, 4);
|
|
191
|
-
if (infoType !== 'INFO' && infoType !== 'INF0') { // exiftool.org claims INF0 can happen
|
|
192
|
-
return; // Not an INFO chunk
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let currentPos = slice.filePos;
|
|
196
|
-
while (currentPos <= startPos + size - 8) {
|
|
197
|
-
slice.filePos = currentPos;
|
|
198
|
-
|
|
199
|
-
const chunkName = readAscii(slice, 4);
|
|
200
|
-
const chunkSize = readU32(slice, littleEndian);
|
|
201
|
-
const bytes = readBytes(slice, chunkSize);
|
|
202
|
-
|
|
203
|
-
let stringLength = 0;
|
|
204
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
205
|
-
if (bytes[i] === 0) {
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
stringLength++;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const value = String.fromCharCode(...bytes.subarray(0, stringLength));
|
|
213
|
-
|
|
214
|
-
this.metadataTags.raw ??= {};
|
|
215
|
-
this.metadataTags.raw[chunkName] = value;
|
|
216
|
-
|
|
217
|
-
switch (chunkName) {
|
|
218
|
-
case 'INAM':
|
|
219
|
-
case 'TITL': {
|
|
220
|
-
this.metadataTags.title ??= value;
|
|
221
|
-
}; break;
|
|
222
|
-
|
|
223
|
-
case 'TIT3': {
|
|
224
|
-
this.metadataTags.description ??= value;
|
|
225
|
-
}; break;
|
|
226
|
-
|
|
227
|
-
case 'IART': {
|
|
228
|
-
this.metadataTags.artist ??= value;
|
|
229
|
-
}; break;
|
|
230
|
-
|
|
231
|
-
case 'IPRD': {
|
|
232
|
-
this.metadataTags.album ??= value;
|
|
233
|
-
}; break;
|
|
234
|
-
|
|
235
|
-
case 'IPRT':
|
|
236
|
-
case 'ITRK':
|
|
237
|
-
case 'TRCK': {
|
|
238
|
-
const parts = value.split('/');
|
|
239
|
-
const trackNum = Number.parseInt(parts[0]!, 10);
|
|
240
|
-
const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
|
|
241
|
-
|
|
242
|
-
if (Number.isInteger(trackNum) && trackNum > 0) {
|
|
243
|
-
this.metadataTags.trackNumber ??= trackNum;
|
|
244
|
-
}
|
|
245
|
-
if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
|
246
|
-
this.metadataTags.tracksTotal ??= tracksTotal;
|
|
247
|
-
}
|
|
248
|
-
}; break;
|
|
249
|
-
|
|
250
|
-
case 'ICRD':
|
|
251
|
-
case 'IDIT': {
|
|
252
|
-
const date = new Date(value);
|
|
253
|
-
if (!Number.isNaN(date.getTime())) {
|
|
254
|
-
this.metadataTags.date ??= date;
|
|
255
|
-
}
|
|
256
|
-
}; break;
|
|
257
|
-
|
|
258
|
-
case 'YEAR': {
|
|
259
|
-
const year = Number.parseInt(value, 10);
|
|
260
|
-
if (Number.isInteger(year) && year > 0) {
|
|
261
|
-
this.metadataTags.date ??= new Date(year, 0, 1);
|
|
262
|
-
}
|
|
263
|
-
}; break;
|
|
264
|
-
|
|
265
|
-
case 'IGNR':
|
|
266
|
-
case 'GENR': {
|
|
267
|
-
this.metadataTags.genre ??= value;
|
|
268
|
-
}; break;
|
|
269
|
-
|
|
270
|
-
case 'ICMT':
|
|
271
|
-
case 'CMNT':
|
|
272
|
-
case 'COMM': {
|
|
273
|
-
this.metadataTags.comment ??= value;
|
|
274
|
-
}; break;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
currentPos += 8 + chunkSize + (chunkSize & 1); // Handle padding
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private async parseId3Chunk(startPos: number, size: number) {
|
|
282
|
-
// Parse ID3 tag embedded in WAV file (non-default, but used a lot in practice anyway)
|
|
283
|
-
let slice = this.reader.requestSlice(startPos, size);
|
|
284
|
-
if (slice instanceof Promise) slice = await slice;
|
|
285
|
-
if (!slice) return; // File too short
|
|
286
|
-
|
|
287
|
-
const id3V2Header = readId3V2Header(slice);
|
|
288
|
-
if (id3V2Header) {
|
|
289
|
-
// Extract the content portion (skip the 10-byte header)
|
|
290
|
-
const contentSlice = slice.slice(startPos + 10, id3V2Header.size);
|
|
291
|
-
|
|
292
|
-
parseId3V2Tag(contentSlice, id3V2Header, this.metadataTags);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
getCodec(): AudioCodec | null {
|
|
297
|
-
assert(this.audioInfo);
|
|
298
|
-
|
|
299
|
-
if (this.audioInfo.format === WaveFormat.MULAW) {
|
|
300
|
-
return 'ulaw';
|
|
301
|
-
}
|
|
302
|
-
if (this.audioInfo.format === WaveFormat.ALAW) {
|
|
303
|
-
return 'alaw';
|
|
304
|
-
}
|
|
305
|
-
if (this.audioInfo.format === WaveFormat.PCM) {
|
|
306
|
-
// All formats are little-endian
|
|
307
|
-
if (this.audioInfo.sampleSizeInBytes === 1) {
|
|
308
|
-
return 'pcm-u8';
|
|
309
|
-
} else if (this.audioInfo.sampleSizeInBytes === 2) {
|
|
310
|
-
return 'pcm-s16';
|
|
311
|
-
} else if (this.audioInfo.sampleSizeInBytes === 3) {
|
|
312
|
-
return 'pcm-s24';
|
|
313
|
-
} else if (this.audioInfo.sampleSizeInBytes === 4) {
|
|
314
|
-
return 'pcm-s32';
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (this.audioInfo.format === WaveFormat.IEEE_FLOAT) {
|
|
318
|
-
if (this.audioInfo.sampleSizeInBytes === 4) {
|
|
319
|
-
return 'pcm-f32';
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async getMimeType() {
|
|
327
|
-
return 'audio/wav';
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async computeDuration() {
|
|
331
|
-
await this.readMetadata();
|
|
332
|
-
|
|
333
|
-
const track = this.tracks[0];
|
|
334
|
-
assert(track);
|
|
335
|
-
|
|
336
|
-
return track.computeDuration();
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async getTracks() {
|
|
340
|
-
await this.readMetadata();
|
|
341
|
-
return this.tracks;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
async getMetadataTags() {
|
|
345
|
-
await this.readMetadata();
|
|
346
|
-
return this.metadataTags;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const PACKET_SIZE_IN_FRAMES = 2048;
|
|
351
|
-
|
|
352
|
-
class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
|
353
|
-
constructor(public demuxer: WaveDemuxer) {}
|
|
354
|
-
|
|
355
|
-
getId() {
|
|
356
|
-
return 1;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
getCodec() {
|
|
360
|
-
return this.demuxer.getCodec();
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
getInternalCodecId() {
|
|
364
|
-
assert(this.demuxer.audioInfo);
|
|
365
|
-
return this.demuxer.audioInfo.format;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async getDecoderConfig(): Promise<AudioDecoderConfig | null> {
|
|
369
|
-
const codec = this.demuxer.getCodec();
|
|
370
|
-
if (!codec) {
|
|
371
|
-
return null;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
assert(this.demuxer.audioInfo);
|
|
375
|
-
return {
|
|
376
|
-
codec,
|
|
377
|
-
numberOfChannels: this.demuxer.audioInfo.numberOfChannels,
|
|
378
|
-
sampleRate: this.demuxer.audioInfo.sampleRate,
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
async computeDuration() {
|
|
383
|
-
const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
|
|
384
|
-
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
getNumberOfChannels() {
|
|
388
|
-
assert(this.demuxer.audioInfo);
|
|
389
|
-
return this.demuxer.audioInfo.numberOfChannels;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
getSampleRate() {
|
|
393
|
-
assert(this.demuxer.audioInfo);
|
|
394
|
-
return this.demuxer.audioInfo.sampleRate;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
getTimeResolution() {
|
|
398
|
-
assert(this.demuxer.audioInfo);
|
|
399
|
-
return this.demuxer.audioInfo.sampleRate;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
getName() {
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
getLanguageCode() {
|
|
407
|
-
return UNDETERMINED_LANGUAGE;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
getDisposition() {
|
|
411
|
-
return {
|
|
412
|
-
...DEFAULT_TRACK_DISPOSITION,
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async getFirstTimestamp() {
|
|
417
|
-
return 0;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
private async getPacketAtIndex(
|
|
421
|
-
packetIndex: number,
|
|
422
|
-
options: PacketRetrievalOptions,
|
|
423
|
-
): Promise<EncodedPacket | null> {
|
|
424
|
-
assert(this.demuxer.audioInfo);
|
|
425
|
-
const startOffset = packetIndex * PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes;
|
|
426
|
-
if (startOffset >= this.demuxer.dataSize) {
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const sizeInBytes = Math.min(
|
|
431
|
-
PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes,
|
|
432
|
-
this.demuxer.dataSize - startOffset,
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
if (this.demuxer.reader.fileSize === null) {
|
|
436
|
-
// If the file size is unknown, we weren't able to cap the dataSize in the init logic and we instead have to
|
|
437
|
-
// rely on the headers telling us how large the file is. But, these might be wrong, so let's check if the
|
|
438
|
-
// requested slice actually exists.
|
|
439
|
-
|
|
440
|
-
let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes);
|
|
441
|
-
if (slice instanceof Promise) slice = await slice;
|
|
442
|
-
|
|
443
|
-
if (!slice) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
let data: Uint8Array;
|
|
449
|
-
if (options.metadataOnly) {
|
|
450
|
-
data = PLACEHOLDER_DATA;
|
|
451
|
-
} else {
|
|
452
|
-
let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes);
|
|
453
|
-
if (slice instanceof Promise) slice = await slice;
|
|
454
|
-
assert(slice);
|
|
455
|
-
|
|
456
|
-
data = readBytes(slice, sizeInBytes);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const timestamp = packetIndex * PACKET_SIZE_IN_FRAMES / this.demuxer.audioInfo.sampleRate;
|
|
460
|
-
const duration = sizeInBytes / this.demuxer.audioInfo.blockSizeInBytes / this.demuxer.audioInfo.sampleRate;
|
|
461
|
-
|
|
462
|
-
this.demuxer.lastKnownPacketIndex = Math.max(
|
|
463
|
-
packetIndex,
|
|
464
|
-
timestamp,
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
return new EncodedPacket(
|
|
468
|
-
data,
|
|
469
|
-
'key',
|
|
470
|
-
timestamp,
|
|
471
|
-
duration,
|
|
472
|
-
packetIndex,
|
|
473
|
-
sizeInBytes,
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
getFirstPacket(options: PacketRetrievalOptions) {
|
|
478
|
-
return this.getPacketAtIndex(0, options);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
async getPacket(timestamp: number, options: PacketRetrievalOptions) {
|
|
482
|
-
assert(this.demuxer.audioInfo);
|
|
483
|
-
|
|
484
|
-
const packetIndex = Math.floor(Math.min(
|
|
485
|
-
timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES,
|
|
486
|
-
(this.demuxer.dataSize - 1) / (PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes),
|
|
487
|
-
));
|
|
488
|
-
|
|
489
|
-
const packet = await this.getPacketAtIndex(packetIndex, options);
|
|
490
|
-
if (packet) {
|
|
491
|
-
return packet;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (packetIndex === 0) {
|
|
495
|
-
return null; // Empty data chunk
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
assert(this.demuxer.reader.fileSize === null);
|
|
499
|
-
|
|
500
|
-
// The file is shorter than we thought, meaning the packet we were looking for doesn't exist. So, let's find
|
|
501
|
-
// the last packet by doing a sequential scan, instead.
|
|
502
|
-
let currentPacket = await this.getPacketAtIndex(this.demuxer.lastKnownPacketIndex, options);
|
|
503
|
-
while (currentPacket) {
|
|
504
|
-
const nextPacket = await this.getNextPacket(currentPacket, options);
|
|
505
|
-
if (!nextPacket) {
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
currentPacket = nextPacket;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return currentPacket;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
|
|
516
|
-
assert(this.demuxer.audioInfo);
|
|
517
|
-
const packetIndex = Math.round(packet.timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES);
|
|
518
|
-
|
|
519
|
-
return this.getPacketAtIndex(packetIndex + 1, options);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
getKeyPacket(timestamp: number, options: PacketRetrievalOptions) {
|
|
523
|
-
return this.getPacket(timestamp, options);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
getNextKeyPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
|
|
527
|
-
return this.getNextPacket(packet, options);
|
|
528
|
-
}
|
|
529
|
-
}
|