@kenzuya/mediabunny 1.26.0 → 1.28.6
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 -21390
- 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/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.d.ts +0 -9
- package/dist/modules/src/node.d.ts.map +0 -1
- 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/flac/flac-demuxer.ts
DELETED
|
@@ -1,730 +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 { FlacBlockType, readVorbisComments } from '../codec-data';
|
|
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 {
|
|
15
|
-
assert,
|
|
16
|
-
AsyncMutex,
|
|
17
|
-
binarySearchLessOrEqual,
|
|
18
|
-
Bitstream,
|
|
19
|
-
textDecoder,
|
|
20
|
-
UNDETERMINED_LANGUAGE,
|
|
21
|
-
} from '../misc';
|
|
22
|
-
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
|
23
|
-
import {
|
|
24
|
-
FileSlice,
|
|
25
|
-
readBytes,
|
|
26
|
-
Reader,
|
|
27
|
-
readU24Be,
|
|
28
|
-
readU32Be,
|
|
29
|
-
readU8,
|
|
30
|
-
} from '../reader';
|
|
31
|
-
import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
|
|
32
|
-
import {
|
|
33
|
-
calculateCrc8,
|
|
34
|
-
readBlockSize,
|
|
35
|
-
getBlockSizeOrUncommon,
|
|
36
|
-
readCodedNumber,
|
|
37
|
-
readSampleRate,
|
|
38
|
-
getSampleRateOrUncommon,
|
|
39
|
-
} from './flac-misc';
|
|
40
|
-
|
|
41
|
-
type FlacAudioInfo = {
|
|
42
|
-
numberOfChannels: number;
|
|
43
|
-
sampleRate: number;
|
|
44
|
-
totalSamples: number;
|
|
45
|
-
minimumBlockSize: number;
|
|
46
|
-
maximumBlockSize: number;
|
|
47
|
-
minimumFrameSize: number;
|
|
48
|
-
maximumFrameSize: number;
|
|
49
|
-
description: Uint8Array;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type Sample = {
|
|
53
|
-
blockOffset: number;
|
|
54
|
-
blockSize: number;
|
|
55
|
-
byteOffset: number;
|
|
56
|
-
byteSize: number;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
type NextFlacFrameResult = {
|
|
60
|
-
num: number;
|
|
61
|
-
blockSize: number;
|
|
62
|
-
sampleRate: number;
|
|
63
|
-
size: number;
|
|
64
|
-
isLastFrame: boolean;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export class FlacDemuxer extends Demuxer {
|
|
68
|
-
reader: Reader;
|
|
69
|
-
|
|
70
|
-
loadedSamples: Sample[] = []; // All samples from the start of the file to lastLoadedPos
|
|
71
|
-
|
|
72
|
-
metadataPromise: Promise<void> | null = null;
|
|
73
|
-
track: InputAudioTrack | null = null;
|
|
74
|
-
metadataTags: MetadataTags = {};
|
|
75
|
-
|
|
76
|
-
audioInfo: FlacAudioInfo | null = null;
|
|
77
|
-
lastLoadedPos: number | null = null;
|
|
78
|
-
blockingBit: number | null = null;
|
|
79
|
-
|
|
80
|
-
readingMutex = new AsyncMutex();
|
|
81
|
-
lastSampleLoaded = false;
|
|
82
|
-
|
|
83
|
-
constructor(input: Input) {
|
|
84
|
-
super(input);
|
|
85
|
-
|
|
86
|
-
this.reader = input._reader;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
override async computeDuration(): Promise<number> {
|
|
90
|
-
await this.readMetadata();
|
|
91
|
-
assert(this.track);
|
|
92
|
-
return this.track.computeDuration();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
override async getMetadataTags(): Promise<MetadataTags> {
|
|
96
|
-
await this.readMetadata();
|
|
97
|
-
return this.metadataTags;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async getTracks() {
|
|
101
|
-
await this.readMetadata();
|
|
102
|
-
assert(this.track);
|
|
103
|
-
return [this.track];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async getMimeType() {
|
|
107
|
-
return 'audio/flac';
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async readMetadata() {
|
|
111
|
-
let currentPos = 4; // Skip 'fLaC'
|
|
112
|
-
|
|
113
|
-
return (this.metadataPromise ??= (async () => {
|
|
114
|
-
while (
|
|
115
|
-
this.reader.fileSize === null
|
|
116
|
-
|| currentPos < this.reader.fileSize
|
|
117
|
-
) {
|
|
118
|
-
let sizeSlice = this.reader.requestSlice(currentPos, 4);
|
|
119
|
-
if (sizeSlice instanceof Promise) sizeSlice = await sizeSlice;
|
|
120
|
-
currentPos += 4;
|
|
121
|
-
|
|
122
|
-
if (sizeSlice === null) {
|
|
123
|
-
throw new Error(
|
|
124
|
-
`Metadata block at position ${currentPos} is too small! Corrupted file.`,
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
assert(sizeSlice);
|
|
129
|
-
|
|
130
|
-
const byte = readU8(sizeSlice); // first bit: isLastMetadata, remaining 7 bits: metaBlockType
|
|
131
|
-
const size = readU24Be(sizeSlice);
|
|
132
|
-
const isLastMetadata = (byte & 0x80) !== 0;
|
|
133
|
-
const metaBlockType = byte & 0x7f;
|
|
134
|
-
|
|
135
|
-
switch (metaBlockType) {
|
|
136
|
-
case FlacBlockType.STREAMINFO: {
|
|
137
|
-
// Parse streaminfo block
|
|
138
|
-
// https://www.rfc-editor.org/rfc/rfc9639.html#section-8.2
|
|
139
|
-
let streamInfoBlock = this.reader.requestSlice(
|
|
140
|
-
currentPos,
|
|
141
|
-
size,
|
|
142
|
-
);
|
|
143
|
-
if (streamInfoBlock instanceof Promise) streamInfoBlock = await streamInfoBlock;
|
|
144
|
-
|
|
145
|
-
assert(streamInfoBlock);
|
|
146
|
-
if (streamInfoBlock === null) {
|
|
147
|
-
throw new Error(
|
|
148
|
-
`StreamInfo block at position ${currentPos} is too small! Corrupted file.`,
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const streamInfoBytes = readBytes(streamInfoBlock, 34);
|
|
153
|
-
const bitstream = new Bitstream(streamInfoBytes);
|
|
154
|
-
|
|
155
|
-
const minimumBlockSize = bitstream.readBits(16);
|
|
156
|
-
const maximumBlockSize = bitstream.readBits(16);
|
|
157
|
-
const minimumFrameSize = bitstream.readBits(24);
|
|
158
|
-
const maximumFrameSize = bitstream.readBits(24);
|
|
159
|
-
|
|
160
|
-
const sampleRate = bitstream.readBits(20);
|
|
161
|
-
const numberOfChannels = bitstream.readBits(3) + 1;
|
|
162
|
-
bitstream.readBits(5); // bitsPerSample - 1
|
|
163
|
-
const totalSamples = bitstream.readBits(36);
|
|
164
|
-
|
|
165
|
-
// https://www.w3.org/TR/webcodecs-flac-codec-registration/#audiodecoderconfig-description
|
|
166
|
-
// description is required, and has to be the following:
|
|
167
|
-
// 1. The bytes 0x66 0x4C 0x61 0x43 ("fLaC" in ASCII)
|
|
168
|
-
// 2. A metadata block (called the STREAMINFO block) as described in section 7 of [FLAC]
|
|
169
|
-
// 3. Optionaly (sic) other metadata blocks, that are not used by the specification
|
|
170
|
-
|
|
171
|
-
bitstream.skipBits(16 * 8); // md5 hash
|
|
172
|
-
|
|
173
|
-
const description = new Uint8Array(42);
|
|
174
|
-
// 1. "fLaC"
|
|
175
|
-
description.set(new Uint8Array([0x66, 0x4c, 0x61, 0x43]), 0);
|
|
176
|
-
// 2. STREAMINFO block
|
|
177
|
-
description.set(new Uint8Array([128, 0, 0, 34]), 4);
|
|
178
|
-
// 3. Other metadata blocks
|
|
179
|
-
description.set(streamInfoBytes, 8);
|
|
180
|
-
|
|
181
|
-
this.audioInfo = {
|
|
182
|
-
numberOfChannels,
|
|
183
|
-
sampleRate,
|
|
184
|
-
totalSamples,
|
|
185
|
-
minimumBlockSize,
|
|
186
|
-
maximumBlockSize,
|
|
187
|
-
minimumFrameSize,
|
|
188
|
-
maximumFrameSize,
|
|
189
|
-
description,
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
this.track = new InputAudioTrack(this.input, new FlacAudioTrackBacking(this));
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
case FlacBlockType.VORBIS_COMMENT: {
|
|
196
|
-
// Parse vorbis comment block
|
|
197
|
-
// https://www.rfc-editor.org/rfc/rfc9639.html#name-vorbis-comment
|
|
198
|
-
let vorbisCommentBlock = this.reader.requestSlice(
|
|
199
|
-
currentPos,
|
|
200
|
-
size,
|
|
201
|
-
);
|
|
202
|
-
if (vorbisCommentBlock instanceof Promise) vorbisCommentBlock = await vorbisCommentBlock;
|
|
203
|
-
|
|
204
|
-
assert(vorbisCommentBlock);
|
|
205
|
-
|
|
206
|
-
readVorbisComments(
|
|
207
|
-
readBytes(vorbisCommentBlock, size),
|
|
208
|
-
this.metadataTags,
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
case FlacBlockType.PICTURE: {
|
|
214
|
-
// Parse picture block
|
|
215
|
-
// https://www.rfc-editor.org/rfc/rfc9639.html#name-picture
|
|
216
|
-
let pictureBlock = this.reader.requestSlice(
|
|
217
|
-
currentPos,
|
|
218
|
-
size,
|
|
219
|
-
);
|
|
220
|
-
if (pictureBlock instanceof Promise) pictureBlock = await pictureBlock;
|
|
221
|
-
|
|
222
|
-
assert(pictureBlock);
|
|
223
|
-
const pictureType = readU32Be(pictureBlock);
|
|
224
|
-
const mediaTypeLength = readU32Be(pictureBlock);
|
|
225
|
-
const mediaType = textDecoder.decode(
|
|
226
|
-
readBytes(pictureBlock, mediaTypeLength),
|
|
227
|
-
);
|
|
228
|
-
const descriptionLength = readU32Be(pictureBlock);
|
|
229
|
-
const description = textDecoder.decode(
|
|
230
|
-
readBytes(pictureBlock, descriptionLength),
|
|
231
|
-
);
|
|
232
|
-
pictureBlock.skip(4 + 4 + 4 + 4); // Skip width, height, color depth, number of indexed colors
|
|
233
|
-
const dataLength = readU32Be(pictureBlock);
|
|
234
|
-
const data = readBytes(pictureBlock, dataLength);
|
|
235
|
-
|
|
236
|
-
this.metadataTags.images ??= [];
|
|
237
|
-
this.metadataTags.images.push({
|
|
238
|
-
data,
|
|
239
|
-
mimeType: mediaType,
|
|
240
|
-
// https://www.rfc-editor.org/rfc/rfc9639.html#table13
|
|
241
|
-
kind:
|
|
242
|
-
pictureType === 3
|
|
243
|
-
? 'coverFront'
|
|
244
|
-
: pictureType === 4
|
|
245
|
-
? 'coverBack'
|
|
246
|
-
: 'unknown',
|
|
247
|
-
description,
|
|
248
|
-
});
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
default:
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
currentPos += size;
|
|
255
|
-
|
|
256
|
-
if (isLastMetadata) {
|
|
257
|
-
this.lastLoadedPos = currentPos;
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
})());
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async readNextFlacFrame({
|
|
265
|
-
startPos,
|
|
266
|
-
isFirstPacket,
|
|
267
|
-
}: {
|
|
268
|
-
startPos: number;
|
|
269
|
-
isFirstPacket: boolean;
|
|
270
|
-
}): Promise<NextFlacFrameResult | null> {
|
|
271
|
-
assert(this.audioInfo);
|
|
272
|
-
// we expect that there are at least `minimumFrameSize` bytes left in the file
|
|
273
|
-
|
|
274
|
-
// Ideally we also want to validate the next header is valid
|
|
275
|
-
// to throw out an accidential sync word
|
|
276
|
-
|
|
277
|
-
// The shortest valid FLAC header I can think of, based off the code
|
|
278
|
-
// of readFlacFrameHeader:
|
|
279
|
-
// 4 bytes used for bitstream from syncword to bit depth
|
|
280
|
-
// 1 byte coded number
|
|
281
|
-
// (uncommon values, no bytes read)
|
|
282
|
-
// 1 byte crc
|
|
283
|
-
// --> 6 bytes
|
|
284
|
-
const minimumHeaderLength = 6;
|
|
285
|
-
// If we read everything in readFlacFrameHeader, we read 16 bytes
|
|
286
|
-
const maximumHeaderSize = 16;
|
|
287
|
-
const maximumSliceLength
|
|
288
|
-
= this.audioInfo.maximumFrameSize + maximumHeaderSize;
|
|
289
|
-
|
|
290
|
-
const slice = await this.reader.requestSliceRange(
|
|
291
|
-
startPos,
|
|
292
|
-
this.audioInfo.minimumFrameSize,
|
|
293
|
-
maximumSliceLength,
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
if (!slice) {
|
|
297
|
-
return null;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
const frameHeader = this.readFlacFrameHeader({
|
|
301
|
-
slice,
|
|
302
|
-
isFirstPacket: isFirstPacket,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
if (!frameHeader) {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// We don't know exactly how long the packet is, we only know the `minimumFrameSize` and `maximumFrameSize`
|
|
310
|
-
// The packet is over if the next 2 bytes are the sync word followed by a valid header
|
|
311
|
-
// or the end of the file is reached
|
|
312
|
-
|
|
313
|
-
// The next sync word is expected at earliest when `minimumFrameSize` is reached,
|
|
314
|
-
// we can skip over anything before that
|
|
315
|
-
slice.filePos = startPos + this.audioInfo.minimumFrameSize;
|
|
316
|
-
|
|
317
|
-
while (true) {
|
|
318
|
-
// Reached end of the file, packet is over
|
|
319
|
-
if (slice.filePos > slice.end - minimumHeaderLength) {
|
|
320
|
-
return {
|
|
321
|
-
num: frameHeader.num,
|
|
322
|
-
blockSize: frameHeader.blockSize,
|
|
323
|
-
sampleRate: frameHeader.sampleRate,
|
|
324
|
-
size: slice.end - startPos,
|
|
325
|
-
isLastFrame: true,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const nextByte = readU8(slice);
|
|
330
|
-
if (nextByte === 0xff) {
|
|
331
|
-
const positionBeforeReading = slice.filePos;
|
|
332
|
-
|
|
333
|
-
const byteAfterNextByte = readU8(slice);
|
|
334
|
-
|
|
335
|
-
const expected = this.blockingBit === 1 ? 0b1111_1001 : 0b1111_1000;
|
|
336
|
-
if (byteAfterNextByte !== expected) {
|
|
337
|
-
slice.filePos = positionBeforeReading;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
slice.skip(-2);
|
|
342
|
-
const lengthIfNextFlacFrameHeaderIsLegit = slice.filePos - startPos;
|
|
343
|
-
|
|
344
|
-
const nextFrameHeader = this.readFlacFrameHeader({
|
|
345
|
-
slice,
|
|
346
|
-
isFirstPacket: false,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
if (!nextFrameHeader) {
|
|
350
|
-
slice.filePos = positionBeforeReading;
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Ensure the frameOrSampleNum is consecutive.
|
|
355
|
-
// https://github.com/Vanilagy/mediabunny/issues/194
|
|
356
|
-
|
|
357
|
-
if (this.blockingBit === 0) {
|
|
358
|
-
// Case A: If the stream is fixed block size, this is the frame number, which increments by 1
|
|
359
|
-
if (nextFrameHeader.num - frameHeader.num !== 1) {
|
|
360
|
-
slice.filePos = positionBeforeReading;
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
} else {
|
|
364
|
-
// Case B: If the stream is variable block size, this is the sample number, which increments by
|
|
365
|
-
// amount of samples in a frame.
|
|
366
|
-
if (nextFrameHeader.num - frameHeader.num !== frameHeader.blockSize) {
|
|
367
|
-
slice.filePos = positionBeforeReading;
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return {
|
|
373
|
-
num: frameHeader.num,
|
|
374
|
-
blockSize: frameHeader.blockSize,
|
|
375
|
-
sampleRate: frameHeader.sampleRate,
|
|
376
|
-
size: lengthIfNextFlacFrameHeaderIsLegit,
|
|
377
|
-
isLastFrame: false,
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
readFlacFrameHeader({
|
|
384
|
-
slice,
|
|
385
|
-
isFirstPacket,
|
|
386
|
-
}: {
|
|
387
|
-
slice: FileSlice;
|
|
388
|
-
isFirstPacket: boolean;
|
|
389
|
-
}) {
|
|
390
|
-
// In this function, generally it is not safe to throw errors.
|
|
391
|
-
// We might end up here because we stumbled upon a syncword,
|
|
392
|
-
// but the data might not actually be a FLAC frame, it might be random bitstream
|
|
393
|
-
// data, in that case we should return null and continue.
|
|
394
|
-
|
|
395
|
-
const startOffset = slice.filePos;
|
|
396
|
-
|
|
397
|
-
// https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1
|
|
398
|
-
// Each frame MUST start on a byte boundary and start with the 15-bit frame
|
|
399
|
-
// sync code 0b111111111111100. Following the sync code is the blocking strategy
|
|
400
|
-
// bit, which MUST NOT change during the audio stream.
|
|
401
|
-
const bytes = readBytes(slice, 4);
|
|
402
|
-
const bitstream = new Bitstream(bytes);
|
|
403
|
-
|
|
404
|
-
const bits = bitstream.readBits(15);
|
|
405
|
-
if (bits !== 0b111111111111100) {
|
|
406
|
-
// This cannot be a valid FLAC frame, must start with the syncword
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (this.blockingBit === null) {
|
|
411
|
-
assert(isFirstPacket);
|
|
412
|
-
const newBlockingBit = bitstream.readBits(1);
|
|
413
|
-
this.blockingBit = newBlockingBit;
|
|
414
|
-
} else if (this.blockingBit === 1) {
|
|
415
|
-
assert(!isFirstPacket);
|
|
416
|
-
const newBlockingBit = bitstream.readBits(1);
|
|
417
|
-
if (newBlockingBit !== 1) {
|
|
418
|
-
// This cannot be a valid FLAC frame, expected 1 but got 0
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
|
-
} else if (this.blockingBit === 0) {
|
|
422
|
-
assert(!isFirstPacket);
|
|
423
|
-
const newBlockingBit = bitstream.readBits(1);
|
|
424
|
-
if (newBlockingBit !== 0) {
|
|
425
|
-
// This cannot be a valid FLAC frame, expected 0 but got 1
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
} else {
|
|
429
|
-
throw new Error('Invalid blocking bit');
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const blockSizeOrUncommon = getBlockSizeOrUncommon(bitstream.readBits(4));
|
|
433
|
-
if (!blockSizeOrUncommon) {
|
|
434
|
-
// This cannot be a valid FLAC frame, the syncword was just coincidental
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
assert(this.audioInfo);
|
|
438
|
-
const sampleRateOrUncommon = getSampleRateOrUncommon(
|
|
439
|
-
bitstream.readBits(4),
|
|
440
|
-
this.audioInfo.sampleRate,
|
|
441
|
-
);
|
|
442
|
-
if (!sampleRateOrUncommon) {
|
|
443
|
-
// This cannot be a valid FLAC frame, the syncword was just coincidental
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
bitstream.readBits(4); // channel count
|
|
448
|
-
bitstream.readBits(3); // bit depth
|
|
449
|
-
const reservedZero = bitstream.readBits(1); // reserved zero
|
|
450
|
-
|
|
451
|
-
if (reservedZero !== 0) {
|
|
452
|
-
// This cannot be a valid FLAC frame, the syncword was just coincidental
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const num = readCodedNumber(slice);
|
|
457
|
-
const blockSize = readBlockSize(slice, blockSizeOrUncommon);
|
|
458
|
-
|
|
459
|
-
const sampleRate = readSampleRate(slice, sampleRateOrUncommon);
|
|
460
|
-
if (sampleRate === null) {
|
|
461
|
-
// This cannot be a valid FLAC frame, the syncword was just coincidental
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (sampleRate !== this.audioInfo.sampleRate) {
|
|
466
|
-
// This cannot be a valid FLAC frame, the sample rate is not the same as in the stream info
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const size = slice.filePos - startOffset;
|
|
471
|
-
const crc = readU8(slice);
|
|
472
|
-
|
|
473
|
-
slice.skip(-size);
|
|
474
|
-
slice.skip(-1);
|
|
475
|
-
const crcCalculated = calculateCrc8(readBytes(slice, size));
|
|
476
|
-
|
|
477
|
-
if (crc !== crcCalculated) {
|
|
478
|
-
// Maybe this wasn't a FLAC frame at all, the syncword was just coincidentally
|
|
479
|
-
// in the bitstream
|
|
480
|
-
return null;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return { num, blockSize, sampleRate };
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
async advanceReader() {
|
|
487
|
-
await this.readMetadata();
|
|
488
|
-
assert(this.lastLoadedPos !== null);
|
|
489
|
-
assert(this.audioInfo);
|
|
490
|
-
const startPos = this.lastLoadedPos;
|
|
491
|
-
const frame = await this.readNextFlacFrame({
|
|
492
|
-
startPos,
|
|
493
|
-
isFirstPacket: this.loadedSamples.length === 0,
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
if (!frame) {
|
|
497
|
-
// Unexpected case, failed to read next FLAC frame
|
|
498
|
-
// handling gracefully
|
|
499
|
-
this.lastSampleLoaded = true;
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const lastSample = this.loadedSamples[this.loadedSamples.length - 1];
|
|
504
|
-
const blockOffset = lastSample
|
|
505
|
-
? lastSample.blockOffset + lastSample.blockSize
|
|
506
|
-
: 0;
|
|
507
|
-
|
|
508
|
-
const sample: Sample = {
|
|
509
|
-
blockOffset,
|
|
510
|
-
blockSize: frame.blockSize,
|
|
511
|
-
byteOffset: startPos,
|
|
512
|
-
byteSize: frame.size,
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
this.lastLoadedPos = this.lastLoadedPos + frame.size;
|
|
516
|
-
this.loadedSamples.push(sample);
|
|
517
|
-
|
|
518
|
-
if (frame.isLastFrame) {
|
|
519
|
-
this.lastSampleLoaded = true;
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
class FlacAudioTrackBacking implements InputAudioTrackBacking {
|
|
526
|
-
constructor(public demuxer: FlacDemuxer) {}
|
|
527
|
-
|
|
528
|
-
getId() {
|
|
529
|
-
return 1;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
getCodec() {
|
|
533
|
-
return 'flac' as const;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
getInternalCodecId(): string | number | Uint8Array | null {
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
getNumberOfChannels() {
|
|
541
|
-
assert(this.demuxer.audioInfo);
|
|
542
|
-
return this.demuxer.audioInfo.numberOfChannels;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
async computeDuration() {
|
|
546
|
-
const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
|
|
547
|
-
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
getSampleRate() {
|
|
551
|
-
assert(this.demuxer.audioInfo);
|
|
552
|
-
return this.demuxer.audioInfo.sampleRate;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
getName(): string | null {
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
getLanguageCode() {
|
|
560
|
-
return UNDETERMINED_LANGUAGE;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
getTimeResolution() {
|
|
564
|
-
assert(this.demuxer.audioInfo);
|
|
565
|
-
return this.demuxer.audioInfo.sampleRate;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
getDisposition() {
|
|
569
|
-
return {
|
|
570
|
-
...DEFAULT_TRACK_DISPOSITION,
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
async getFirstTimestamp() {
|
|
575
|
-
return 0;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async getDecoderConfig(): Promise<AudioDecoderConfig | null> {
|
|
579
|
-
assert(this.demuxer.audioInfo);
|
|
580
|
-
|
|
581
|
-
return {
|
|
582
|
-
codec: 'flac' as const,
|
|
583
|
-
numberOfChannels: this.demuxer.audioInfo.numberOfChannels,
|
|
584
|
-
sampleRate: this.demuxer.audioInfo.sampleRate,
|
|
585
|
-
description: this.demuxer.audioInfo.description,
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
async getPacket(
|
|
590
|
-
timestamp: number,
|
|
591
|
-
options: PacketRetrievalOptions,
|
|
592
|
-
): Promise<EncodedPacket | null> {
|
|
593
|
-
assert(this.demuxer.audioInfo);
|
|
594
|
-
if (timestamp < 0) {
|
|
595
|
-
throw new Error('Timestamp cannot be negative');
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const release = await this.demuxer.readingMutex.acquire();
|
|
599
|
-
|
|
600
|
-
try {
|
|
601
|
-
while (true) {
|
|
602
|
-
const packetIndex = binarySearchLessOrEqual(
|
|
603
|
-
this.demuxer.loadedSamples,
|
|
604
|
-
timestamp,
|
|
605
|
-
x => x.blockOffset / this.demuxer.audioInfo!.sampleRate,
|
|
606
|
-
);
|
|
607
|
-
if (packetIndex === -1) {
|
|
608
|
-
await this.demuxer.advanceReader();
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const packet = this.demuxer.loadedSamples[packetIndex]!;
|
|
613
|
-
const sampleTimestamp
|
|
614
|
-
= packet.blockOffset / this.demuxer.audioInfo.sampleRate;
|
|
615
|
-
const sampleDuration
|
|
616
|
-
= packet.blockSize / this.demuxer.audioInfo.sampleRate;
|
|
617
|
-
|
|
618
|
-
if (sampleTimestamp + sampleDuration <= timestamp) {
|
|
619
|
-
if (this.demuxer.lastSampleLoaded) {
|
|
620
|
-
return this.getPacketAtIndex(
|
|
621
|
-
this.demuxer.loadedSamples.length - 1,
|
|
622
|
-
options,
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
await this.demuxer.advanceReader();
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
return this.getPacketAtIndex(packetIndex, options);
|
|
631
|
-
}
|
|
632
|
-
} finally {
|
|
633
|
-
release();
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
async getNextPacket(
|
|
638
|
-
packet: EncodedPacket,
|
|
639
|
-
options: PacketRetrievalOptions,
|
|
640
|
-
): Promise<EncodedPacket | null> {
|
|
641
|
-
const release = await this.demuxer.readingMutex.acquire();
|
|
642
|
-
try {
|
|
643
|
-
const nextIndex = packet.sequenceNumber + 1;
|
|
644
|
-
if (
|
|
645
|
-
this.demuxer.lastSampleLoaded
|
|
646
|
-
&& nextIndex >= this.demuxer.loadedSamples.length
|
|
647
|
-
) {
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Ensure the next sample exists
|
|
652
|
-
while (
|
|
653
|
-
nextIndex >= this.demuxer.loadedSamples.length
|
|
654
|
-
&& !this.demuxer.lastSampleLoaded
|
|
655
|
-
) {
|
|
656
|
-
await this.demuxer.advanceReader();
|
|
657
|
-
}
|
|
658
|
-
return this.getPacketAtIndex(nextIndex, options);
|
|
659
|
-
} finally {
|
|
660
|
-
release();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
getKeyPacket(
|
|
665
|
-
timestamp: number,
|
|
666
|
-
options: PacketRetrievalOptions,
|
|
667
|
-
): Promise<EncodedPacket | null> {
|
|
668
|
-
return this.getPacket(timestamp, options);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
getNextKeyPacket(
|
|
672
|
-
packet: EncodedPacket,
|
|
673
|
-
options: PacketRetrievalOptions,
|
|
674
|
-
): Promise<EncodedPacket | null> {
|
|
675
|
-
return this.getNextPacket(packet, options);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
async getPacketAtIndex(
|
|
679
|
-
sampleIndex: number,
|
|
680
|
-
options: PacketRetrievalOptions,
|
|
681
|
-
): Promise<EncodedPacket | null> {
|
|
682
|
-
const rawSample = this.demuxer.loadedSamples[sampleIndex];
|
|
683
|
-
if (!rawSample) {
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
let data: Uint8Array;
|
|
688
|
-
if (options.metadataOnly) {
|
|
689
|
-
data = PLACEHOLDER_DATA;
|
|
690
|
-
} else {
|
|
691
|
-
let slice = this.demuxer.reader.requestSlice(
|
|
692
|
-
rawSample.byteOffset,
|
|
693
|
-
rawSample.byteSize,
|
|
694
|
-
);
|
|
695
|
-
if (slice instanceof Promise) slice = await slice;
|
|
696
|
-
|
|
697
|
-
if (!slice) {
|
|
698
|
-
return null; // Data didn't fit into the rest of the file
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
data = readBytes(slice, rawSample.byteSize);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
assert(this.demuxer.audioInfo);
|
|
705
|
-
const timestamp = rawSample.blockOffset / this.demuxer.audioInfo.sampleRate;
|
|
706
|
-
const duration = rawSample.blockSize / this.demuxer.audioInfo.sampleRate;
|
|
707
|
-
return new EncodedPacket(
|
|
708
|
-
data,
|
|
709
|
-
'key',
|
|
710
|
-
timestamp,
|
|
711
|
-
duration,
|
|
712
|
-
sampleIndex,
|
|
713
|
-
rawSample.byteSize,
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
async getFirstPacket(
|
|
718
|
-
options: PacketRetrievalOptions,
|
|
719
|
-
): Promise<EncodedPacket | null> {
|
|
720
|
-
// Ensure the next sample exists
|
|
721
|
-
while (
|
|
722
|
-
this.demuxer.loadedSamples.length === 0
|
|
723
|
-
&& !this.demuxer.lastSampleLoaded
|
|
724
|
-
) {
|
|
725
|
-
await this.demuxer.advanceReader();
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
return this.getPacketAtIndex(0, options);
|
|
729
|
-
}
|
|
730
|
-
}
|