A BUNCH of HLS (and related) progress

- Add support for fMP4 segments
- Add initInput (supported by ISOBMFF and MPEG-TS)
- Input.isSupported()
- Refactored file size retrieval logic, the file size can now be retrieved alongside the first read.
- Add support for unsized UrlSources
- Add support for multiple trun boxes in a row for the same track (as the spec literally says lol)
- Add support for more M3U8 features
- Fix faulty line reader
- MPEG-TS improvements: support video parameters not in first packet, support better key frame detection, support extension PES packets without PTS
- Fix reorder buffer being 1 element too small
This commit is contained in:
Vanilagy
2026-02-20 19:56:20 +01:00
parent de84d78092
commit 68b3ae7155
73 changed files with 17552 additions and 967 deletions
+24 -3
View File
@@ -14,6 +14,8 @@ import { Source } from './source';
polyfillSymbolDispose();
const UNSUPPORTED_INPUT_FORMAT_MESSAGE = 'Input has an unsupported or unrecognizable format.';
/**
* The options for creating an Input object.
* @group Input files & tracks
@@ -24,6 +26,7 @@ export type InputOptions<S extends Source = Source> = {
formats: InputFormat[];
/** The source from which data will be read. */
source: S;
initInput?: Input;
};
/**
@@ -37,6 +40,8 @@ export class Input<S extends Source = Source> implements Disposable {
/** @internal */
_formats: InputFormat[];
/** @internal */
_initInput: Input | null;
/** @internal */
_demuxerPromise: Promise<Demuxer> | null = null;
/** @internal */
_format: InputFormat | null = null;
@@ -67,15 +72,19 @@ export class Input<S extends Source = Source> implements Disposable {
if (options.source._disposed) {
throw new TypeError('options.source must not be disposed.');
}
if (options.initInput !== undefined && !(options.initInput instanceof Input)) {
throw new TypeError('options.initInput, when provided, must be an Input.');
}
this._formats = options.formats;
this._source = options.source;
this._initInput = options.initInput ?? null;
}
/** @internal */
_getDemuxer() {
return this._demuxerPromise ??= (async () => {
this._reader = await Reader.fromSource(this._source);
this._reader = new Reader(this._source);
for (const format of this._formats) {
const canRead = await format._canReadInput(this);
@@ -85,8 +94,7 @@ export class Input<S extends Source = Source> implements Disposable {
}
}
console.log(this._source);
throw new Error('Input has an unsupported or unrecognizable format.');
throw new Error(UNSUPPORTED_INPUT_FORMAT_MESSAGE);
})();
}
@@ -109,6 +117,19 @@ export class Input<S extends Source = Source> implements Disposable {
return this._format;
}
async isSupported(): Promise<boolean> {
try {
const demuxer = await this._getDemuxer();
return demuxer.isSupported();
} catch (error) {
if (error instanceof Error && error.message === UNSUPPORTED_INPUT_FORMAT_MESSAGE) {
return false;
}
throw error;
}
}
/**
* Computes the duration of the input file, in seconds. More precisely, returns the largest end timestamp among
* all tracks.