use std::{
fmt::{self, Display, Formatter},
str::FromStr,
};
use str_reader::StringReader;
use crate::{
attribute::{Attribute, Attributes},
bandwidth::Bandwidth,
connection::ConnectionInfo,
key::EncryptionKey,
parser::{FromSessionDescriptionLines, SessionDescriptionLines},
ParseError,
};
#[derive(Clone)]
pub struct MediaDescriptionBuilder {
inner: MediaDescription,
}
impl MediaDescriptionBuilder {
const fn new(media_type: String, port: u16, protocol: String) -> Self {
let inner = MediaDescription {
media_type,
port,
port_count: None,
protocol,
formats: Vec::new(),
title: None,
bandwidth: Vec::new(),
key: None,
connection: Vec::new(),
attributes: Attributes::new(),
};
Self { inner }
}
#[inline]
pub fn port_count(&mut self, port_count: u16) -> &mut Self {
self.inner.port_count = Some(port_count);
self
}
#[inline]
pub fn format<T>(&mut self, fmt: T) -> &mut Self
where
T: ToString,
{
self.inner.formats.push(fmt.to_string());
self
}
#[inline]
pub fn title<T>(&mut self, title: T) -> &mut Self
where
T: ToString,
{
self.inner.title = Some(title.to_string());
self
}
#[inline]
pub fn bandwidth(&mut self, bandwidth: Bandwidth) -> &mut Self {
self.inner.bandwidth.push(bandwidth);
self
}
#[inline]
pub fn key(&mut self, key: EncryptionKey) -> &mut Self {
self.inner.key = Some(key);
self
}
#[inline]
pub fn connection(&mut self, connection: ConnectionInfo) -> &mut Self {
self.inner.connection.push(connection);
self
}
#[inline]
pub fn flag<T>(&mut self, name: T) -> &mut Self
where
T: ToString,
{
self.inner.attributes.push(Attribute::new_flag(name));
self
}
#[inline]
pub fn attribute<T, U>(&mut self, name: T, value: U) -> &mut Self
where
T: ToString,
U: ToString,
{
self.inner
.attributes
.push(Attribute::new_attribute(name, value));
self
}
#[inline]
pub fn build(self) -> MediaDescription {
self.inner
}
}
pub type MediaFormat = String;
#[derive(Clone)]
pub struct MediaDescription {
media_type: String,
port: u16,
port_count: Option<u16>,
protocol: String,
formats: Vec<MediaFormat>,
title: Option<String>,
bandwidth: Vec<Bandwidth>,
key: Option<EncryptionKey>,
connection: Vec<ConnectionInfo>,
attributes: Attributes,
}
impl MediaDescription {
const fn new() -> Self {
Self {
media_type: String::new(),
port: 0,
port_count: None,
protocol: String::new(),
formats: Vec::new(),
title: None,
bandwidth: Vec::new(),
key: None,
connection: Vec::new(),
attributes: Attributes::new(),
}
}
#[inline]
pub fn builder<T, U>(media_type: T, port: u16, protocol: U) -> MediaDescriptionBuilder
where
T: ToString,
U: ToString,
{
MediaDescriptionBuilder::new(media_type.to_string(), port, protocol.to_string())
}
#[inline]
pub fn media_type(&self) -> &str {
&self.media_type
}
#[inline]
pub fn port(&self) -> u16 {
self.port
}
#[inline]
pub fn port_count(&self) -> Option<u16> {
self.port_count
}
#[inline]
pub fn protocol(&self) -> &str {
&self.protocol
}
#[inline]
pub fn formats(&self) -> &[MediaFormat] {
&self.formats
}
#[inline]
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
}
#[inline]
pub fn bandwidth(&self) -> &[Bandwidth] {
&self.bandwidth
}
#[inline]
pub fn encryption_key(&self) -> Option<&EncryptionKey> {
self.key.as_ref()
}
#[inline]
pub fn connection(&self) -> &[ConnectionInfo] {
&self.connection
}
#[inline]
pub fn attributes(&self) -> &Attributes {
&self.attributes
}
}
impl Display for MediaDescription {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "m={} {}", self.media_type, self.port)?;
if let Some(count) = self.port_count {
write!(f, "/{count}")?;
}
write!(f, " {}", self.protocol)?;
for fmt in &self.formats {
write!(f, " {fmt}")?;
}
f.write_str("\r\n")?;
if let Some(title) = self.title.as_ref() {
write!(f, "i={title}\r\n")?;
}
for connection in &self.connection {
write!(f, "c={connection}\r\n")?;
}
for bw in &self.bandwidth {
write!(f, "b={bw}\r\n")?;
}
if let Some(k) = self.key.as_ref() {
write!(f, "k={k}\r\n")?;
}
for attr in self.attributes.iter() {
write!(f, "a={attr}\r\n")?;
}
Ok(())
}
}
impl FromSessionDescriptionLines for MediaDescription {
fn from_sdp_lines(lines: &mut SessionDescriptionLines) -> Result<Self, ParseError> {
let (t, v) = lines.current().unwrap();
debug_assert_eq!(t, 'm');
let mut mdp = MediaDescription::new();
let mut reader = StringReader::new(v);
mdp.media_type = String::from(reader.read_word());
mdp.port = reader.read_u16()?;
reader.skip_whitespace();
if reader.current_char() == Some('/') {
reader.skip_char();
mdp.port_count = Some(reader.read_u16()?);
}
mdp.protocol = String::from(reader.read_word());
loop {
reader.skip_whitespace();
if reader.is_empty() {
break;
}
mdp.formats.push(String::from(reader.read_word()));
}
lines.next()?;
while let Some((t, _)) = lines.current() {
match t {
'm' => break,
'i' => mdp.title = Some(lines.parse()?),
'c' => mdp.connection.push(lines.parse()?),
'b' => mdp.bandwidth.push(lines.parse()?),
'k' => mdp.key = Some(lines.parse()?),
'a' => mdp.attributes.push(lines.parse()?),
_ => {
return Err(ParseError::with_msg(format!(
"unknown media description field: {t}",
)))
}
}
}
Ok(mdp)
}
}
impl FromStr for MediaDescription {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lines = SessionDescriptionLines::new(s)?;
MediaDescription::from_sdp_lines(&mut lines)
}
}