use crate::sdp::{codec::SdpCodec, codec_string::CodecStringEntry, error::CodecStringError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CodecStringOptions {
emit_rate: bool,
emit_ptime: bool,
emit_bitrate: bool,
emit_channels: bool,
emit_fmtp: bool,
}
impl Default for CodecStringOptions {
fn default() -> Self {
Self {
emit_rate: true,
emit_ptime: true,
emit_bitrate: true,
emit_channels: true,
emit_fmtp: false,
}
}
}
impl CodecStringOptions {
pub fn audio() -> Self {
Self::default()
}
pub fn video() -> Self {
Self {
emit_rate: false,
emit_ptime: false,
emit_bitrate: false,
emit_channels: false,
emit_fmtp: true,
}
}
pub fn with_rate(mut self, emit: bool) -> Self {
self.emit_rate = emit;
self
}
pub fn with_ptime(mut self, emit: bool) -> Self {
self.emit_ptime = emit;
self
}
pub fn with_bitrate(mut self, emit: bool) -> Self {
self.emit_bitrate = emit;
self
}
pub fn with_channels(mut self, emit: bool) -> Self {
self.emit_channels = emit;
self
}
pub fn with_fmtp(mut self, emit: bool) -> Self {
self.emit_fmtp = emit;
self
}
pub fn emits_rate(&self) -> bool {
self.emit_rate
}
pub fn emits_ptime(&self) -> bool {
self.emit_ptime
}
pub fn emits_bitrate(&self) -> bool {
self.emit_bitrate
}
pub fn emits_channels(&self) -> bool {
self.emit_channels
}
pub fn emits_fmtp(&self) -> bool {
self.emit_fmtp
}
pub fn sdp_codec_to_entry(
&self,
codec: &SdpCodec,
) -> Result<CodecStringEntry, CodecStringError> {
let mut entry = CodecStringEntry::new(codec.name())?;
if self.emit_fmtp {
if let Some(fmtp) = codec.fmtp() {
entry = entry.with_fmtp(fmtp)?;
}
}
Ok(self.apply_qualifiers(entry, codec))
}
pub(crate) fn apply_qualifiers(
&self,
mut entry: CodecStringEntry,
codec: &SdpCodec,
) -> CodecStringEntry {
if self.emit_rate {
entry = entry.with_rate(codec.clock_rate());
}
if self.emit_ptime {
if let Some(p) = codec.ptime() {
entry = entry.with_ptime(p);
}
}
if self.emit_bitrate {
if let Some(b) = codec.bitrate() {
entry = entry.with_bitrate(b);
}
}
if self.emit_channels {
if let Some(c) = codec.channels() {
entry = entry.with_channels(u32::from(c));
}
}
entry
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sdp::codec::{SdpDirection, SdpMediaType};
fn make_audio_codec(name: &str, rate: u32, ptime: u32) -> SdpCodec {
SdpCodec::new(
SdpMediaType::Audio,
0,
name,
rate,
Some(1),
None,
Some(ptime),
None,
Some(64000),
SdpDirection::SendRecv,
false,
)
}
fn make_video_codec(name: &str) -> SdpCodec {
SdpCodec::new(
SdpMediaType::Video,
99,
name,
90000,
None,
Some("profile-level-id=42e01f".to_string()),
None,
None,
None,
SdpDirection::SendRecv,
true,
)
}
#[test]
fn audio_defaults_emit_rate_ptime_bitrate_channels_not_fmtp() {
let opts = CodecStringOptions::audio();
assert!(opts.emits_rate());
assert!(opts.emits_ptime());
assert!(opts.emits_bitrate());
assert!(opts.emits_channels());
assert!(!opts.emits_fmtp(), "audio fmtp must be off by default");
}
#[test]
fn video_defaults_emit_only_fmtp() {
let opts = CodecStringOptions::video();
assert!(!opts.emits_rate());
assert!(!opts.emits_ptime());
assert!(!opts.emits_bitrate());
assert!(!opts.emits_channels());
assert!(opts.emits_fmtp());
}
#[test]
fn audio_entry_has_rate_ptime_bitrate_no_fmtp() {
let codec = make_audio_codec("PCMU", 8000, 20);
let entry = CodecStringOptions::audio()
.sdp_codec_to_entry(&codec)
.unwrap();
assert_eq!(entry.name(), "PCMU");
assert_eq!(entry.rate(), Some(8000));
assert_eq!(entry.ptime(), Some(20));
assert_eq!(entry.bitrate(), Some(64000));
assert_eq!(entry.channels(), Some(1));
assert!(
entry
.fmtp()
.is_none(),
"audio fmtp must not be emitted by default"
);
}
#[test]
fn video_entry_emits_no_qualifiers() {
let codec = make_video_codec("H264");
let entry = CodecStringOptions::video()
.sdp_codec_to_entry(&codec)
.unwrap();
assert_eq!(entry.name(), "H264");
assert!(
entry
.rate()
.is_none(),
"video must not emit rate"
);
assert!(
entry
.ptime()
.is_none(),
"video must not emit ptime"
);
assert!(
entry
.bitrate()
.is_none(),
"video must not emit bitrate"
);
assert!(
entry
.channels()
.is_none(),
"video must not emit channels"
);
assert_eq!(
entry.fmtp(),
Some("profile-level-id=42e01f"),
"video must emit fmtp"
);
}
#[test]
fn codec_string_options_clone_and_eq() {
let a = CodecStringOptions::audio();
let b = a.clone();
assert_eq!(a, b);
let c = CodecStringOptions::video();
assert_ne!(a, c);
}
}