Add codec-specific encoder configs

This commit is contained in:
Vanilagy
2024-11-30 19:30:43 +01:00
parent 25c271e4af
commit 3ae891842f
8 changed files with 141 additions and 23 deletions
+39 -3
View File
@@ -181,6 +181,42 @@ export const buildAudioCodecString = (codec: AudioCodec, numberOfChannels: numbe
throw new TypeError(`Unhandled codec '${codec}'.`);
};
export const getVideoEncoderConfigExtension = (codec: VideoCodec) => {
if (codec === 'avc') {
return {
avc: {
format: 'avc' as const // Ensure the format is not Annex B
}
};
} else if (codec === 'hevc') {
return {
hevc: {
format: 'hevc' as const // Ensure the format is not Annex B
}
};
}
return {};
};
export const getAudioEncoderConfigExtension = (codec: AudioCodec) => {
if (codec === 'aac') {
return {
aac: {
format: 'aac' as const // Ensure the format is not ADTS
}
};
} else if (codec === 'opus') {
return {
opus: {
format: 'opus' as const
}
};
}
return {};
};
export const validateVideoChunkMetadata = (metadata: EncodedVideoChunkMetadata | undefined) => {
if (!metadata) {
throw new TypeError("Video chunk metadata must be provided.");
@@ -236,12 +272,9 @@ export const validateVideoChunkMetadata = (metadata: EncodedVideoChunkMetadata |
}
if ((metadata.decoderConfig.codec.startsWith('avc1') || metadata.decoderConfig.codec.startsWith('avc3')) && !metadata.decoderConfig.description) {
// TODO Technically not correct, they can also lack a description in which case the chunks are annexb. Generally,
// go through all the codec registrations and catch these edge cases.
throw new TypeError("Video chunk metadata decoder configuration for AVC must include a description, which is expected to be an AVCDecoderConfigurationRecord as specified in ISO 14496-15.");
}
if ((metadata.decoderConfig.codec.startsWith('hev1') || metadata.decoderConfig.codec.startsWith('hvc1')) && !metadata.decoderConfig.description) {
// TODO Same as above
throw new TypeError("Video chunk metadata decoder configuration for HEVC must include a description, which is expected to be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15.");
}
if ((metadata.decoderConfig.codec === 'vp8' || metadata.decoderConfig.codec.startsWith('vp09')) && metadata.decoderConfig.colorSpace === undefined) {
@@ -278,6 +311,9 @@ export const validateAudioChunkMetadata = (metadata: EncodedAudioChunkMetadata |
}
}
if (metadata.decoderConfig.codec.startsWith('mp4a') && !metadata.decoderConfig.description) {
throw new TypeError("Audio chunk metadata decoder configuration for AAC must include a description, which is expected to be an AudioSpecificConfig as specified in ISO 14496-3.");
}
if (metadata.decoderConfig.codec === 'opus' && metadata.decoderConfig.description && metadata.decoderConfig.description.byteLength < 18) {
throw new TypeError('Invalid decoder description provided for Opus; must be at least 18 bytes long.');
}
+1 -1
View File
@@ -696,7 +696,7 @@ export const dOps = (trackData: IsobmffAudioTrackData) => {
// https://www.rfc-editor.org/rfc/rfc7845#section-5
const description = trackData.info.decoderConfig?.description;
if (description) {
assert(description.byteLength < 18); // Is validated in an earlier step
assert(description.byteLength >= 18); // Is validated in an earlier step
const view = ArrayBuffer.isView(description)
? new DataView(description.buffer, description.byteOffset, description.byteLength)
+3 -1
View File
@@ -1,4 +1,4 @@
import { buildAudioCodecString, buildVideoCodecString } from "./codec";
import { buildAudioCodecString, buildVideoCodecString, getAudioEncoderConfigExtension, getVideoEncoderConfigExtension } from "./codec";
import { assert } from "./misc";
import { Muxer } from "./muxer";
import { OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from "./output";
@@ -194,6 +194,7 @@ class VideoEncoderWrapper {
bitrate: this.codecConfig.bitrate,
framerate: this.source._connectedTrack?.metadata.frameRate,
latencyMode: this.codecConfig.latencyMode,
...getVideoEncoderConfigExtension(this.codecConfig.codec)
});
assert(this.source._connectedTrack);
@@ -438,6 +439,7 @@ class AudioEncoderWrapper {
numberOfChannels: audioData.numberOfChannels,
sampleRate: audioData.sampleRate,
bitrate: this.codecConfig.bitrate,
...getAudioEncoderConfigExtension(this.codecConfig.codec)
});
assert(this.source._connectedTrack);