mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-29 03:43:50 +02:00
Add more options to Video/AudioEncodingConfig, restructure code (#61)
This commit is contained in:
-269
@@ -12,7 +12,6 @@ import {
|
||||
HevcDecoderConfigurationRecord,
|
||||
Vp9CodecInfo,
|
||||
} from './codec-data';
|
||||
import { customAudioEncoders, customVideoEncoders } from './custom-coder';
|
||||
import {
|
||||
Bitstream,
|
||||
COLOR_PRIMARIES_MAP,
|
||||
@@ -1124,271 +1123,3 @@ export const validateSubtitleMetadata = (metadata: SubtitleMetadata | undefined)
|
||||
throw new TypeError('Subtitle metadata config description must be a string.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser is able to encode the given codec.
|
||||
* @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 TypeError(`Unknown codec '${codec}'.`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser is able to encode the given video codec with the given parameters.
|
||||
* @public
|
||||
*/
|
||||
export const canEncodeVideo = async (codec: VideoCodec, { width = 1280, height = 720, bitrate = 1e6 }: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
bitrate?: number | Quality;
|
||||
} = {}) => {
|
||||
if (!VIDEO_CODECS.includes(codec)) {
|
||||
return false;
|
||||
}
|
||||
if (!Number.isInteger(width) || width <= 0) {
|
||||
throw new TypeError('width must be a positive integer.');
|
||||
}
|
||||
if (!Number.isInteger(height) || height <= 0) {
|
||||
throw new TypeError('height must be a positive integer.');
|
||||
}
|
||||
if (!(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {
|
||||
throw new TypeError('bitrate must be a positive integer or a quality.');
|
||||
}
|
||||
|
||||
const resolvedBitrate = bitrate instanceof Quality
|
||||
? bitrate._toVideoBitrate(codec, width, height)
|
||||
: bitrate;
|
||||
|
||||
if (customVideoEncoders.length > 0) {
|
||||
const encoderConfig: VideoEncoderConfig = {
|
||||
codec: buildVideoCodecString(
|
||||
codec,
|
||||
width,
|
||||
height,
|
||||
resolvedBitrate,
|
||||
),
|
||||
width,
|
||||
height,
|
||||
bitrate: resolvedBitrate,
|
||||
...getVideoEncoderConfigExtension(codec),
|
||||
};
|
||||
|
||||
if (customVideoEncoders.some(x => x.supports(codec, encoderConfig))) {
|
||||
// There's a custom encoder
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof VideoEncoder === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const support = await VideoEncoder.isConfigSupported({
|
||||
codec: buildVideoCodecString(codec, width, height, resolvedBitrate),
|
||||
width,
|
||||
height,
|
||||
bitrate: resolvedBitrate,
|
||||
...getVideoEncoderConfigExtension(codec),
|
||||
});
|
||||
|
||||
return support.supported === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser is able to encode the given audio codec with the given parameters.
|
||||
* @public
|
||||
*/
|
||||
export const canEncodeAudio = async (codec: AudioCodec, { numberOfChannels = 2, sampleRate = 48000, bitrate = 128e3 }: {
|
||||
numberOfChannels?: number;
|
||||
sampleRate?: number;
|
||||
bitrate?: number | Quality;
|
||||
} = {}) => {
|
||||
if (!AUDIO_CODECS.includes(codec)) {
|
||||
return false;
|
||||
}
|
||||
if (!Number.isInteger(numberOfChannels) || numberOfChannels <= 0) {
|
||||
throw new TypeError('numberOfChannels must be a positive integer.');
|
||||
}
|
||||
if (!Number.isInteger(sampleRate) || sampleRate <= 0) {
|
||||
throw new TypeError('sampleRate must be a positive integer.');
|
||||
}
|
||||
if (!(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {
|
||||
throw new TypeError('bitrate must be a positive integer.');
|
||||
}
|
||||
|
||||
const resolvedBitrate = bitrate instanceof Quality
|
||||
? bitrate._toAudioBitrate(codec)
|
||||
: bitrate;
|
||||
|
||||
if (customAudioEncoders.length > 0) {
|
||||
const encoderConfig: AudioEncoderConfig = {
|
||||
codec: buildAudioCodecString(
|
||||
codec,
|
||||
numberOfChannels,
|
||||
sampleRate,
|
||||
),
|
||||
numberOfChannels,
|
||||
sampleRate,
|
||||
bitrate: resolvedBitrate,
|
||||
...getAudioEncoderConfigExtension(codec),
|
||||
};
|
||||
|
||||
if (customAudioEncoders.some(x => x.supports(codec, encoderConfig))) {
|
||||
// There's a custom encoder
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((PCM_AUDIO_CODECS as readonly string[]).includes(codec)) {
|
||||
return true; // Because we encode these ourselves
|
||||
}
|
||||
|
||||
if (typeof AudioEncoder === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const support = await AudioEncoder.isConfigSupported({
|
||||
codec: buildAudioCodecString(codec, numberOfChannels, sampleRate),
|
||||
numberOfChannels,
|
||||
sampleRate,
|
||||
bitrate: resolvedBitrate,
|
||||
...getAudioEncoderConfigExtension(codec),
|
||||
});
|
||||
|
||||
return support.supported === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser is able to encode the given subtitle codec.
|
||||
* @public
|
||||
*/
|
||||
export const canEncodeSubtitles = async (codec: SubtitleCodec) => {
|
||||
if (!SUBTITLE_CODECS.includes(codec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the list of all media codecs that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getEncodableCodecs = async (): Promise<MediaCodec[]> => {
|
||||
const [videoCodecs, audioCodecs, subtitleCodecs] = await Promise.all([
|
||||
getEncodableVideoCodecs(),
|
||||
getEncodableAudioCodecs(),
|
||||
getEncodableSubtitleCodecs(),
|
||||
]);
|
||||
|
||||
return [...videoCodecs, ...audioCodecs, ...subtitleCodecs];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the list of all video codecs that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getEncodableVideoCodecs = async (
|
||||
checkedCodecs = VIDEO_CODECS as unknown as VideoCodec[],
|
||||
options?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
bitrate?: number | Quality;
|
||||
},
|
||||
): Promise<VideoCodec[]> => {
|
||||
const bools = await Promise.all(checkedCodecs.map(codec => canEncodeVideo(codec, options)));
|
||||
return checkedCodecs.filter((_, i) => bools[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the list of all audio codecs that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getEncodableAudioCodecs = async (
|
||||
checkedCodecs = AUDIO_CODECS as unknown as AudioCodec[],
|
||||
options?: {
|
||||
numberOfChannels?: number;
|
||||
sampleRate?: number;
|
||||
bitrate?: number | Quality;
|
||||
},
|
||||
): Promise<AudioCodec[]> => {
|
||||
const bools = await Promise.all(checkedCodecs.map(codec => canEncodeAudio(codec, options)));
|
||||
return checkedCodecs.filter((_, i) => bools[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the list of all subtitle codecs that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getEncodableSubtitleCodecs = async (
|
||||
checkedCodecs = SUBTITLE_CODECS as unknown as SubtitleCodec[],
|
||||
): Promise<SubtitleCodec[]> => {
|
||||
const bools = await Promise.all(checkedCodecs.map(canEncodeSubtitles));
|
||||
return checkedCodecs.filter((_, i) => bools[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the first video codec from the given list that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getFirstEncodableVideoCodec = async (
|
||||
checkedCodecs: VideoCodec[],
|
||||
options?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
bitrate?: number | Quality;
|
||||
},
|
||||
): Promise<VideoCodec | null> => {
|
||||
for (const codec of checkedCodecs) {
|
||||
if (await canEncodeVideo(codec, options)) {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the first audio codec from the given list that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getFirstEncodableAudioCodec = async (
|
||||
checkedCodecs: AudioCodec[],
|
||||
options?: {
|
||||
numberOfChannels?: number;
|
||||
sampleRate?: number;
|
||||
bitrate?: number | Quality;
|
||||
},
|
||||
): Promise<AudioCodec | null> => {
|
||||
for (const codec of checkedCodecs) {
|
||||
if (await canEncodeAudio(codec, options)) {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the first subtitle codec from the given list that can be encoded by the browser.
|
||||
* @public
|
||||
*/
|
||||
export const getFirstEncodableSubtitleCodec = async (
|
||||
checkedCodecs: SubtitleCodec[],
|
||||
): Promise<SubtitleCodec | null> => {
|
||||
for (const codec of checkedCodecs) {
|
||||
if (await canEncodeSubtitles(codec)) {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user