mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Add functions that test codec encodability
This commit is contained in:
+103
-3
@@ -383,11 +383,17 @@ export const buildAudioCodecString = (codec: AudioCodec, numberOfChannels: numbe
|
|||||||
// Default to standard AAC-LC for higher sample rates
|
// Default to standard AAC-LC for higher sample rates
|
||||||
return 'mp4a.40.2'; // AAC-LC
|
return 'mp4a.40.2'; // AAC-LC
|
||||||
} else if (codec === 'mp3') {
|
} else if (codec === 'mp3') {
|
||||||
return 'mp3'; // Easy, this one
|
return 'mp3';
|
||||||
} else if (codec === 'opus') {
|
} else if (codec === 'opus') {
|
||||||
return 'opus'; // Easy, this one
|
return 'opus';
|
||||||
} else if (codec === 'vorbis') {
|
} else if (codec === 'vorbis') {
|
||||||
return 'vorbis'; // Also easy, this one
|
return 'vorbis';
|
||||||
|
} else if (codec === 'flac') {
|
||||||
|
return 'flac';
|
||||||
|
} else if (codec === 'ulaw') {
|
||||||
|
return 'ulaw';
|
||||||
|
} else if (codec === 'alaw') {
|
||||||
|
return 'alaw';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeError(`Unhandled codec '${codec}'.`);
|
throw new TypeError(`Unhandled codec '${codec}'.`);
|
||||||
@@ -825,3 +831,97 @@ export const validateSubtitleMetadata = (metadata: SubtitleMetadata | undefined)
|
|||||||
throw new TypeError('Subtitle metadata config description must be a string.');
|
throw new TypeError('Subtitle metadata config description must be a string.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const canEncode = (codec: MediaCodec) => {
|
||||||
|
if ((VIDEO_CODECS as readonly string[]).includes(codec)) {
|
||||||
|
return canEncodeVideo(codec as VideoCodec);
|
||||||
|
} else if ((AUDIO_CODECS as readonly string[]).includes(codec)) {
|
||||||
|
return canEncodeAudio(codec as AudioCodec);
|
||||||
|
} else if ((SUBTITLE_CODECS as readonly string[]).includes(codec)) {
|
||||||
|
return canEncodeSubtitles(codec as SubtitleCodec);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unhandled codec.');
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const canEncodeVideo = async (codec: VideoCodec, { width = 1280, height = 720, bitrate = 1e6 }: {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
bitrate?: 1e6;
|
||||||
|
} = {}) => {
|
||||||
|
if (typeof VideoEncoder === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const support = await VideoEncoder.isConfigSupported({
|
||||||
|
codec: buildVideoCodecString(codec, width, height, bitrate),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
bitrate,
|
||||||
|
...getVideoEncoderConfigExtension(codec),
|
||||||
|
});
|
||||||
|
|
||||||
|
return support.supported === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const canEncodeAudio = async (codec: AudioCodec, { numberOfChannels = 2, sampleRate = 48000, bitrate = 128e3 }: {
|
||||||
|
numberOfChannels?: number;
|
||||||
|
sampleRate?: number;
|
||||||
|
bitrate?: number;
|
||||||
|
} = {}) => {
|
||||||
|
if (typeof AudioEncoder === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((PCM_CODECS as readonly string[]).includes(codec)) {
|
||||||
|
return false; // TODO write encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
const support = await AudioEncoder.isConfigSupported({
|
||||||
|
codec: buildAudioCodecString(codec, numberOfChannels, sampleRate),
|
||||||
|
numberOfChannels,
|
||||||
|
sampleRate,
|
||||||
|
bitrate,
|
||||||
|
...getAudioEncoderConfigExtension(codec),
|
||||||
|
});
|
||||||
|
|
||||||
|
return support.supported === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const getEncodableCodecs = async (): Promise<MediaCodec[]> => {
|
||||||
|
const [videoCodecs, audioCodecs, subtitleCodecs] = await Promise.all([
|
||||||
|
getEncodableVideoCodecs(),
|
||||||
|
getEncodableAudioCodecs(),
|
||||||
|
getEncodableSubtitleCodecs(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [...videoCodecs, ...audioCodecs, ...subtitleCodecs];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const canEncodeSubtitles = async (codec: SubtitleCodec) => {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const getEncodableVideoCodecs = async (): Promise<VideoCodec[]> => {
|
||||||
|
const bools = await Promise.all(VIDEO_CODECS.map(canEncode));
|
||||||
|
return VIDEO_CODECS.filter((_, i) => bools[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const getEncodableAudioCodecs = async (): Promise<AudioCodec[]> => {
|
||||||
|
const bools = await Promise.all(AUDIO_CODECS.map(canEncode));
|
||||||
|
return AUDIO_CODECS.filter((_, i) => bools[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const getEncodableSubtitleCodecs = async (): Promise<SubtitleCodec[]> => {
|
||||||
|
const bools = await Promise.all(SUBTITLE_CODECS.map(canEncode));
|
||||||
|
return SUBTITLE_CODECS.filter((_, i) => bools[i]);
|
||||||
|
};
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ export {
|
|||||||
SUBTITLE_CODECS,
|
SUBTITLE_CODECS,
|
||||||
SubtitleCodec,
|
SubtitleCodec,
|
||||||
MediaCodec,
|
MediaCodec,
|
||||||
|
canEncode,
|
||||||
|
canEncodeVideo,
|
||||||
|
canEncodeAudio,
|
||||||
|
canEncodeSubtitles,
|
||||||
|
getEncodableCodecs,
|
||||||
|
getEncodableVideoCodecs,
|
||||||
|
getEncodableAudioCodecs,
|
||||||
|
getEncodableSubtitleCodecs,
|
||||||
} from './codec';
|
} from './codec';
|
||||||
export { Target, ArrayBufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions } from './target';
|
export { Target, ArrayBufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions } from './target';
|
||||||
export { Rotation, TransformationMatrix, AnyIterable } from './misc';
|
export { Rotation, TransformationMatrix, AnyIterable } from './misc';
|
||||||
|
|||||||
+2
-2
@@ -282,7 +282,7 @@ export abstract class BaseMediaFrameDrain<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await decoder.flush();
|
if (!terminated) await decoder.flush();
|
||||||
decoder.close();
|
decoder.close();
|
||||||
|
|
||||||
decoderIsFlushed = true;
|
decoderIsFlushed = true;
|
||||||
@@ -452,7 +452,7 @@ export abstract class BaseMediaFrameDrain<
|
|||||||
|
|
||||||
await chunks.return();
|
await chunks.return();
|
||||||
|
|
||||||
await decoder.flush();
|
if (!terminated) await decoder.flush();
|
||||||
decoder.close();
|
decoder.close();
|
||||||
|
|
||||||
if (!firstFrameQueued && lastFrame) {
|
if (!firstFrameQueued && lastFrame) {
|
||||||
|
|||||||
Reference in New Issue
Block a user