diff --git a/src/input-format.ts b/src/input-format.ts index 515ec9e..cc7e8aa 100644 --- a/src/input-format.ts +++ b/src/input-format.ts @@ -35,7 +35,7 @@ export abstract class InputFormat { export abstract class IsobmffInputFormat extends InputFormat { /** @internal */ protected async _getMajorBrand(input: Input) { - const sourceSize = await input._mainReader.source._getSize(); + const sourceSize = await input._mainReader.source.getSize(); if (sourceSize < 12) { return null; } @@ -104,7 +104,7 @@ export class QuickTimeInputFormat extends IsobmffInputFormat { export class MatroskaInputFormat extends InputFormat { /** @internal */ protected async isSupportedEBMLOfDocType(input: Input, desiredDocType: string) { - const sourceSize = await input._mainReader.source._getSize(); + const sourceSize = await input._mainReader.source.getSize(); if (sourceSize < 8) { return false; } @@ -209,7 +209,7 @@ export class WebMInputFormat extends MatroskaInputFormat { export class Mp3InputFormat extends InputFormat { /** @internal */ async _canReadInput(input: Input) { - const sourceSize = await input._mainReader.source._getSize(); + const sourceSize = await input._mainReader.source.getSize(); if (sourceSize < 4) { return false; } @@ -274,7 +274,7 @@ export class Mp3InputFormat extends InputFormat { export class WaveInputFormat extends InputFormat { /** @internal */ async _canReadInput(input: Input) { - const sourceSize = await input._mainReader.source._getSize(); + const sourceSize = await input._mainReader.source.getSize(); if (sourceSize < 12) { return false; } @@ -311,7 +311,7 @@ export class WaveInputFormat extends InputFormat { export class OggInputFormat extends InputFormat { /** @internal */ async _canReadInput(input: Input) { - const sourceSize = await input._mainReader.source._getSize(); + const sourceSize = await input._mainReader.source.getSize(); if (sourceSize < 4) { return false; } diff --git a/src/input.ts b/src/input.ts index ad022c4..8a12fc2 100644 --- a/src/input.ts +++ b/src/input.ts @@ -8,20 +8,20 @@ import { Source } from './source'; * The options for creating an Input object. * @public */ -export type InputOptions = { +export type InputOptions = { /** A list of supported formats. If the source file is not of one of these formats, then it cannot be read. */ formats: InputFormat[]; /** The source from which data will be read. */ - source: Source; + source: S; }; /** * Represents an input media file. This is the root object from which all media read operations start. * @public */ -export class Input { +export class Input { /** @internal */ - _source: Source; + _source: S; /** @internal */ _formats: InputFormat[]; /** @internal */ @@ -31,7 +31,7 @@ export class Input { /** @internal */ _format: InputFormat | null = null; - constructor(options: InputOptions) { + constructor(options: InputOptions) { if (!options || typeof options !== 'object') { throw new TypeError('options must be an object.'); } @@ -64,6 +64,14 @@ export class Input { })(); } + /** + * Returns the source from which this input file reads its data. This is the same source that was passed to the + * constructor. + */ + get source() { + return this._source; + } + /** * Returns the format of the input file. You can compare this result directly to the InputFormat singletons or use * `instanceof` checks for subset-aware logic (for example, `format instanceof MatroskaInputFormat` is true for diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts index 7dc3a9a..87e2573 100644 --- a/src/isobmff/isobmff-demuxer.ts +++ b/src/isobmff/isobmff-demuxer.ts @@ -198,7 +198,7 @@ export class IsobmffDemuxer extends Demuxer { super(input); this.metadataReader = new IsobmffReader(input._mainReader); - this.chunkReader = new IsobmffReader(new Reader(input._source, 64 * 2 ** 20)); // Max 64 MiB of stored chunks + this.chunkReader = new IsobmffReader(new Reader(input.source, 64 * 2 ** 20)); // Max 64 MiB of stored chunks } override async computeDuration() { @@ -235,7 +235,7 @@ export class IsobmffDemuxer extends Demuxer { readMetadata() { return this.metadataPromise ??= (async () => { - const sourceSize = await this.metadataReader.reader.source._getSize(); + const sourceSize = await this.metadataReader.reader.source.getSize(); while (this.metadataReader.pos < sourceSize) { await this.metadataReader.reader.loadRange( @@ -2191,7 +2191,7 @@ abstract class IsobmffTrackBacking implements InputTrackBacking { } const metadataReader = demuxer.metadataReader; - const sourceSize = await metadataReader.reader.source._getSize(); + const sourceSize = await metadataReader.reader.source.getSize(); let prevFragment: Fragment | null = null; let bestFragmentIndex = fragmentIndex; diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index c2afde4..d7be74f 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -165,7 +165,7 @@ export class MatroskaDemuxer extends Demuxer { this.metadataReader = new EBMLReader(input._mainReader); // Max 64 MiB of stored clusters - this.clusterReader = new EBMLReader(new Reader(input._source, 64 * 2 ** 20)); + this.clusterReader = new EBMLReader(new Reader(input.source, 64 * 2 ** 20)); } override async computeDuration() { @@ -211,7 +211,7 @@ export class MatroskaDemuxer extends Demuxer { return this.readMetadataPromise ??= (async () => { this.metadataReader.pos = 0; - const fileSize = await this.input._source._getSize(); + const fileSize = await this.input.source.getSize(); while (this.metadataReader.pos < fileSize - MIN_HEADER_SIZE) { await this.metadataReader.reader.loadRange( @@ -258,7 +258,7 @@ export class MatroskaDemuxer extends Demuxer { dataStartPos: segmentDataStart, elementEndPos: dataSize === -1 - ? (await this.input._source._getSize() - MIN_HEADER_SIZE) + ? (await this.input.source.getSize() - MIN_HEADER_SIZE) : segmentDataStart + dataSize, clusterSeekStartPos: segmentDataStart, diff --git a/src/mp3/mp3-demuxer.ts b/src/mp3/mp3-demuxer.ts index 83a519d..ba9f16c 100644 --- a/src/mp3/mp3-demuxer.ts +++ b/src/mp3/mp3-demuxer.ts @@ -32,7 +32,7 @@ export class Mp3Demuxer extends Demuxer { async readMetadata() { return this.metadataPromise ??= (async () => { - const fileSize = await this.input._source._getSize(); + const fileSize = await this.input.source.getSize(); this.reader.fileSize = fileSize; // Just load the entire file. Primitive, but the only way to actually ensure 100% correct timestamps. diff --git a/src/ogg/ogg-demuxer.ts b/src/ogg/ogg-demuxer.ts index a9045d9..3626400 100644 --- a/src/ogg/ogg-demuxer.ts +++ b/src/ogg/ogg-demuxer.ts @@ -38,12 +38,12 @@ export class OggDemuxer extends Demuxer { super(input); // We don't need a persistent metadata reader as we read all metadata once at the start and then never again - this.reader = new OggReader(new Reader(input._source, 64 * 2 ** 20)); + this.reader = new OggReader(new Reader(input.source, 64 * 2 ** 20)); } async readMetadata() { return this.metadataPromise ??= (async () => { - this.fileSize = await this.input._source._getSize(); + this.fileSize = await this.input.source.getSize(); while (this.reader.pos < this.fileSize - MIN_PAGE_HEADER_SIZE) { await this.reader.reader.loadRange( diff --git a/src/reader.ts b/src/reader.ts index 88a8ac4..4f720a6 100644 --- a/src/reader.ts +++ b/src/reader.ts @@ -25,7 +25,7 @@ export class Reader { constructor(public source: Source, public maxStorableBytes = Infinity) {} async loadRange(start: number, end: number) { - end = Math.min(end, await this.source._getSize()); + end = Math.min(end, await this.source.getSize()); if (start >= end) { return; diff --git a/src/source.ts b/src/source.ts index 854532a..390ed7e 100644 --- a/src/source.ts +++ b/src/source.ts @@ -13,8 +13,11 @@ export abstract class Source { /** @internal */ _sizePromise: Promise | null = null; - /** @internal */ - _getSize() { + /** + * Resolves with the total size of the file in bytes. This function is memoized, meaning only the first call + * will retrieve the size. + */ + getSize() { return this._sizePromise ??= this._retrieveSize(); } diff --git a/src/wave/wave-demuxer.ts b/src/wave/wave-demuxer.ts index 79b1167..53ec8f0 100644 --- a/src/wave/wave-demuxer.ts +++ b/src/wave/wave-demuxer.ts @@ -37,7 +37,7 @@ export class WaveDemuxer extends Demuxer { super(input); this.metadataReader = new RiffReader(input._mainReader); - this.chunkReader = new RiffReader(new Reader(input._source, 64 * 2 ** 20)); + this.chunkReader = new RiffReader(new Reader(input.source, 64 * 2 ** 20)); } async readMetadata() { diff --git a/todo.txt b/todo.txt index 3f5dfc6..43ab60d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,5 @@ - https://github.com/Vanilagy/mp4-muxer/issues/83 tell him it's possible now - cross-track offset for streaming sources - textsubtitlesource, chunked piping -- More efficient MP3 loading when reading sequentially \ No newline at end of file +- More efficient MP3 loading when reading sequentially +- Validate types returned by custom coders \ No newline at end of file