#[cfg(test)]
mod tests;
use crate::scalability_modes::ScalabilityMode;
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeStruct;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt;
use std::iter::FromIterator;
use std::num::{NonZeroU32, NonZeroU8};
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
pub struct RtpCodecParametersParameters(
BTreeMap<Cow<'static, str>, RtpCodecParametersParametersValue>,
);
impl RtpCodecParametersParameters {
pub fn insert<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: Into<Cow<'static, str>>,
V: Into<RtpCodecParametersParametersValue>,
{
self.0.insert(key.into(), value.into());
self
}
pub fn iter(
&self,
) -> std::collections::btree_map::Iter<'_, Cow<'static, str>, RtpCodecParametersParametersValue>
{
self.0.iter()
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&RtpCodecParametersParametersValue> {
self.0.get(key)
}
}
impl<K, const N: usize> From<[(K, RtpCodecParametersParametersValue); N]>
for RtpCodecParametersParameters
where
K: Into<Cow<'static, str>>,
{
fn from(array: [(K, RtpCodecParametersParametersValue); N]) -> Self {
IntoIterator::into_iter(array).collect()
}
}
impl IntoIterator for RtpCodecParametersParameters {
type Item = (Cow<'static, str>, RtpCodecParametersParametersValue);
type IntoIter =
std::collections::btree_map::IntoIter<Cow<'static, str>, RtpCodecParametersParametersValue>;
fn into_iter(
self,
) -> std::collections::btree_map::IntoIter<Cow<'static, str>, RtpCodecParametersParametersValue>
{
self.0.into_iter()
}
}
impl<K> Extend<(K, RtpCodecParametersParametersValue)> for RtpCodecParametersParameters
where
K: Into<Cow<'static, str>>,
{
fn extend<T: IntoIterator<Item = (K, RtpCodecParametersParametersValue)>>(&mut self, iter: T) {
iter.into_iter().for_each(|(k, v)| {
self.insert(k, v);
});
}
}
impl<K> FromIterator<(K, RtpCodecParametersParametersValue)> for RtpCodecParametersParameters
where
K: Into<Cow<'static, str>>,
{
fn from_iter<T: IntoIterator<Item = (K, RtpCodecParametersParametersValue)>>(iter: T) -> Self {
Self(iter.into_iter().map(|(k, v)| (k.into(), v)).collect())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum RtpCodecCapabilityFinalized {
#[serde(rename_all = "camelCase")]
Audio {
mime_type: MimeTypeAudio,
preferred_payload_type: u8,
clock_rate: NonZeroU32,
channels: NonZeroU8,
parameters: RtpCodecParametersParameters,
rtcp_feedback: Vec<RtcpFeedback>,
},
#[serde(rename_all = "camelCase")]
Video {
mime_type: MimeTypeVideo,
preferred_payload_type: u8,
clock_rate: NonZeroU32,
parameters: RtpCodecParametersParameters,
rtcp_feedback: Vec<RtcpFeedback>,
},
}
impl RtpCodecCapabilityFinalized {
pub fn is_rtx(&self) -> bool {
match self {
Self::Audio { mime_type, .. } => mime_type == &MimeTypeAudio::Rtx,
Self::Video { mime_type, .. } => mime_type == &MimeTypeVideo::Rtx,
}
}
pub fn clock_rate(&self) -> NonZeroU32 {
let (Self::Audio { clock_rate, .. } | Self::Video { clock_rate, .. }) = self;
*clock_rate
}
pub fn parameters(&self) -> &RtpCodecParametersParameters {
let (Self::Audio { parameters, .. } | Self::Video { parameters, .. }) = self;
parameters
}
pub fn parameters_mut(&mut self) -> &mut RtpCodecParametersParameters {
let (Self::Audio { parameters, .. } | Self::Video { parameters, .. }) = self;
parameters
}
pub fn preferred_payload_type(&self) -> u8 {
match self {
Self::Audio {
preferred_payload_type,
..
}
| Self::Video {
preferred_payload_type,
..
} => *preferred_payload_type,
}
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpCapabilitiesFinalized {
pub codecs: Vec<RtpCodecCapabilityFinalized>,
pub header_extensions: Vec<RtpHeaderExtension>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum MediaKind {
Audio,
Video,
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum ParseMimeTypeError {
#[error("Invalid MIME type input string")]
InvalidInput,
#[error("Unknown MIME type")]
UnknownMimeType,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MimeType {
Audio(MimeTypeAudio),
Video(MimeTypeVideo),
}
impl FromStr for MimeType {
type Err = ParseMimeTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("audio/") {
MimeTypeAudio::from_str(s).map(Self::Audio)
} else if s.starts_with("video/") {
MimeTypeVideo::from_str(s).map(Self::Video)
} else {
Err(ParseMimeTypeError::InvalidInput)
}
}
}
impl MimeType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Audio(mime_type) => mime_type.as_str(),
Self::Video(mime_type) => mime_type.as_str(),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
pub enum MimeTypeAudio {
#[serde(rename = "audio/opus")]
Opus,
#[serde(rename = "audio/multiopus")]
MultiChannelOpus,
#[serde(rename = "audio/PCMU")]
Pcmu,
#[serde(rename = "audio/PCMA")]
Pcma,
#[serde(rename = "audio/ISAC")]
Isac,
#[serde(rename = "audio/G722")]
G722,
#[serde(rename = "audio/iLBC")]
Ilbc,
#[serde(rename = "audio/SILK")]
Silk,
#[serde(rename = "audio/CN")]
Cn,
#[serde(rename = "audio/telephone-event")]
TelephoneEvent,
#[serde(rename = "audio/rtx")]
Rtx,
#[serde(rename = "audio/red")]
Red,
}
impl FromStr for MimeTypeAudio {
type Err = ParseMimeTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"audio/opus" => Ok(Self::Opus),
"audio/multiopus" => Ok(Self::MultiChannelOpus),
"audio/pcmu" => Ok(Self::Pcmu),
"audio/pcma" => Ok(Self::Pcma),
"audio/isac" => Ok(Self::Isac),
"audio/g722" => Ok(Self::G722),
"audio/ilbc" => Ok(Self::Ilbc),
"audio/silk" => Ok(Self::Silk),
"audio/cn" => Ok(Self::Cn),
"audio/telephone-event" => Ok(Self::TelephoneEvent),
"audio/rtx" => Ok(Self::Rtx),
"audio/red" => Ok(Self::Red),
s => Err(if s.starts_with("audio/") {
ParseMimeTypeError::UnknownMimeType
} else {
ParseMimeTypeError::InvalidInput
}),
}
}
}
impl<'de> Deserialize<'de> for MimeTypeAudio {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
MimeTypeAudio::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl MimeTypeAudio {
pub fn as_str(&self) -> &'static str {
match self {
Self::Opus => "audio/opus",
Self::MultiChannelOpus => "audio/multiopus",
Self::Pcmu => "audio/PCMU",
Self::Pcma => "audio/PCMA",
Self::Isac => "audio/ISAC",
Self::G722 => "audio/G722",
Self::Ilbc => "audio/iLBC",
Self::Silk => "audio/SILK",
Self::Cn => "audio/CN",
Self::TelephoneEvent => "audio/telephone-event",
Self::Rtx => "audio/rtx",
Self::Red => "audio/red",
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
pub enum MimeTypeVideo {
#[serde(rename = "video/VP8")]
Vp8,
#[serde(rename = "video/VP9")]
Vp9,
#[serde(rename = "video/H264")]
H264,
#[serde(rename = "video/AV1")]
AV1,
#[serde(rename = "video/rtx")]
Rtx,
#[serde(rename = "video/red")]
Red,
#[serde(rename = "video/ulpfec")]
Ulpfec,
}
impl FromStr for MimeTypeVideo {
type Err = ParseMimeTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"video/vp8" => Ok(Self::Vp8),
"video/vp9" => Ok(Self::Vp9),
"video/h264" => Ok(Self::H264),
"video/av1" => Ok(Self::AV1),
"video/rtx" => Ok(Self::Rtx),
"video/red" => Ok(Self::Red),
"video/ulpfec" => Ok(Self::Ulpfec),
s => Err(if s.starts_with("video/") {
ParseMimeTypeError::UnknownMimeType
} else {
ParseMimeTypeError::InvalidInput
}),
}
}
}
impl<'de> Deserialize<'de> for MimeTypeVideo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
MimeTypeVideo::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl MimeTypeVideo {
pub fn as_str(&self) -> &'static str {
match self {
Self::Vp8 => "video/VP8",
Self::Vp9 => "video/VP9",
Self::H264 => "video/H264",
Self::AV1 => "video/AV1",
Self::Rtx => "video/rtx",
Self::Red => "video/red",
Self::Ulpfec => "video/ulpfec",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum RtpCodecCapability {
#[serde(rename_all = "camelCase")]
Audio {
mime_type: MimeTypeAudio,
#[serde(skip_serializing_if = "Option::is_none")]
preferred_payload_type: Option<u8>,
clock_rate: NonZeroU32,
channels: NonZeroU8,
#[serde(default)]
parameters: RtpCodecParametersParameters,
#[serde(default)]
rtcp_feedback: Vec<RtcpFeedback>,
},
#[serde(rename_all = "camelCase")]
Video {
mime_type: MimeTypeVideo,
#[serde(skip_serializing_if = "Option::is_none")]
preferred_payload_type: Option<u8>,
clock_rate: NonZeroU32,
#[serde(default)]
parameters: RtpCodecParametersParameters,
#[serde(default)]
rtcp_feedback: Vec<RtcpFeedback>,
},
}
impl RtpCodecCapability {
pub fn mime_type(&self) -> MimeType {
match self {
Self::Audio { mime_type, .. } => MimeType::Audio(*mime_type),
Self::Video { mime_type, .. } => MimeType::Video(*mime_type),
}
}
pub fn parameters(&self) -> &RtpCodecParametersParameters {
let (Self::Audio { parameters, .. } | Self::Video { parameters, .. }) = self;
parameters
}
pub fn parameters_mut(&mut self) -> &mut RtpCodecParametersParameters {
let (Self::Audio { parameters, .. } | Self::Video { parameters, .. }) = self;
parameters
}
pub fn preferred_payload_type(&self) -> Option<u8> {
match self {
Self::Audio {
preferred_payload_type,
..
}
| Self::Video {
preferred_payload_type,
..
} => *preferred_payload_type,
}
}
pub fn rtcp_feedback(&self) -> &Vec<RtcpFeedback> {
let (Self::Audio { rtcp_feedback, .. } | Self::Video { rtcp_feedback, .. }) = self;
rtcp_feedback
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpCapabilities {
pub codecs: Vec<RtpCodecCapability>,
pub header_extensions: Vec<RtpHeaderExtension>,
}
#[derive(
Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Default,
)]
#[serde(rename_all = "lowercase")]
pub enum RtpHeaderExtensionDirection {
#[default]
SendRecv,
SendOnly,
RecvOnly,
Inactive,
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum RtpHeaderExtensionUriParseError {
#[error("Unsupported")]
Unsupported,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
pub enum RtpHeaderExtensionUri {
#[serde(rename = "urn:ietf:params:rtp-hdrext:sdes:mid")]
Mid,
#[serde(rename = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id")]
RtpStreamId,
#[serde(rename = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id")]
RepairRtpStreamId,
#[serde(rename = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")]
AbsSendTime,
#[serde(rename = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01")]
TransportWideCcDraft01,
#[serde(rename = "urn:ietf:params:rtp-hdrext:ssrc-audio-level")]
SsrcAudioLevel,
#[serde(
rename = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension"
)]
DependencyDescriptor,
#[serde(rename = "urn:3gpp:video-orientation")]
VideoOrientation,
#[serde(rename = "urn:ietf:params:rtp-hdrext:toffset")]
TimeOffset,
#[serde(rename = "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time")]
AbsCaptureTime,
#[serde(rename = "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay")]
PlayoutDelay,
#[serde(rename = "urn:mediasoup:params:rtp-hdrext:packet-id")]
MediasoupPacketId,
#[doc(hidden)]
#[serde(other, rename = "unsupported")]
Unsupported,
}
impl FromStr for RtpHeaderExtensionUri {
type Err = RtpHeaderExtensionUriParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"urn:ietf:params:rtp-hdrext:sdes:mid" => Ok(Self::Mid),
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id" => Ok(Self::RtpStreamId),
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id" => Ok(Self::RepairRtpStreamId),
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time" => Ok(Self::AbsSendTime),
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01" => {
Ok(Self::TransportWideCcDraft01)
}
"urn:ietf:params:rtp-hdrext:ssrc-audio-level" => Ok(Self::SsrcAudioLevel),
"https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension" => Ok(Self::DependencyDescriptor),
"urn:3gpp:video-orientation" => Ok(Self::VideoOrientation),
"urn:ietf:params:rtp-hdrext:toffset" => Ok(Self::TimeOffset),
"http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time" => {
Ok(Self::AbsCaptureTime)
}
"http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" => Ok(Self::PlayoutDelay),
"urn:mediasoup:params:rtp-hdrext:packet-id" => Ok(Self::MediasoupPacketId),
_ => Err(RtpHeaderExtensionUriParseError::Unsupported),
}
}
}
impl RtpHeaderExtensionUri {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
RtpHeaderExtensionUri::Mid => "urn:ietf:params:rtp-hdrext:sdes:mid",
RtpHeaderExtensionUri::RtpStreamId => "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
RtpHeaderExtensionUri::RepairRtpStreamId => {
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
}
RtpHeaderExtensionUri::AbsSendTime => {
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
}
RtpHeaderExtensionUri::TransportWideCcDraft01 => {
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
}
RtpHeaderExtensionUri::SsrcAudioLevel => "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
RtpHeaderExtensionUri::DependencyDescriptor => "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension",
RtpHeaderExtensionUri::VideoOrientation => "urn:3gpp:video-orientation",
RtpHeaderExtensionUri::TimeOffset => "urn:ietf:params:rtp-hdrext:toffset",
RtpHeaderExtensionUri::AbsCaptureTime => {
"http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"
}
RtpHeaderExtensionUri::PlayoutDelay => {
"http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"
}
RtpHeaderExtensionUri::MediasoupPacketId => {
"urn:mediasoup:params:rtp-hdrext:packet-id"
}
RtpHeaderExtensionUri::Unsupported => "unsupported",
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpHeaderExtension {
pub kind: MediaKind,
pub uri: RtpHeaderExtensionUri,
pub preferred_id: u16,
pub preferred_encrypt: bool,
pub direction: RtpHeaderExtensionDirection,
}
#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub mid: Option<String>,
pub codecs: Vec<RtpCodecParameters>,
pub header_extensions: Vec<RtpHeaderExtensionParameters>,
pub encodings: Vec<RtpEncodingParameters>,
pub rtcp: RtcpParameters,
#[serde(skip_serializing_if = "Option::is_none")]
pub msid: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(untagged)]
pub enum RtpCodecParametersParametersValue {
String(Cow<'static, str>),
Number(u32),
}
impl From<Cow<'static, str>> for RtpCodecParametersParametersValue {
fn from(s: Cow<'static, str>) -> Self {
Self::String(s)
}
}
impl From<String> for RtpCodecParametersParametersValue {
fn from(s: String) -> Self {
Self::String(s.into())
}
}
impl From<&'static str> for RtpCodecParametersParametersValue {
fn from(s: &'static str) -> Self {
Self::String(s.into())
}
}
impl From<u8> for RtpCodecParametersParametersValue {
fn from(n: u8) -> Self {
Self::Number(u32::from(n))
}
}
impl From<u16> for RtpCodecParametersParametersValue {
fn from(n: u16) -> Self {
Self::Number(u32::from(n))
}
}
impl From<u32> for RtpCodecParametersParametersValue {
fn from(n: u32) -> Self {
Self::Number(n)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(untagged, rename_all = "lowercase")]
pub enum RtpCodecParameters {
#[serde(rename_all = "camelCase")]
Audio {
mime_type: MimeTypeAudio,
payload_type: u8,
clock_rate: NonZeroU32,
channels: NonZeroU8,
#[serde(default)]
parameters: RtpCodecParametersParameters,
#[serde(default)]
rtcp_feedback: Vec<RtcpFeedback>,
},
#[serde(rename_all = "camelCase")]
Video {
mime_type: MimeTypeVideo,
payload_type: u8,
clock_rate: NonZeroU32,
#[serde(default)]
parameters: RtpCodecParametersParameters,
#[serde(default)]
rtcp_feedback: Vec<RtcpFeedback>,
},
}
impl RtpCodecParameters {
pub fn is_rtx(&self) -> bool {
match self {
Self::Audio { mime_type, .. } => mime_type == &MimeTypeAudio::Rtx,
Self::Video { mime_type, .. } => mime_type == &MimeTypeVideo::Rtx,
}
}
pub fn mime_type(&self) -> MimeType {
match self {
Self::Audio { mime_type, .. } => MimeType::Audio(*mime_type),
Self::Video { mime_type, .. } => MimeType::Video(*mime_type),
}
}
pub fn payload_type(&self) -> u8 {
let (Self::Audio { payload_type, .. } | Self::Video { payload_type, .. }) = self;
*payload_type
}
pub fn clock_rate(&self) -> NonZeroU32 {
let (Self::Audio { clock_rate, .. } | Self::Video { clock_rate, .. }) = self;
*clock_rate
}
pub fn parameters(&self) -> &RtpCodecParametersParameters {
let (Self::Audio { parameters, .. } | Self::Video { parameters, .. }) = self;
parameters
}
pub fn rtcp_feedback(&self) -> &[RtcpFeedback] {
let (Self::Audio { rtcp_feedback, .. } | Self::Video { rtcp_feedback, .. }) = self;
rtcp_feedback
}
pub fn rtcp_feedback_mut(&mut self) -> &mut Vec<RtcpFeedback> {
let (Self::Audio { rtcp_feedback, .. } | Self::Video { rtcp_feedback, .. }) = self;
rtcp_feedback
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum RtcpFeedback {
Nack,
NackPli,
CcmFir,
GoogRemb,
TransportCc,
#[doc(hidden)]
Unsupported,
}
impl Serialize for RtcpFeedback {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut rtcp_feedback = serializer.serialize_struct("RtcpFeedback", 2)?;
let (r#type, parameter) = self.as_type_parameter();
rtcp_feedback.serialize_field("type", r#type)?;
rtcp_feedback.serialize_field("parameter", parameter)?;
rtcp_feedback.end()
}
}
impl<'de> Deserialize<'de> for RtcpFeedback {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Type,
Parameter,
}
struct RtcpFeedbackVisitor;
impl<'de> Visitor<'de> for RtcpFeedbackVisitor {
type Value = RtcpFeedback;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(
r#"RTCP feedback type and parameter like {"type": "nack", "parameter": ""}"#,
)
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
let mut r#type = None::<Cow<'_, str>>;
let mut parameter = Cow::Borrowed("");
while let Some(key) = map.next_key()? {
match key {
Field::Type => {
if r#type.is_some() {
return Err(de::Error::duplicate_field("type"));
}
r#type = Some(map.next_value()?);
}
Field::Parameter => {
if !parameter.is_empty() {
return Err(de::Error::duplicate_field("parameter"));
}
parameter = map.next_value()?;
}
}
}
let r#type = r#type.ok_or_else(|| de::Error::missing_field("type"))?;
Ok(
RtcpFeedback::from_type_parameter(r#type.as_ref(), parameter.as_ref())
.unwrap_or(RtcpFeedback::Unsupported),
)
}
}
const FIELDS: &[&str] = &["type", "parameter"];
deserializer.deserialize_struct("RtcpFeedback", FIELDS, RtcpFeedbackVisitor)
}
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum RtcpFeedbackFromTypeParameterError {
#[error("Unsupported")]
Unsupported,
}
impl RtcpFeedback {
pub fn from_type_parameter(
r#type: &str,
parameter: &str,
) -> Result<Self, RtcpFeedbackFromTypeParameterError> {
match (r#type, parameter) {
("nack", "") => Ok(RtcpFeedback::Nack),
("nack", "pli") => Ok(RtcpFeedback::NackPli),
("ccm", "fir") => Ok(RtcpFeedback::CcmFir),
("goog-remb", "") => Ok(RtcpFeedback::GoogRemb),
("transport-cc", "") => Ok(RtcpFeedback::TransportCc),
("unknown", "") => Ok(RtcpFeedback::Unsupported),
_ => Err(RtcpFeedbackFromTypeParameterError::Unsupported),
}
}
pub fn as_type_parameter(&self) -> (&'static str, &'static str) {
match self {
RtcpFeedback::Nack => ("nack", ""),
RtcpFeedback::NackPli => ("nack", "pli"),
RtcpFeedback::CcmFir => ("ccm", "fir"),
RtcpFeedback::GoogRemb => ("goog-remb", ""),
RtcpFeedback::TransportCc => ("transport-cc", ""),
RtcpFeedback::Unsupported => ("unknown", ""),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
pub struct RtpEncodingParametersRtx {
pub ssrc: u32,
}
#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpEncodingParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub ssrc: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub codec_payload_type: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rtx: Option<RtpEncodingParametersRtx>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dtx: Option<bool>,
#[serde(default, skip_serializing_if = "ScalabilityMode::is_none")]
pub scalability_mode: ScalabilityMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_bitrate: Option<u32>,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
pub struct RtpHeaderExtensionParameters {
pub uri: RtpHeaderExtensionUri,
pub id: u16,
pub encrypt: bool,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcpParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub cname: Option<String>,
pub reduced_size: bool,
}
impl Default for RtcpParameters {
fn default() -> Self {
Self {
cname: None,
reduced_size: true,
}
}
}