make jingle more generic over codecs

This adds a very simple Codec struct, which describes each codec
supported by a RTP description.

It will make it much easier to add newer codecs in the future, like AV1.

This commit is best viewed with git log -p -w to ignore indent changes.
This commit is contained in:
Emmanuel Gil Peyrot 2021-10-23 19:31:04 +02:00 committed by Jasper
parent 5db78b11bf
commit 7fa31bd796
1 changed files with 292 additions and 318 deletions

View File

@ -41,18 +41,82 @@ const RTP_HDREXT_TRANSPORT_CC: &str =
const DEFAULT_STUN_PORT: u16 = 3478; const DEFAULT_STUN_PORT: u16 = 3478;
const DEFAULT_TURNS_PORT: u16 = 5349; const DEFAULT_TURNS_PORT: u16 = 5349;
#[derive(Clone, PartialEq)]
enum CodecName {
Opus,
H264,
Vp8,
Vp9,
}
#[derive(Clone)]
struct Codec {
name: CodecName,
pt: u8,
rtx_pt: Option<u8>,
rtcp_fbs: Vec<RtcpFb>,
}
impl Codec {
fn is(&self, pt: u8) -> bool {
self.pt == pt
}
fn is_rtx(&self, rtx_pt: u8) -> bool {
if let Some(pt) = self.rtx_pt {
pt == rtx_pt
} else {
false
}
}
fn is_audio(&self) -> bool {
self.name == CodecName::Opus
}
fn is_video(&self) -> bool {
self.name != CodecName::Opus
}
fn is_codec(&self, name: &str) -> bool {
match name {
"h264" => self.name == CodecName::H264,
"vp8" => self.name == CodecName::Vp8,
"vp9" => self.name == CodecName::Vp9,
_ => false,
}
}
fn encoding_name(&self) -> &'static str {
match self.name {
CodecName::Opus => "opus",
CodecName::H264 => "H264",
CodecName::Vp8 => "VP8",
CodecName::Vp9 => "VP9",
}
}
fn make_depay_name(&self) -> &'static str {
match self.name {
CodecName::Opus => "rtpopusdepay",
CodecName::H264 => "rtph264depay",
CodecName::Vp8 => "rtpvp8depay",
CodecName::Vp9 => "rtpvp9depay",
}
}
fn make_pay_name(&self) -> &'static str {
match self.name {
CodecName::Opus => "rtpopuspay",
CodecName::H264 => "rtph264pay",
CodecName::Vp8 => "rtpvp8pay",
CodecName::Vp9 => "rtpvp9pay",
}
}
}
struct ParsedRtpDescription { struct ParsedRtpDescription {
opus_payload_type: Option<u8>, codecs: Vec<Codec>,
opus_rtcp_fbs: Option<Vec<RtcpFb>>,
h264_payload_type: Option<u8>,
h264_rtx_payload_type: Option<u8>,
h264_rtcp_fbs: Option<Vec<RtcpFb>>,
vp8_payload_type: Option<u8>,
vp8_rtx_payload_type: Option<u8>,
vp8_rtcp_fbs: Option<Vec<RtcpFb>>,
vp9_payload_type: Option<u8>,
vp9_rtx_payload_type: Option<u8>,
vp9_rtcp_fbs: Option<Vec<RtcpFb>>,
audio_hdrext_ssrc_audio_level: Option<u8>, audio_hdrext_ssrc_audio_level: Option<u8>,
audio_hdrext_transport_cc: Option<u8>, audio_hdrext_transport_cc: Option<u8>,
video_hdrext_abs_send_time: Option<u8>, video_hdrext_abs_send_time: Option<u8>,
@ -111,17 +175,10 @@ impl JingleSession {
} }
fn parse_rtp_description(description: &RtpDescription, remote_ssrc_map: &mut HashMap<u32, Source>) -> Result<Option<ParsedRtpDescription>> { fn parse_rtp_description(description: &RtpDescription, remote_ssrc_map: &mut HashMap<u32, Source>) -> Result<Option<ParsedRtpDescription>> {
let mut opus_payload_type = None; let mut opus = None;
let mut opus_rtcp_fbs = None; let mut h264 = None;
let mut h264_payload_type = None; let mut vp8 = None;
let mut h264_rtx_payload_type = None; let mut vp9 = None;
let mut h264_rtcp_fbs = None;
let mut vp8_payload_type = None;
let mut vp8_rtx_payload_type = None;
let mut vp8_rtcp_fbs = None;
let mut vp9_payload_type = None;
let mut vp9_rtx_payload_type = None;
let mut vp9_rtcp_fbs = None;
let mut audio_hdrext_ssrc_audio_level = None; let mut audio_hdrext_ssrc_audio_level = None;
let mut audio_hdrext_transport_cc = None; let mut audio_hdrext_transport_cc = None;
let mut video_hdrext_abs_send_time = None; let mut video_hdrext_abs_send_time = None;
@ -133,8 +190,12 @@ impl JingleSession {
if let Some(name) = &pt.name { if let Some(name) = &pt.name {
match name.as_str() { match name.as_str() {
"opus" => { "opus" => {
opus_payload_type = Some(pt.id); opus = Some(Codec {
opus_rtcp_fbs = Some(pt.rtcp_fbs.clone()); name: CodecName::Opus,
pt: pt.id,
rtx_pt: None,
rtcp_fbs: pt.rtcp_fbs.clone(),
});
} }
_ => (), _ => (),
} }
@ -157,16 +218,28 @@ impl JingleSession {
if let Some(name) = &pt.name { if let Some(name) = &pt.name {
match name.as_str() { match name.as_str() {
"H264" => { "H264" => {
h264_payload_type = Some(pt.id); h264 = Some(Codec {
h264_rtcp_fbs = Some(pt.rtcp_fbs.clone()); name: CodecName::H264,
pt: pt.id,
rtx_pt: None,
rtcp_fbs: pt.rtcp_fbs.clone(),
});
} }
"VP8" => { "VP8" => {
vp8_payload_type = Some(pt.id); vp8 = Some(Codec {
vp8_rtcp_fbs = Some(pt.rtcp_fbs.clone()); name: CodecName::Vp8,
pt: pt.id,
rtx_pt: None,
rtcp_fbs: pt.rtcp_fbs.clone(),
});
} }
"VP9" => { "VP9" => {
vp9_payload_type = Some(pt.id); vp9 = Some(Codec {
vp9_rtcp_fbs = Some(pt.rtcp_fbs.clone()); name: CodecName::Vp9,
pt: pt.id,
rtx_pt: None,
rtcp_fbs: pt.rtcp_fbs.clone(),
});
} }
_ => (), _ => (),
} }
@ -177,18 +250,21 @@ impl JingleSession {
if name == "rtx" { if name == "rtx" {
for param in pt.parameters.iter() { for param in pt.parameters.iter() {
if param.name == "apt" { if param.name == "apt" {
let pt: u8 = param.value.parse()?; let apt_pt: u8 = param.value.parse()?;
if Some(pt) == h264_payload_type { if let Some(h264) = &mut h264 {
h264_rtx_payload_type = Some(pt); if apt_pt == h264.pt {
h264.rtx_pt = Some(pt.id);
}
} }
else if Some(pt) == h264_payload_type { if let Some(vp8) = &mut vp8 {
h264_rtx_payload_type = Some(pt); if apt_pt == vp8.pt {
vp8.rtx_pt = Some(pt.id);
}
} }
else if Some(pt) == h264_payload_type { if let Some(vp9) = &mut vp9 {
h264_rtx_payload_type = Some(pt); if apt_pt == vp9.pt {
} vp9.rtx_pt = Some(pt.id);
else { }
bail!("unknown rtx apt: {}", pt);
} }
} }
} }
@ -211,6 +287,8 @@ impl JingleSession {
return Ok(None); return Ok(None);
} }
let codecs = [opus, h264, vp8, vp9].iter().flatten().cloned().collect();
for ssrc in &description.ssrcs { for ssrc in &description.ssrcs {
let owner = ssrc let owner = ssrc
.info .info
@ -246,17 +324,7 @@ impl JingleSession {
); );
} }
Ok(Some(ParsedRtpDescription { Ok(Some(ParsedRtpDescription {
opus_payload_type, codecs,
opus_rtcp_fbs,
h264_payload_type,
h264_rtx_payload_type,
h264_rtcp_fbs,
vp8_payload_type,
vp8_rtx_payload_type,
vp8_rtcp_fbs,
vp9_payload_type,
vp9_rtx_payload_type,
vp9_rtcp_fbs,
audio_hdrext_ssrc_audio_level, audio_hdrext_ssrc_audio_level,
audio_hdrext_transport_cc, audio_hdrext_transport_cc,
video_hdrext_abs_send_time, video_hdrext_abs_send_time,
@ -396,17 +464,7 @@ impl JingleSession {
debug!("Received Jingle session-initiate from {}", initiator); debug!("Received Jingle session-initiate from {}", initiator);
let mut ice_transport = None; let mut ice_transport = None;
let mut opus_payload_type = None; let mut codecs = vec![];
let mut opus_rtcp_fbs = None;
let mut h264_payload_type = None;
let mut h264_rtx_payload_type = None;
let mut h264_rtcp_fbs = None;
let mut vp8_payload_type = None;
let mut vp8_rtx_payload_type = None;
let mut vp8_rtcp_fbs = None;
let mut vp9_payload_type = None;
let mut vp9_rtx_payload_type = None;
let mut vp9_rtcp_fbs = None;
let mut audio_hdrext_ssrc_audio_level = None; let mut audio_hdrext_ssrc_audio_level = None;
let mut audio_hdrext_transport_cc = None; let mut audio_hdrext_transport_cc = None;
let mut video_hdrext_abs_send_time = None; let mut video_hdrext_abs_send_time = None;
@ -417,17 +475,7 @@ impl JingleSession {
for content in &jingle.contents { for content in &jingle.contents {
if let Some(Description::Rtp(description)) = &content.description { if let Some(Description::Rtp(description)) = &content.description {
if let Some(description) = JingleSession::parse_rtp_description(description, &mut remote_ssrc_map)? { if let Some(description) = JingleSession::parse_rtp_description(description, &mut remote_ssrc_map)? {
opus_payload_type = opus_payload_type.or(description.opus_payload_type); codecs.extend(description.codecs);
opus_rtcp_fbs = opus_rtcp_fbs.or(description.opus_rtcp_fbs);
h264_payload_type = h264_payload_type.or(description.h264_payload_type);
h264_rtx_payload_type = h264_rtx_payload_type.or(description.h264_rtx_payload_type);
h264_rtcp_fbs = h264_rtcp_fbs.or(description.h264_rtcp_fbs);
vp8_payload_type = vp8_payload_type.or(description.vp8_payload_type);
vp8_rtx_payload_type = vp8_rtx_payload_type.or(description.vp8_rtx_payload_type);
vp8_rtcp_fbs = vp8_rtcp_fbs.or(description.vp8_rtcp_fbs);
vp9_payload_type = vp9_payload_type.or(description.vp9_payload_type);
vp9_rtx_payload_type = vp9_rtx_payload_type.or(description.vp9_rtx_payload_type);
vp9_rtcp_fbs = vp9_rtcp_fbs.or(description.vp9_rtcp_fbs);
audio_hdrext_ssrc_audio_level = audio_hdrext_ssrc_audio_level.or(description.audio_hdrext_ssrc_audio_level); audio_hdrext_ssrc_audio_level = audio_hdrext_ssrc_audio_level.or(description.audio_hdrext_ssrc_audio_level);
audio_hdrext_transport_cc = audio_hdrext_transport_cc.or(description.audio_hdrext_transport_cc); audio_hdrext_transport_cc = audio_hdrext_transport_cc.or(description.audio_hdrext_transport_cc);
video_hdrext_abs_send_time = video_hdrext_abs_send_time.or(description.video_hdrext_abs_send_time); video_hdrext_abs_send_time = video_hdrext_abs_send_time.or(description.video_hdrext_abs_send_time);
@ -522,96 +570,68 @@ impl JingleSession {
)?; )?;
pipeline.add(&dtlssrtpdec)?; pipeline.add(&dtlssrtpdec)?;
rtpbin.connect("request-pt-map", false, move |values| { {
let f = || { let codecs = codecs.clone();
debug!("rtpbin request-pt-map {:?}", values); rtpbin.connect("request-pt-map", false, move |values| {
let pt = values[2].get::<u32>()? as u8; let f = || {
let mut caps = gstreamer::Caps::builder("application/x-rtp"); debug!("rtpbin request-pt-map {:?}", values);
if Some(pt) == opus_payload_type { let pt = values[2].get::<u32>()? as u8;
caps = caps let mut caps = gstreamer::Caps::builder("application/x-rtp");
.field("media", "audio") for codec in codecs.iter() {
.field("encoding-name", "OPUS") if codec.is(pt) {
.field("clock-rate", 48000); if codec.is_audio() {
if let Some(hdrext) = audio_hdrext_ssrc_audio_level { caps = caps
caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL); .field("media", "audio")
} .field("encoding-name", "OPUS")
if let Some(hdrext) = audio_hdrext_transport_cc { .field("clock-rate", 48000);
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC); if let Some(hdrext) = audio_hdrext_ssrc_audio_level {
} caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL);
Ok::<_, anyhow::Error>(Some(caps.build())) }
} if let Some(hdrext) = audio_hdrext_transport_cc {
else if Some(pt) == h264_payload_type caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|| Some(pt) == vp8_payload_type }
|| Some(pt) == vp9_payload_type
{
caps = caps
.field("media", "video")
.field("clock-rate", 90000)
.field(
"encoding-name",
if Some(pt) == h264_payload_type {
"H264"
}
else if Some(pt) == vp8_payload_type {
"VP8"
}
else if Some(pt) == vp9_payload_type {
"VP9"
} }
else { else {
unreachable!() // A video codec, as the only audio codec we support is Opus.
}, caps = caps
); .field("media", "video")
// if let Some(hdrext) = video_hdrext_abs_send_time { .field("clock-rate", 90000)
// caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME); .field("encoding-name", codec.encoding_name());
// } // if let Some(hdrext) = video_hdrext_abs_send_time {
if let Some(hdrext) = video_hdrext_transport_cc { // caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC); // }
if let Some(hdrext) = video_hdrext_transport_cc {
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
}
}
return Ok::<_, anyhow::Error>(Some(caps.build()));
}
else if codec.is_rtx(pt) {
caps = caps
.field("media", "video")
.field("clock-rate", 90000)
.field("encoding-name", "RTX")
.field("apt", codec.pt);
return Ok(Some(caps.build()));
}
} }
Ok(Some(caps.build()))
}
else if Some(pt) == vp8_rtx_payload_type
|| Some(pt) == vp9_rtx_payload_type
|| Some(pt) == h264_rtx_payload_type
{
caps = caps
.field("media", "video")
.field("clock-rate", 90000)
.field("encoding-name", "RTX")
.field(
"apt",
if Some(pt) == vp8_rtx_payload_type {
vp8_payload_type.context("missing VP8 payload type")?
}
else if Some(pt) == vp9_rtx_payload_type {
vp9_payload_type.context("missing VP9 payload type")?
}
else if Some(pt) == h264_rtx_payload_type {
h264_payload_type.context("missing H264 payload type")?
}
else {
unreachable!()
},
);
Ok(Some(caps.build()))
}
else {
warn!("unknown payload type: {}", pt); warn!("unknown payload type: {}", pt);
Ok(None) Ok(None)
};
match f() {
Ok(Some(caps)) => {
debug!("mapped pt to caps: {:?}", caps);
Some(caps.to_value())
},
Ok(None) => None,
Err(e) => {
error!("handling request-pt-map: {:?}", e);
None
},
} }
}; })?;
match f() { }
Ok(Some(caps)) => {
debug!("mapped pt to caps: {:?}", caps);
Some(caps.to_value())
},
Ok(None) => None,
Err(e) => {
error!("handling request-pt-map: {:?}", e);
None
},
}
})?;
let handle = Handle::current(); let handle = Handle::current();
let jingle_session = conference.jingle_session.clone(); let jingle_session = conference.jingle_session.clone();
@ -653,63 +673,64 @@ impl JingleSession {
None None
})?; })?;
rtpbin.connect("request-aux-sender", false, move |values| { let pts: Vec<(String, u32)> = codecs.iter()
let f = move || { .filter(|codec| codec.is_video())
let session: u32 = values[1].get()?; .flat_map(|codec| {
debug!("creating RTX sender for session {}", session); if let Some(rtx_pt) = codec.rtx_pt {
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map"); Some((codec.pt.to_string(), rtx_pt as u32))
let mut ssrc_map = gstreamer::Structure::builder("application/x-rtp-ssrc-map"); } else {
if let (Some(pt), Some(rtx_pt)) = (vp8_payload_type, vp8_rtx_payload_type) {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
}
if let (Some(pt), Some(rtx_pt)) = (vp9_payload_type, vp9_rtx_payload_type) {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
}
if let (Some(pt), Some(rtx_pt)) = (h264_payload_type, h264_rtx_payload_type) {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
}
ssrc_map = ssrc_map.field(&video_ssrc.to_string(), &(video_rtx_ssrc as u32));
let bin = gstreamer::Bin::new(None);
let rtx_sender = gstreamer::ElementFactory::make("rtprtxsend", None)?;
rtx_sender.set_property("payload-type-map", pt_map.build())?;
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
bin.add(&rtx_sender)?;
bin.add_pad(&gstreamer::GhostPad::with_target(
Some(&format!("src_{}", session)),
&rtx_sender
.static_pad("src")
.context("rtprtxsend has no src pad")?,
)?)?;
bin.add_pad(&gstreamer::GhostPad::with_target(
Some(&format!("sink_{}", session)),
&rtx_sender
.static_pad("sink")
.context("rtprtxsend has no sink pad")?,
)?)?;
Ok::<_, anyhow::Error>(Some(bin.to_value()))
};
match f() {
Ok(o) => o,
Err(e) => {
warn!("request-aux-sender: {:?}", e);
None None
}, }
} })
})?; .collect();
{
let pts = pts.clone();
rtpbin.connect("request-aux-sender", false, move |values| {
let f = || {
let session: u32 = values[1].get()?;
debug!("creating RTX sender for session {}", session);
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
let mut ssrc_map = gstreamer::Structure::builder("application/x-rtp-ssrc-map");
for (pt, rtx_pt) in pts.iter() {
pt_map = pt_map.field(pt, rtx_pt);
}
ssrc_map = ssrc_map.field(&video_ssrc.to_string(), &(video_rtx_ssrc as u32));
let bin = gstreamer::Bin::new(None);
let rtx_sender = gstreamer::ElementFactory::make("rtprtxsend", None)?;
rtx_sender.set_property("payload-type-map", pt_map.build())?;
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
bin.add(&rtx_sender)?;
bin.add_pad(&gstreamer::GhostPad::with_target(
Some(&format!("src_{}", session)),
&rtx_sender
.static_pad("src")
.context("rtprtxsend has no src pad")?,
)?)?;
bin.add_pad(&gstreamer::GhostPad::with_target(
Some(&format!("sink_{}", session)),
&rtx_sender
.static_pad("sink")
.context("rtprtxsend has no sink pad")?,
)?)?;
Ok::<_, anyhow::Error>(Some(bin.to_value()))
};
match f() {
Ok(o) => o,
Err(e) => {
warn!("request-aux-sender: {:?}", e);
None
},
}
})?;
}
rtpbin.connect("request-aux-receiver", false, move |values| { rtpbin.connect("request-aux-receiver", false, move |values| {
let f = move || { let f = || {
let session: u32 = values[1].get()?; let session: u32 = values[1].get()?;
debug!("creating RTX receiver for session {}", session); debug!("creating RTX receiver for session {}", session);
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map"); let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
if let (Some(pt), Some(rtx_pt)) = (vp8_payload_type, vp8_rtx_payload_type) { for (pt, rtx_pt) in pts.iter() {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32)); pt_map = pt_map.field(pt, rtx_pt);
}
if let (Some(pt), Some(rtx_pt)) = (vp9_payload_type, vp9_rtx_payload_type) {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
}
if let (Some(pt), Some(rtx_pt)) = (h264_payload_type, h264_rtx_payload_type) {
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
} }
let bin = gstreamer::Bin::new(None); let bin = gstreamer::Bin::new(None);
let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?; let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?;
@ -743,6 +764,7 @@ impl JingleSession {
let conference = conference.clone(); let conference = conference.clone();
let pipeline = pipeline.clone(); let pipeline = pipeline.clone();
let rtpbin_ = rtpbin.clone(); let rtpbin_ = rtpbin.clone();
let codecs = codecs.clone();
rtpbin.connect("pad-added", false, move |values| { rtpbin.connect("pad-added", false, move |values| {
let rtpbin = &rtpbin_; let rtpbin = &rtpbin_;
let f = || { let f = || {
@ -772,26 +794,22 @@ impl JingleSession {
let source_element = match source.media_type { let source_element = match source.media_type {
MediaType::Audio => { MediaType::Audio => {
if Some(pt) == opus_payload_type { let codec = codecs.iter()
gstreamer::ElementFactory::make("rtpopusdepay", None)? .filter(|codec| codec.is_audio())
.find(|codec| codec.is(pt));
if let Some(codec) = codec {
gstreamer::ElementFactory::make(codec.make_depay_name(), None)?
} }
else { else {
bail!("received audio with unsupported PT {}", pt); bail!("received audio with unsupported PT {}", pt);
} }
}, },
MediaType::Video => { MediaType::Video => {
if Some(pt) == h264_payload_type { let codec = codecs.iter()
let element = gstreamer::ElementFactory::make("rtph264depay", None)?; .filter(|codec| codec.is_video())
element.set_property("request-keyframe", true)?; .find(|codec| codec.is(pt));
element if let Some(codec) = codec {
} let element = gstreamer::ElementFactory::make(codec.make_depay_name(), None)?;
else if Some(pt) == vp8_payload_type {
let element = gstreamer::ElementFactory::make("rtpvp8depay", None)?;
element.set_property("request-keyframe", true)?;
element
}
else if Some(pt) == vp9_payload_type {
let element = gstreamer::ElementFactory::make("rtpvp9depay", None)?;
element.set_property("request-keyframe", true)?; element.set_property("request-keyframe", true)?;
element element
} }
@ -897,11 +915,14 @@ impl JingleSession {
})?; })?;
} }
let audio_sink_element = gstreamer::ElementFactory::make("rtpopuspay", None)?; let opus = codecs.iter().find(|codec| codec.name == CodecName::Opus);
audio_sink_element.set_property( let audio_sink_element = if let Some(opus) = opus {
"pt", let audio_sink_element = gstreamer::ElementFactory::make(opus.make_pay_name(), None)?;
opus_payload_type.context("no opus payload type in jingle session-initiate")? as u32, audio_sink_element.set_property("pt", opus.pt as u32)?;
)?; audio_sink_element
} else {
bail!("no opus payload type in jingle session-initiate");
};
audio_sink_element.set_property("min-ptime", 10i64 * 1000 * 1000)?; audio_sink_element.set_property("min-ptime", 10i64 * 1000 * 1000)?;
audio_sink_element.set_property("ssrc", audio_ssrc)?; audio_sink_element.set_property("ssrc", audio_ssrc)?;
if audio_sink_element.has_property("auto-header-extension", None) { if audio_sink_element.has_property("auto-header-extension", None) {
@ -943,35 +964,21 @@ impl JingleSession {
} }
pipeline.add(&audio_sink_element)?; pipeline.add(&audio_sink_element)?;
let video_sink_element = match conference.config.video_codec.as_str() { let codec_name = conference.config.video_codec.as_str();
"h264" => { let codec = codecs.iter().find(|codec| codec.is_codec(codec_name));
let element = gstreamer::ElementFactory::make("rtph264pay", None)?; let video_sink_element = if let Some(codec) = codec {
element.set_property( let element = gstreamer::ElementFactory::make(codec.make_pay_name(), None)?;
"pt", element.set_property("pt", codec.pt as u32)?;
h264_payload_type.context("no h264 payload type in jingle session-initiate")? as u32, if codec.name == CodecName::H264 {
)?;
element.set_property_from_str("aggregate-mode", "zero-latency"); element.set_property_from_str("aggregate-mode", "zero-latency");
element }
}, else {
"vp8" => {
let element = gstreamer::ElementFactory::make("rtpvp8pay", None)?;
element.set_property(
"pt",
vp8_payload_type.context("no vp8 payload type in jingle session-initiate")? as u32,
)?;
element.set_property_from_str("picture-id-mode", "15-bit"); element.set_property_from_str("picture-id-mode", "15-bit");
element }
}, element
"vp9" => { }
let element = gstreamer::ElementFactory::make("rtpvp9pay", None)?; else {
element.set_property( bail!("unsupported video codec: {}", codec_name);
"pt",
vp9_payload_type.context("no vp9 payload type in jingle session-initiate")? as u32,
)?;
element.set_property_from_str("picture-id-mode", "15-bit");
element
},
other => bail!("unsupported video codec: {}", other),
}; };
video_sink_element.set_property("ssrc", video_ssrc)?; video_sink_element.set_property("ssrc", video_ssrc)?;
if video_sink_element.has_property("auto-header-extension", None) { if video_sink_element.has_property("auto-header-extension", None) {
@ -1114,73 +1121,40 @@ impl JingleSession {
let mut description = RtpDescription::new(initiate_content.name.0.clone()); let mut description = RtpDescription::new(initiate_content.name.0.clone());
description.payload_types = if initiate_content.name.0 == "audio" { description.payload_types = if initiate_content.name.0 == "audio" {
let mut pt = PayloadType::new( let codec = codecs.iter().find(|codec| codec.name == CodecName::Opus);
opus_payload_type.context("no opus payload type in jingle session-initiate")?, if let Some(codec) = codec {
"opus".to_owned(), let mut pt = PayloadType::new(
48000, codec.pt,
2, "opus".to_owned(),
); 48000,
pt.rtcp_fbs = opus_rtcp_fbs.clone().unwrap_or_default(); 2,
vec![pt] );
pt.rtcp_fbs = codec.rtcp_fbs.clone();
vec![pt]
}
else {
bail!("no opus payload type in jingle session-initiate");
}
} }
else { else {
let mut pts = vec![]; let mut pts = vec![];
match conference.config.video_codec.as_str() { let codec_name = conference.config.video_codec.as_str();
"h264" => { let codec = codecs.iter().find(|codec| codec.is_codec(codec_name));
if let Some(h264_pt) = h264_payload_type { if let Some(codec) = codec {
let mut pt = PayloadType::new(h264_pt, "H264".to_owned(), 90000, 1); let mut pt = PayloadType::new(codec.pt, codec.encoding_name().to_owned(), 90000, 1);
pt.rtcp_fbs = h264_rtcp_fbs.clone().unwrap_or_default(); pt.rtcp_fbs = codec.rtcp_fbs.clone();
pts.push(pt); pts.push(pt);
if let Some(rtx_pt) = h264_rtx_payload_type { if let Some(rtx_pt) = codec.rtx_pt {
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1); let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
rtx_pt.parameters = vec![jingle_rtp::Parameter { rtx_pt.parameters = vec![jingle_rtp::Parameter {
name: "apt".to_owned(), name: "apt".to_owned(),
value: h264_pt.to_string(), value: codec.pt.to_string(),
}]; }];
pts.push(rtx_pt); pts.push(rtx_pt);
} }
} }
else { else {
bail!("no h264 payload type in jingle session-initiate"); bail!("unsupported video codec: {}", codec_name);
}
},
"vp8" => {
if let Some(vp8_pt) = vp8_payload_type {
let mut pt = PayloadType::new(vp8_pt, "VP8".to_owned(), 90000, 1);
pt.rtcp_fbs = vp8_rtcp_fbs.clone().unwrap_or_default();
pts.push(pt);
if let Some(rtx_pt) = vp8_rtx_payload_type {
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
rtx_pt.parameters = vec![jingle_rtp::Parameter {
name: "apt".to_owned(),
value: vp8_pt.to_string(),
}];
pts.push(rtx_pt);
}
}
else {
bail!("no vp8 payload type in jingle session-initiate");
}
},
"vp9" => {
if let Some(vp9_pt) = vp9_payload_type {
let mut pt = PayloadType::new(vp9_pt, "VP9".to_owned(), 90000, 1);
pt.rtcp_fbs = vp9_rtcp_fbs.clone().unwrap_or_default();
pts.push(pt);
if let Some(rtx_pt) = vp9_rtx_payload_type {
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
rtx_pt.parameters = vec![jingle_rtp::Parameter {
name: "apt".to_owned(),
value: vp9_pt.to_string(),
}];
pts.push(rtx_pt);
}
}
else {
bail!("no vp9 payload type in jingle session-initiate");
}
},
other => bail!("unsupported video codec: {}", other),
} }
pts pts
}; };