156 lines
5.2 KiB
JavaScript
156 lines
5.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createAudioDecoder = exports.internalCreateAudioDecoder = void 0;
|
|
const flush_pending_1 = require("./flush-pending");
|
|
const get_wave_audio_decoder_1 = require("./get-wave-audio-decoder");
|
|
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
|
const undecodable_error_1 = require("./undecodable-error");
|
|
const internalCreateAudioDecoder = async ({ onFrame, onError, controller, config, logLevel, }) => {
|
|
if (controller &&
|
|
controller._internals._mediaParserController._internals.signal.aborted) {
|
|
throw new Error('Not creating audio decoder, already aborted');
|
|
}
|
|
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
|
|
logLevel,
|
|
label: 'Audio decoder',
|
|
controller,
|
|
});
|
|
let mostRecentSampleReceived = null;
|
|
if (config.codec === 'pcm-s16') {
|
|
return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
|
|
onFrame,
|
|
config,
|
|
sampleFormat: 's16',
|
|
logLevel,
|
|
ioSynchronizer,
|
|
onError,
|
|
});
|
|
}
|
|
if (config.codec === 'pcm-s24') {
|
|
return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
|
|
onFrame,
|
|
config,
|
|
sampleFormat: 's24',
|
|
logLevel,
|
|
ioSynchronizer,
|
|
onError,
|
|
});
|
|
}
|
|
const audioDecoder = new AudioDecoder({
|
|
async output(frame) {
|
|
try {
|
|
await onFrame(frame);
|
|
}
|
|
catch (err) {
|
|
frame.close();
|
|
onError(err);
|
|
}
|
|
ioSynchronizer.onOutput(frame.timestamp + (frame.duration ?? 0));
|
|
},
|
|
error(error) {
|
|
onError(error);
|
|
},
|
|
});
|
|
const close = () => {
|
|
if (controller) {
|
|
controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
|
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
onAbort);
|
|
}
|
|
if (audioDecoder.state === 'closed') {
|
|
return;
|
|
}
|
|
audioDecoder.close();
|
|
};
|
|
const onAbort = () => {
|
|
close();
|
|
};
|
|
if (controller) {
|
|
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
|
|
}
|
|
const isConfigSupported = await AudioDecoder.isConfigSupported(config);
|
|
if (!isConfigSupported) {
|
|
throw new undecodable_error_1.AudioUndecodableError({
|
|
message: 'Audio cannot be decoded by this browser',
|
|
config,
|
|
});
|
|
}
|
|
audioDecoder.configure(config);
|
|
const decode = async (audioSample) => {
|
|
if (audioDecoder.state === 'closed') {
|
|
return;
|
|
}
|
|
try {
|
|
await controller?._internals._mediaParserController._internals.checkForAbortAndPause();
|
|
}
|
|
catch (err) {
|
|
onError(err);
|
|
return;
|
|
}
|
|
mostRecentSampleReceived = audioSample.timestamp;
|
|
// Don't flush, it messes up the audio
|
|
const chunk = audioSample instanceof EncodedAudioChunk
|
|
? audioSample
|
|
: new EncodedAudioChunk(audioSample);
|
|
audioDecoder.decode(chunk);
|
|
// https://test-streams.mux.dev/x36xhzz/url_0/url_525/193039199_mp4_h264_aac_hd_7.ts
|
|
// has a 16 byte audio sample at the end which chrome does not decode
|
|
// Might be empty audio
|
|
// For now only reporting chunks that are bigger than that
|
|
// 16 was chosen arbitrarily, can be improved
|
|
if (chunk.byteLength > 16) {
|
|
ioSynchronizer.inputItem(chunk.timestamp);
|
|
}
|
|
};
|
|
let flushPending = null;
|
|
const lastReset = null;
|
|
return {
|
|
decode,
|
|
close,
|
|
flush: () => {
|
|
if (flushPending) {
|
|
throw new Error('Flush already pending');
|
|
}
|
|
const pendingFlush = (0, flush_pending_1.makeFlushPending)();
|
|
flushPending = pendingFlush;
|
|
Promise.resolve()
|
|
.then(() => {
|
|
return audioDecoder.flush();
|
|
})
|
|
.catch(() => {
|
|
// Firefox might throw "Needs to be configured first"
|
|
})
|
|
.finally(() => {
|
|
pendingFlush.resolve();
|
|
flushPending = null;
|
|
});
|
|
return pendingFlush.promise;
|
|
},
|
|
waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
|
|
reset: () => {
|
|
audioDecoder.reset();
|
|
audioDecoder.configure(config);
|
|
},
|
|
checkReset: () => {
|
|
const initTime = Date.now();
|
|
return {
|
|
wasReset: () => lastReset !== null && lastReset > initTime,
|
|
};
|
|
},
|
|
getMostRecentSampleInput() {
|
|
return mostRecentSampleReceived;
|
|
},
|
|
};
|
|
};
|
|
exports.internalCreateAudioDecoder = internalCreateAudioDecoder;
|
|
const createAudioDecoder = ({ track, onFrame, onError, controller, logLevel, }) => {
|
|
return (0, exports.internalCreateAudioDecoder)({
|
|
onFrame,
|
|
onError,
|
|
controller: controller ?? null,
|
|
config: track,
|
|
logLevel: logLevel ?? 'error',
|
|
});
|
|
};
|
|
exports.createAudioDecoder = createAudioDecoder;
|