use shiguredo_webrtc::{
AudioCodecInfo, AudioCodecSpec, AudioDecoder, AudioDecoderFactory, AudioEncoder,
AudioEncoderFactory, AudioEncoderFactoryOptions, EnvironmentRef, SdpAudioFormatRef,
};
use crate::audio_codec_capability::{AudioCodecCapability, AudioCodecImplementation};
use crate::codec_direction::CodecDirection;
pub struct InternalAudioCodecCapability {
implementation: AudioCodecImplementation,
encoder_factory: AudioEncoderFactory,
decoder_factory: AudioDecoderFactory,
}
impl InternalAudioCodecCapability {
pub fn new() -> Self {
Self {
implementation: AudioCodecImplementation::new(
"internal",
"WebRTC built-in AudioCodecFactory",
),
encoder_factory: AudioEncoderFactory::builtin(),
decoder_factory: AudioDecoderFactory::builtin(),
}
}
}
impl Default for InternalAudioCodecCapability {
fn default() -> Self {
Self::new()
}
}
impl AudioCodecCapability for InternalAudioCodecCapability {
fn get_implementation(&self) -> AudioCodecImplementation {
self.implementation.clone()
}
fn get_supported_codec_specs(&self, direction: CodecDirection) -> Vec<AudioCodecSpec> {
match direction {
CodecDirection::Encoder => self.encoder_factory.get_supported_encoders(),
CodecDirection::Decoder => self.decoder_factory.get_supported_decoders(),
}
}
fn query(
&self,
direction: CodecDirection,
format: SdpAudioFormatRef<'_>,
) -> Option<AudioCodecInfo> {
match direction {
CodecDirection::Encoder => self.encoder_factory.query_audio_encoder(format),
CodecDirection::Decoder => {
let request = format.to_owned();
if !self.decoder_factory.is_supported_decoder(request.as_ref()) {
return None;
}
self.decoder_factory
.get_supported_decoders()
.into_iter()
.find(|spec| spec.format().matches(request.as_ref()))
.map(|spec| spec.info())
}
}
}
fn create_audio_encoder(
&self,
env: EnvironmentRef<'_>,
format: SdpAudioFormatRef<'_>,
options: &AudioEncoderFactoryOptions,
) -> Option<AudioEncoder> {
self.encoder_factory.create(env, format, options)
}
fn create_audio_decoder(
&self,
env: EnvironmentRef<'_>,
format: SdpAudioFormatRef<'_>,
) -> Option<AudioDecoder> {
self.decoder_factory.create(env, format)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec_direction::CodecDirection;
use shiguredo_webrtc::SdpAudioFormat;
#[test]
fn builtin_advertises_real_non_opus_spec_info() {
let capability = InternalAudioCodecCapability::new();
for direction in [CodecDirection::Encoder, CodecDirection::Decoder] {
let specs = capability.get_supported_codec_specs(direction);
for name in ["G722", "PCMU", "PCMA"] {
let spec = specs
.iter()
.find(|spec| spec.format().name().ok().as_deref() == Some(name))
.expect("builtin が広告するコーデックが見つかりません");
let info = spec.info();
assert_eq!(
info.default_bitrate_bps(),
64000,
"{name} の既定ビットレートが builtin 実値と一致しません"
);
assert_eq!(
info.min_bitrate_bps(),
64000,
"{name} の最小ビットレートが builtin 実値と一致しません"
);
assert_eq!(
info.max_bitrate_bps(),
64000,
"{name} の最大ビットレートが builtin 実値と一致しません"
);
}
}
}
#[test]
fn builtin_advertises_real_opus_spec_info() {
let capability = InternalAudioCodecCapability::new();
let enc = capability
.get_supported_codec_specs(CodecDirection::Encoder)
.into_iter()
.find(|spec| spec.format().name().ok().as_deref() == Some("opus"))
.expect("builtin が Opus エンコーダーを広告すること");
assert_eq!(enc.info().sample_rate_hz(), 48000);
assert_eq!(enc.info().num_channels(), 1);
assert_eq!(enc.info().default_bitrate_bps(), 32000);
let dec = capability
.get_supported_codec_specs(CodecDirection::Decoder)
.into_iter()
.find(|spec| spec.format().name().ok().as_deref() == Some("opus"))
.expect("builtin が Opus デコーダーを広告すること");
assert_eq!(dec.info().sample_rate_hz(), 48000);
assert_eq!(dec.info().num_channels(), 1);
assert_eq!(dec.info().default_bitrate_bps(), 64000);
}
#[test]
fn query_returns_none_for_unsupported_codec() {
let capability = InternalAudioCodecCapability::new();
for direction in [CodecDirection::Encoder, CodecDirection::Decoder] {
let format = SdpAudioFormat::new("not-a-codec", 48000, 1);
assert!(
capability.query(direction, format.as_ref()).is_none(),
"未対応コーデックは問い合わせできないはずです"
);
}
}
#[test]
fn query_rejects_incompatible_clockrate() {
let capability = InternalAudioCodecCapability::new();
for direction in [CodecDirection::Encoder, CodecDirection::Decoder] {
let incompatible = SdpAudioFormat::new("opus", 16000, 1);
assert!(
capability.query(direction, incompatible.as_ref()).is_none(),
"互換性のないクロックレートは問い合わせできないはずです"
);
let compatible = SdpAudioFormat::new("opus", 48000, 2);
assert!(
capability.query(direction, compatible.as_ref()).is_some(),
"相容れる Opus は問い合わせできるはずです"
);
}
}
#[test]
fn query_decoder_rejects_malformed_parameter() {
let capability = InternalAudioCodecCapability::new();
let mut malformed = SdpAudioFormat::new("opus", 48000, 2);
malformed.parameters_mut().set("stereo", "2");
assert!(
capability
.query(CodecDirection::Decoder, malformed.as_ref())
.is_none(),
"不正なパラメータを持つ Opus は受け付けないはずです"
);
}
#[test]
fn query_decoder_rejects_incompatible_channel_count() {
let capability = InternalAudioCodecCapability::new();
let incompatible = SdpAudioFormat::new("opus", 48000, 1);
assert!(
capability
.query(CodecDirection::Decoder, incompatible.as_ref())
.is_none(),
"チャネル数が不一致の Opus は受け付けないはずです"
);
}
}