This commit is contained in:
Vanilagy
2025-11-14 14:07:31 +01:00
parent abe6185ddd
commit 7fd2ec6373
11 changed files with 882 additions and 78 deletions
+43 -27
View File
@@ -12,7 +12,7 @@ import { Input } from '../input';
import { InputAudioTrack, InputAudioTrackBacking } from '../input-track';
import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
import { PacketRetrievalOptions } from '../media-sink';
import { assert, AsyncMutex, binarySearchExact, binarySearchLessOrEqual, UNDETERMINED_LANGUAGE } from '../misc';
import { assert, AsyncMutex, AsyncMutex2, binarySearchExact, binarySearchLessOrEqual, ResultValue, UNDETERMINED_LANGUAGE, Yo } from '../misc';
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
import { FrameHeader, getXingOffset, INFO, XING } from '../../shared/mp3-misc';
import {
@@ -42,7 +42,8 @@ export class Mp3Demuxer extends Demuxer {
tracks: InputAudioTrack[] = [];
readingMutex = new AsyncMutex();
// readingMutex = new AsyncMutex();
readingMutex = new AsyncMutex2();
lastSampleLoaded = false;
lastLoadedPos = 0;
nextTimestampInSamples = 0;
@@ -57,7 +58,9 @@ export class Mp3Demuxer extends Demuxer {
return this.metadataPromise ??= (async () => {
// Keep loading until we find the first frame header
while (!this.firstFrameHeader && !this.lastSampleLoaded) {
await this.advanceReader();
const result = new ResultValue<void>();
const promise = this.advanceReader(result);
if (result.pending) await promise;
}
if (!this.firstFrameHeader) {
@@ -68,7 +71,7 @@ export class Mp3Demuxer extends Demuxer {
})();
}
async advanceReader() {
async advanceReader(res: ResultValue<void>): Promise<Yo> {
if (this.lastLoadedPos === 0) {
// Let's skip all ID3v2 tags at the start of the file
while (true) {
@@ -77,7 +80,7 @@ export class Mp3Demuxer extends Demuxer {
if (!slice) {
this.lastSampleLoaded = true;
return;
return res.set();
}
const id3V2Header = readId3V2Header(slice);
@@ -89,19 +92,25 @@ export class Mp3Demuxer extends Demuxer {
}
}
const result = await readNextFrameHeader(this.reader, this.lastLoadedPos, this.reader.fileSize);
if (!result) {
const result = new ResultValue<{
header: FrameHeader;
startPos: number;
} | null>();
const promise = readNextFrameHeader(result, this.reader, this.lastLoadedPos, this.reader.fileSize);
if (result.pending) await promise;
if (!result.value) {
this.lastSampleLoaded = true;
return;
return res.set();
}
const header = result.header;
const header = result.value.header;
this.lastLoadedPos = result.startPos + header.totalSize - 1; // -1 in case the frame is 1 byte too short
this.lastLoadedPos = result.value.startPos + header.totalSize - 1; // -1 in case the frame is 1 byte too short
const xingOffset = getXingOffset(header.mpegVersionId, header.channel);
let slice = this.reader.requestSlice(result.startPos + xingOffset, 4);
let slice = this.reader.requestSlice(result.value.startPos + xingOffset, 4);
if (slice instanceof Promise) slice = await slice;
if (slice) {
const word = readU32Be(slice);
@@ -109,7 +118,7 @@ export class Mp3Demuxer extends Demuxer {
if (isXing) {
// There's no actual audio data in this frame, so let's skip it
return;
return res.set();
}
}
@@ -128,14 +137,14 @@ export class Mp3Demuxer extends Demuxer {
const sample: Sample = {
timestamp: this.nextTimestampInSamples / this.firstFrameHeader.sampleRate,
duration: sampleDuration,
dataStart: result.startPos,
dataStart: result.value.startPos,
dataSize: header.totalSize,
};
this.loadedSamples.push(sample);
this.nextTimestampInSamples += header.audioSamplesInFrame;
return;
return res.set();
}
async getMimeType() {
@@ -273,14 +282,14 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
};
}
async getPacketAtIndex(sampleIndex: number, options: PacketRetrievalOptions) {
async getPacketAtIndex(res: ResultValue<EncodedPacket | null>, sampleIndex: number, options: PacketRetrievalOptions): Promise<Yo> {
if (sampleIndex === -1) {
return null;
return res.set(null);
}
const rawSample = this.demuxer.loadedSamples[sampleIndex];
if (!rawSample) {
return null;
return res.set(null);
}
let data: Uint8Array;
@@ -291,28 +300,33 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
if (slice instanceof Promise) slice = await slice;
if (!slice) {
return null; // Data didn't fit into the rest of the file
return res.set(null); // Data didn't fit into the rest of the file
}
data = readBytes(slice, rawSample.dataSize);
}
return new EncodedPacket(
return res.set(new EncodedPacket(
data,
'key',
rawSample.timestamp,
rawSample.duration,
sampleIndex,
rawSample.dataSize,
);
));
}
getFirstPacket(options: PacketRetrievalOptions) {
return this.getPacketAtIndex(0, options);
getFirstPacket(res: ResultValue<EncodedPacket | null>, options: PacketRetrievalOptions) {
return this.getPacketAtIndex(res, 0, options);
}
async getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
const release = await this.demuxer.readingMutex.acquire();
async getNextPacket(res: ResultValue<EncodedPacket | null>, packet: EncodedPacket, options: PacketRetrievalOptions): Promise<Yo> {
// using foo = 5;
while (this.demuxer.readingMutex.locked) await this.demuxer.readingMutex.promise;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
using _ = this.demuxer.readingMutex.lock();
// const release = await this.demuxer.readingMutex.acquire();
try {
const sampleIndex = binarySearchExact(
@@ -330,12 +344,14 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
nextIndex >= this.demuxer.loadedSamples.length
&& !this.demuxer.lastSampleLoaded
) {
await this.demuxer.advanceReader();
const result = new ResultValue<void>();
const promise = this.demuxer.advanceReader(result);
if (result.pending) await promise;
}
return this.getPacketAtIndex(nextIndex, options);
return this.getPacketAtIndex(res, nextIndex, options);
} finally {
release();
// release();
}
}
+12 -6
View File
@@ -7,12 +7,18 @@
*/
import { FRAME_HEADER_SIZE, FrameHeader, readFrameHeader } from '../../shared/mp3-misc';
import { ResultValue, Yo } from '../misc';
import { Reader, readU32Be } from '../reader';
export const readNextFrameHeader = async (reader: Reader, startPos: number, until: number | null): Promise<{
header: FrameHeader;
startPos: number;
} | null> => {
export const readNextFrameHeader = async (
res: ResultValue<{
header: FrameHeader;
startPos: number;
} | null>,
reader: Reader,
startPos: number,
until: number | null,
): Promise<Yo> => {
let currentPos = startPos;
while (until === null || currentPos < until) {
@@ -24,11 +30,11 @@ export const readNextFrameHeader = async (reader: Reader, startPos: number, unti
const result = readFrameHeader(word, reader.fileSize !== null ? reader.fileSize - currentPos : null);
if (result.header) {
return { header: result.header, startPos: currentPos };
return res.set({ header: result.header, startPos: currentPos });
}
currentPos += result.bytesAdvanced;
}
return null;
return res.set(null);
};