mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Add track count limit checking
This commit is contained in:
+23
-2
@@ -32,7 +32,7 @@
|
||||
|
||||
fileInput.addEventListener('change', async () => {
|
||||
const file = fileInput.files[0];
|
||||
const source = new Metamuxer.BlobSource(file);
|
||||
const source = new Metamuxer.BufferSource(await file.arrayBuffer()) ?? new Metamuxer.BlobSource(file);
|
||||
|
||||
const start = performance.now();
|
||||
const input = new Metamuxer.Input({
|
||||
@@ -40,7 +40,27 @@
|
||||
source
|
||||
});
|
||||
|
||||
const videoTrack = await input.getPrimaryVideoTrack();
|
||||
const output = new Metamuxer.Output({
|
||||
format: new Metamuxer.WaveOutputFormat(),
|
||||
target: new Metamuxer.BufferTarget()
|
||||
});
|
||||
output.start();
|
||||
|
||||
/*
|
||||
const audioTrack = await input.getPrimaryAudioTrack();
|
||||
const sink = new Metamuxer.EncodedAudioSampleSink(audioTrack);
|
||||
|
||||
let total = 0;
|
||||
for await (const sample of sink.samples(undefined, undefined, { metadataOnly: true })) {
|
||||
total++;
|
||||
if (performance.now() > 10000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("Done", total);
|
||||
*/
|
||||
|
||||
/*
|
||||
const sink = new Metamuxer.VideoFrameSink(videoTrack);
|
||||
|
||||
console.log(await videoTrack.getLanguageCode());
|
||||
@@ -49,6 +69,7 @@
|
||||
console.log(thing)
|
||||
}
|
||||
console.log("Done")
|
||||
*/
|
||||
|
||||
/*
|
||||
//const videoTrack = await input.getPrimaryVideoTrack();
|
||||
|
||||
@@ -5,6 +5,8 @@ export {
|
||||
VideoTrackMetadata,
|
||||
AudioTrackMetadata,
|
||||
SubtitleTrackMetadata,
|
||||
TrackType,
|
||||
ALL_TRACK_TYPES,
|
||||
} from './output';
|
||||
export {
|
||||
OutputFormat,
|
||||
@@ -17,6 +19,8 @@ export {
|
||||
WebMOutputFormat,
|
||||
WebMOutputFormatOptions,
|
||||
WaveOutputFormat,
|
||||
TrackCountLimits,
|
||||
InclusiveRange,
|
||||
} from './output-format';
|
||||
export {
|
||||
VideoEncodingConfig,
|
||||
|
||||
+39
-2
@@ -2,18 +2,28 @@ import { AUDIO_CODECS, MediaCodec, PCM_CODECS, SUBTITLE_CODECS, VIDEO_CODECS } f
|
||||
import { IsobmffMuxer } from './isobmff/isobmff-muxer';
|
||||
import { MatroskaMuxer } from './matroska/matroska-muxer';
|
||||
import { Muxer } from './muxer';
|
||||
import { Output } from './output';
|
||||
import { Output, TrackType } from './output';
|
||||
import { WaveMuxer } from './wave/wave-muxer';
|
||||
|
||||
/** @public */
|
||||
export type InclusiveRange = { min: number; max: number };
|
||||
/** @public */
|
||||
export type TrackCountLimits = {
|
||||
[K in TrackType]: InclusiveRange;
|
||||
} & {
|
||||
total: InclusiveRange;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export abstract class OutputFormat {
|
||||
/** @internal */
|
||||
abstract _createMuxer(output: Output): Muxer;
|
||||
|
||||
/** @internal */
|
||||
abstract _getName(): string;
|
||||
|
||||
abstract getFileExtension(): string;
|
||||
abstract getSupportedCodecs(): MediaCodec[];
|
||||
abstract getSupportedTrackCounts(): TrackCountLimits;
|
||||
|
||||
getSupportedVideoCodecs() {
|
||||
return this.getSupportedCodecs().filter(codec => (VIDEO_CODECS as readonly string[]).includes(codec));
|
||||
@@ -57,6 +67,15 @@ export abstract class IsobmffOutputFormat extends OutputFormat {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
getSupportedTrackCounts(): TrackCountLimits {
|
||||
return {
|
||||
video: { min: 0, max: Infinity },
|
||||
audio: { min: 0, max: Infinity },
|
||||
subtitle: { min: 0, max: Infinity },
|
||||
total: { min: 1, max: 2 ** 32 - 1 }, // Have fun reaching this one
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createMuxer(output: Output) {
|
||||
return new IsobmffMuxer(output, this);
|
||||
@@ -161,6 +180,15 @@ export class MkvOutputFormat extends OutputFormat {
|
||||
return 'Matroska';
|
||||
}
|
||||
|
||||
getSupportedTrackCounts(): TrackCountLimits {
|
||||
return {
|
||||
video: { min: 0, max: Infinity },
|
||||
audio: { min: 0, max: Infinity },
|
||||
subtitle: { min: 0, max: Infinity },
|
||||
total: { min: 1, max: 127 },
|
||||
};
|
||||
}
|
||||
|
||||
getFileExtension() {
|
||||
return '.mkv';
|
||||
}
|
||||
@@ -228,6 +256,15 @@ export class WaveOutputFormat extends OutputFormat {
|
||||
return 'WAVE';
|
||||
}
|
||||
|
||||
getSupportedTrackCounts(): TrackCountLimits {
|
||||
return {
|
||||
video: { min: 0, max: 0 },
|
||||
audio: { min: 1, max: 1 },
|
||||
subtitle: { min: 0, max: 0 },
|
||||
total: { min: 1, max: 1 },
|
||||
};
|
||||
}
|
||||
|
||||
getFileExtension() {
|
||||
return '.wav';
|
||||
}
|
||||
|
||||
@@ -11,9 +11,15 @@ export type OutputOptions = {
|
||||
target: Target;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const ALL_TRACK_TYPES = ['video', 'audio', 'subtitle'] as const;
|
||||
/** @public */
|
||||
export type TrackType = typeof ALL_TRACK_TYPES[number];
|
||||
|
||||
export type OutputTrack = {
|
||||
id: number;
|
||||
output: Output;
|
||||
type: TrackType;
|
||||
} & ({
|
||||
type: 'video';
|
||||
source: VideoSource;
|
||||
@@ -152,6 +158,29 @@ export class Output {
|
||||
throw new Error('Source is already used for a track.');
|
||||
}
|
||||
|
||||
// Verify maximum track count constraints
|
||||
const supportedTrackCounts = this._format.getSupportedTrackCounts();
|
||||
const presentTracksOfThisType = this._tracks.reduce(
|
||||
(count, track) => count + (track.type === type ? 1 : 0),
|
||||
0,
|
||||
);
|
||||
const maxCount = supportedTrackCounts[type].max;
|
||||
if (presentTracksOfThisType === maxCount) {
|
||||
throw new Error(
|
||||
maxCount === 0
|
||||
? `${this._format._getName()} does not support ${type} tracks.`
|
||||
: (`${this._format._getName()} does not support more than ${maxCount} ${type} track`
|
||||
+ `${maxCount === 1 ? '' : 's'}.`),
|
||||
);
|
||||
}
|
||||
const maxTotalCount = supportedTrackCounts.total.max;
|
||||
if (this._tracks.length === maxTotalCount) {
|
||||
throw new Error(
|
||||
`${this._format._getName()} does not support more than ${maxTotalCount} tracks`
|
||||
+ `${maxTotalCount === 1 ? '' : 's'} in total.`,
|
||||
);
|
||||
}
|
||||
|
||||
const track = {
|
||||
id: this._tracks.length + 1,
|
||||
output: this,
|
||||
@@ -219,6 +248,35 @@ export class Output {
|
||||
throw new Error('Output already started.');
|
||||
}
|
||||
|
||||
// Verify minimum track count constraints
|
||||
const supportedTrackCounts = this._format.getSupportedTrackCounts();
|
||||
for (const trackType of ALL_TRACK_TYPES) {
|
||||
const presentTracksOfThisType = this._tracks.reduce(
|
||||
(count, track) => count + (track.type === trackType ? 1 : 0),
|
||||
0,
|
||||
);
|
||||
const minCount = supportedTrackCounts[trackType].min;
|
||||
if (presentTracksOfThisType < minCount) {
|
||||
throw new Error(
|
||||
minCount === supportedTrackCounts[trackType].max
|
||||
? (`${this._format._getName()} requires exactly ${minCount} ${trackType}`
|
||||
+ ` track${minCount === 1 ? '' : 's'}.`)
|
||||
: (`${this._format._getName()} requires at least ${minCount} ${trackType}`
|
||||
+ ` track${minCount === 1 ? '' : 's'}.`),
|
||||
);
|
||||
}
|
||||
}
|
||||
const totalMinCount = supportedTrackCounts.total.min;
|
||||
if (this._tracks.length < totalMinCount) {
|
||||
throw new Error(
|
||||
totalMinCount === supportedTrackCounts.total.max
|
||||
? (`${this._format._getName()} requires exactly ${totalMinCount} track`
|
||||
+ `${totalMinCount === 1 ? '' : 's'}.`)
|
||||
: (`${this._format._getName()} requires at least ${totalMinCount} track`
|
||||
+ `${totalMinCount === 1 ? '' : 's'}.`),
|
||||
);
|
||||
}
|
||||
|
||||
this._started = true;
|
||||
this._writer.start();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user