130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createVideoDecoder = exports.internalCreateVideoDecoder = void 0;
|
|
const flush_pending_1 = require("./flush-pending");
|
|
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
|
const undecodable_error_1 = require("./undecodable-error");
|
|
const internalCreateVideoDecoder = 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: 'Video decoder',
|
|
controller,
|
|
});
|
|
let mostRecentSampleReceived = null;
|
|
const videoDecoder = new VideoDecoder({
|
|
async output(frame) {
|
|
try {
|
|
await onFrame(frame);
|
|
}
|
|
catch (err) {
|
|
onError(err);
|
|
frame.close();
|
|
}
|
|
ioSynchronizer.onOutput(frame.timestamp);
|
|
},
|
|
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 (videoDecoder.state === 'closed') {
|
|
return;
|
|
}
|
|
videoDecoder.close();
|
|
};
|
|
const onAbort = () => {
|
|
close();
|
|
};
|
|
if (controller) {
|
|
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
|
|
}
|
|
const isConfigSupported = await VideoDecoder.isConfigSupported(config);
|
|
if (!isConfigSupported) {
|
|
throw new undecodable_error_1.VideoUndecodableError({
|
|
message: 'Video cannot be decoded by this browser',
|
|
config,
|
|
});
|
|
}
|
|
videoDecoder.configure(config);
|
|
const decode = async (sample) => {
|
|
if (videoDecoder.state === 'closed') {
|
|
return;
|
|
}
|
|
try {
|
|
await controller?._internals._mediaParserController._internals.checkForAbortAndPause();
|
|
}
|
|
catch (err) {
|
|
onError(err);
|
|
return;
|
|
}
|
|
mostRecentSampleReceived = sample.timestamp;
|
|
const encodedChunk = sample instanceof EncodedVideoChunk
|
|
? sample
|
|
: new EncodedVideoChunk(sample);
|
|
videoDecoder.decode(encodedChunk);
|
|
ioSynchronizer.inputItem(sample.timestamp);
|
|
};
|
|
let flushPending = null;
|
|
let 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 videoDecoder.flush();
|
|
})
|
|
.catch(() => {
|
|
// Firefox might throw "Needs to be configured first"
|
|
})
|
|
.finally(() => {
|
|
pendingFlush.resolve();
|
|
flushPending = null;
|
|
});
|
|
return pendingFlush.promise;
|
|
},
|
|
waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
|
|
reset: () => {
|
|
lastReset = Date.now();
|
|
flushPending?.resolve();
|
|
ioSynchronizer.clearQueue();
|
|
videoDecoder.reset();
|
|
videoDecoder.configure(config);
|
|
},
|
|
checkReset: () => {
|
|
const initTime = Date.now();
|
|
return {
|
|
wasReset: () => lastReset !== null && lastReset > initTime,
|
|
};
|
|
},
|
|
getMostRecentSampleInput() {
|
|
return mostRecentSampleReceived;
|
|
},
|
|
};
|
|
};
|
|
exports.internalCreateVideoDecoder = internalCreateVideoDecoder;
|
|
const createVideoDecoder = ({ onFrame, onError, controller, track, logLevel, }) => {
|
|
return (0, exports.internalCreateVideoDecoder)({
|
|
onFrame,
|
|
onError,
|
|
controller: controller ?? null,
|
|
config: track,
|
|
logLevel: logLevel ?? 'info',
|
|
});
|
|
};
|
|
exports.createVideoDecoder = createVideoDecoder;
|