mediabunny
Version:
Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.
105 lines (104 loc) • 4.07 kB
JavaScript
/*!
* Copyright (c) 2025-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { InputFormat } from './input-format.js';
import { assert } from './misc.js';
import { Reader } from './reader.js';
import { Source } from './source.js';
/**
* Represents an input media file. This is the root object from which all media read operations start.
* @public
*/
export class Input {
constructor(options) {
/** @internal */
this._demuxerPromise = null;
/** @internal */
this._format = null;
if (!options || typeof options !== 'object') {
throw new TypeError('options must be an object.');
}
if (!Array.isArray(options.formats) || options.formats.some(x => !(x instanceof InputFormat))) {
throw new TypeError('options.formats must be an array of InputFormat.');
}
if (!(options.source instanceof Source)) {
throw new TypeError('options.source must be a Source.');
}
this._formats = options.formats;
this._source = options.source;
this._mainReader = new Reader(options.source);
}
/** @internal */
_getDemuxer() {
return this._demuxerPromise ??= (async () => {
await this._mainReader.loadRange(0, 4096); // Load the first 4 kiB so we can determine the format
for (const format of this._formats) {
const canRead = await format._canReadInput(this);
if (canRead) {
this._format = format;
return format._createDemuxer(this);
}
}
throw new Error('Input has an unsupported or unrecognizable format.');
})();
}
/**
* 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
* both MKV and WebM).
*/
async getFormat() {
await this._getDemuxer();
assert(this._format);
return this._format;
}
/**
* Computes the duration of the input file, in seconds. More precisely, returns the largest end timestamp among
* all tracks.
*/
async computeDuration() {
const demuxer = await this._getDemuxer();
return demuxer.computeDuration();
}
/** Returns the list of all tracks of this input file. */
async getTracks() {
const demuxer = await this._getDemuxer();
return demuxer.getTracks();
}
/** Returns the list of all video tracks of this input file. */
async getVideoTracks() {
const tracks = await this.getTracks();
return tracks.filter(x => x.isVideoTrack());
}
/** Returns the primary video track of this input file, or null if there are no video tracks. */
async getPrimaryVideoTrack() {
const tracks = await this.getTracks();
return tracks.find(x => x.isVideoTrack()) ?? null;
}
/** Returns the list of all audio tracks of this input file. */
async getAudioTracks() {
const tracks = await this.getTracks();
return tracks.filter(x => x.isAudioTrack());
}
/** Returns the primary audio track of this input file, or null if there are no audio tracks. */
async getPrimaryAudioTrack() {
const tracks = await this.getTracks();
return tracks.find(x => x.isAudioTrack()) ?? null;
}
/** Returns the full MIME type of this input file, including track codecs. */
async getMimeType() {
const demuxer = await this._getDemuxer();
return demuxer.getMimeType();
}
}