/// Typed views over common RFC 8866 and RTP attributes.
///|
/// Parses an `a=rtpmap: /[/]` attribute.
pub fn parse_rtpmap(item : Attribute) -> Result[RtpMap, SdpError] {
if !item.name().equal_ignore_ascii_case("rtpmap") {
return Err(
sdp_error(
"attribute",
"wrong-kind",
item.line(),
3,
"rtpmap",
"attribute is not rtpmap",
),
)
}
let value = match item.value() {
None =>
return Err(
sdp_error(
"attribute",
"missing-value",
item.line(),
3,
"rtpmap",
"rtpmap requires a value",
),
)
Some(value) => value
}
match value.split_once(" ") {
None =>
Err(
sdp_error(
"attribute",
"invalid-rtpmap",
item.line(),
3,
"rtpmap",
"rtpmap requires payload and encoding",
),
)
Some(pair) => {
let payload = match parse_non_negative_int(pair.0.to_owned()) {
None =>
return Err(
sdp_error(
"attribute",
"invalid-payload",
item.line(),
3,
"rtpmap",
"payload type must be an integer",
),
)
Some(value) => value
}
let encoding = pair.1.split("/").to_array()
if encoding.length() < 2 ||
encoding.length() > 3 ||
encoding[0].length() == 0 {
return Err(
sdp_error(
"attribute",
"invalid-encoding",
item.line(),
3,
"rtpmap",
"encoding must be name/clock[/parameters]",
),
)
}
let clock = match parse_non_negative_int(encoding[1].to_owned()) {
None =>
return Err(
sdp_error(
"attribute",
"invalid-clock",
item.line(),
3,
"rtpmap",
"clock rate must be an integer",
),
)
Some(value) => value
}
let parameters = if encoding.length() == 3 {
Some(encoding[2].to_owned())
} else {
None
}
Ok(rtp_map(payload, encoding[0].to_owned(), clock, parameters))
}
}
}
///|
/// Parses an `a=fmtp: ` attribute.
pub fn parse_fmtp(item : Attribute) -> Result[FormatParameters, SdpError] {
if !item.name().equal_ignore_ascii_case("fmtp") {
return Err(
sdp_error(
"attribute",
"wrong-kind",
item.line(),
3,
"fmtp",
"attribute is not fmtp",
),
)
}
let value = match item.value() {
None =>
return Err(
sdp_error(
"attribute",
"missing-value",
item.line(),
3,
"fmtp",
"fmtp requires a value",
),
)
Some(value) => value
}
match value.split_once(" ") {
None =>
Err(
sdp_error(
"attribute",
"invalid-fmtp",
item.line(),
3,
"fmtp",
"fmtp requires format and parameters",
),
)
Some(pair) =>
if pair.0.length() == 0 || pair.1.length() == 0 {
Err(
sdp_error(
"attribute",
"invalid-fmtp",
item.line(),
3,
"fmtp",
"fmtp fields must not be empty",
),
)
} else {
Ok(format_parameters(pair.0.to_owned(), pair.1.to_owned()))
}
}
}
///|
/// Returns every successfully parsed RTP mapping in media order.
pub fn MediaDescription::rtp_maps(self : MediaDescription) -> Array[RtpMap] {
let out : Array[RtpMap] = Array::new()
for item in self.attributes() {
if item.name().equal_ignore_ascii_case("rtpmap") {
match parse_rtpmap(item) {
Ok(mapping) => out.push(mapping)
Err(_) => ()
}
}
}
out
}
///|
/// Returns every successfully parsed format-parameter mapping.
pub fn MediaDescription::format_parameters(
self : MediaDescription,
) -> Array[FormatParameters] {
let out : Array[FormatParameters] = Array::new()
for item in self.attributes() {
if item.name().equal_ignore_ascii_case("fmtp") {
match parse_fmtp(item) {
Ok(mapping) => out.push(mapping)
Err(_) => ()
}
}
}
out
}
///|
/// Finds an RTP mapping by numeric payload type.
pub fn MediaDescription::rtp_map_for(
self : MediaDescription,
payload_type : Int,
) -> RtpMap? {
for mapping in self.rtp_maps() {
if mapping.payload_type() == payload_type {
return Some(mapping)
}
}
None
}
///|
/// Parses an attribute whose value starts with a numeric payload identifier.
pub fn attribute_payload(item : Attribute) -> Int? {
match item.value() {
None => None
Some(value) => {
let head = match value.split_once(" ") {
None => value
Some(pair) => pair.0.to_owned()
}
parse_non_negative_int(head)
}
}
}
///|
/// Whether a media section advertises a format token on its `m=` line.
pub fn MediaDescription::has_format(
self : MediaDescription,
format : String,
) -> Bool {
for item in self.formats() {
if item == format {
return true
}
}
false
}
///|
/// Returns the effective connection, applying the RFC 8866 session default.
pub fn effective_connection(
document : SessionDescription,
media : MediaDescription,
) -> Connection? {
if media.connections().length() > 0 {
Some(media.connections()[0])
} else {
document.connection()
}
}
///|
/// Returns the effective value of a single-valued attribute.
pub fn effective_attribute(
document : SessionDescription,
media : MediaDescription,
name : String,
) -> String? {
match media.attribute_value(name) {
Some(value) => Some(value)
None => {
for item in document.attributes() {
if item.name().equal_ignore_ascii_case(name) {
return item.value()
}
}
None
}
}
}