diff --git a/Cargo.lock b/Cargo.lock index e4b3c03..0c320b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,9 +387,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c6823b39d46d22cac2466de261f28d7f049ebc18f7b35296a42c7ed8a88325" +checksum = "402a7057cd21d64bfa7ac027b344a7f50f677fb3308693df0e8c70fb55d29f0d" dependencies = [ "bitflags", "futures-channel", @@ -417,9 +417,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbecad7a3a898ee749d491ce2ae0decb0bce9e736f9747bc49159b1cea5d37f4" +checksum = "a8fb802e3798d75b415bea8f016eed88d50106ce82f1274e80f31d80cfd4b056" dependencies = [ "bitflags", "futures-channel", @@ -489,9 +489,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa71d1b3f562645e2d3b96c8d5cf1f26174696e37781ef34193311da733f98c" +checksum = "810e68483c27518ec8491d71ee163f9fc03dcc4ebacee98caa348e8a064898ef" dependencies = [ "bitflags", "cfg-if", @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.17.0" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8188ba998999a4a16005c3984812807ff882a87f5f3457c3d5bbbfcbdf631ebd" +checksum = "a81704feeb3e8599913bdd1e738455c2991a01ff4a1780cb62200993e454cc3e" dependencies = [ "glib-sys", "gobject-sys", @@ -559,9 +559,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba8d84e9efea6aedae6fed9b6d9cfcaac6c53992b437d79a87a549d5537fea9" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] name = "idna" @@ -603,9 +603,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "jid" @@ -618,9 +618,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.52" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752" +checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" dependencies = [ "wasm-bindgen", ] @@ -683,9 +683,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "lock_api" @@ -744,8 +744,6 @@ dependencies = [ [[package]] name = "minidom" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332592c2149fc7dd40a64fc9ef6f0d65607284b474cef9817d1fc8c7e7b3608e" dependencies = [ "quick-xml", ] @@ -1085,9 +1083,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.8.11" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b4fc1b81d685fcd442a86da2e2c829d9e353142633a8159f42bf28e7e94428" +checksum = "2351cbef4bf91837f5ff7face6091cb277ba960d1638d2c5ae2327859912fbba" dependencies = [ "chrono", "ring", @@ -1179,18 +1177,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.127" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.127" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" dependencies = [ "proc-macro2", "quote", @@ -1338,9 +1336,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.74" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", "quote", @@ -1683,9 +1681,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.75" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586" +checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1693,9 +1691,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.75" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" +checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" dependencies = [ "bumpalo", "lazy_static", @@ -1708,9 +1706,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.75" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c" +checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1718,9 +1716,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.75" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f" +checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" dependencies = [ "proc-macro2", "quote", @@ -1731,15 +1729,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.75" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" +checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" [[package]] name = "web-sys" -version = "0.3.52" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696" +checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 47ae19e..8774ad2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ members = [ "nice-gst-meet", "nice-gst-meet-sys", ] + +[patch.crates-io] +minidom = { path = "../xmpp-rs/minidom" } \ No newline at end of file diff --git a/lib-gst-meet/src/jingle.rs b/lib-gst-meet/src/jingle.rs index aff2c93..455ba08 100644 --- a/lib-gst-meet/src/jingle.rs +++ b/lib-gst-meet/src/jingle.rs @@ -22,7 +22,7 @@ use xmpp_parsers::{ jingle::{Action, Content, Creator, Description, Jingle, Senders, Transport}, jingle_dtls_srtp::{Fingerprint, Setup}, jingle_ice_udp::{self, Transport as IceUdpTransport}, - jingle_rtp::{Description as RtpDescription, PayloadType, RtcpMux}, + jingle_rtp::{self, Description as RtpDescription, PayloadType, RtcpMux}, jingle_rtp_hdrext::RtpHdrext, jingle_ssma::{self, Parameter}, Jid, @@ -39,10 +39,6 @@ const RTP_HDREXT_SSRC_AUDIO_LEVEL: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio const RTP_HDREXT_ABS_SEND_TIME: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; const RTP_HDREXT_TRANSPORT_CC: &str = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"; -const RTX_PAYLOAD_TYPE_VP8: u8 = 96; -const RTX_PAYLOAD_TYPE_VP9: u8 = 97; -const RTX_PAYLOAD_TYPE_H264: u8 = 99; - const DEFAULT_STUN_PORT: u16 = 3478; const DEFAULT_TURNS_PORT: u16 = 5349; @@ -111,9 +107,16 @@ impl JingleSession { let mut ice_remote_pwd = None; let mut dtls_fingerprint = None; let mut opus_payload_type = None; + 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_transport_cc = None; let mut video_hdrext_abs_send_time = None; @@ -130,6 +133,11 @@ impl JingleSession { .iter() .find(|pt| pt.name.as_deref() == Some("opus")) .map(|pt| pt.id); + opus_rtcp_fbs = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("opus")) + .map(|pt| pt.rtcp_fbs.clone()); audio_hdrext_ssrc_audio_level = description .hdrexts .iter() @@ -149,16 +157,79 @@ impl JingleSession { .iter() .find(|pt| pt.name.as_deref() == Some("H264")) .map(|pt| pt.id); + h264_rtx_payload_type = description + .payload_types + .iter() + .find(|pt| { + pt.name.as_deref() == Some("rtx") + && + pt + .parameters + .iter() + .any(|param| { + param.name == "apt" + && + param.value == h264_payload_type.map(|pt| pt.to_string()).unwrap_or_default() + }) + }) + .map(|pt| pt.id); + h264_rtcp_fbs = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("H264")) + .map(|pt| pt.rtcp_fbs.clone()); vp8_payload_type = description .payload_types .iter() .find(|pt| pt.name.as_deref() == Some("VP8")) .map(|pt| pt.id); + vp8_rtx_payload_type = description + .payload_types + .iter() + .find(|pt| { + pt.name.as_deref() == Some("rtx") + && + pt + .parameters + .iter() + .any(|param| { + param.name == "apt" + && + param.value == vp8_payload_type.map(|pt| pt.to_string()).unwrap_or_default() + }) + }) + .map(|pt| pt.id); + vp8_rtcp_fbs = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("VP8")) + .map(|pt| pt.rtcp_fbs.clone()); vp9_payload_type = description .payload_types .iter() .find(|pt| pt.name.as_deref() == Some("VP9")) .map(|pt| pt.id); + vp9_rtx_payload_type = description + .payload_types + .iter() + .find(|pt| { + pt.name.as_deref() == Some("rtx") + && + pt + .parameters + .iter() + .any(|param| { + param.name == "apt" + && + param.value == vp9_payload_type.map(|pt| pt.to_string()).unwrap_or_default() + }) + }) + .map(|pt| pt.id); + vp9_rtcp_fbs = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("VP9")) + .map(|pt| pt.rtcp_fbs.clone()); video_hdrext_abs_send_time = description .hdrexts .iter() @@ -257,9 +328,11 @@ impl JingleSession { let audio_ssrc: u32 = random(); let video_ssrc: u32 = random(); + let video_rtx_ssrc: u32 = random(); debug!("audio SSRC: {}", audio_ssrc); debug!("video SSRC: {}", video_ssrc); + debug!("video RTX SSRC: {}", video_rtx_ssrc); let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245); ice_agent.set_ice_tcp(false); @@ -389,7 +462,6 @@ impl JingleSession { let rtpbin = gstreamer::ElementFactory::make("rtpbin", Some("rtpbin"))?; rtpbin.set_property_from_str("rtp-profile", "savpf"); - rtpbin.set_property("do-retransmission", true)?; rtpbin.set_property("autoremove", true)?; pipeline.add(&rtpbin)?; @@ -407,6 +479,11 @@ impl JingleSession { let dtls_srtp_connection_id = "gst-meet"; + let dtlssrtpenc = gstreamer::ElementFactory::make("dtlssrtpenc", None)?; + dtlssrtpenc.set_property("connection-id", dtls_srtp_connection_id)?; + dtlssrtpenc.set_property("is-client", true)?; + pipeline.add(&dtlssrtpenc)?; + let dtlssrtpdec = gstreamer::ElementFactory::make("dtlssrtpdec", None)?; dtlssrtpdec.set_property("connection-id", dtls_srtp_connection_id)?; dtlssrtpdec.set_property( @@ -415,11 +492,6 @@ impl JingleSession { )?; pipeline.add(&dtlssrtpdec)?; - let dtlssrtpenc = gstreamer::ElementFactory::make("dtlssrtpenc", None)?; - dtlssrtpenc.set_property("connection-id", dtls_srtp_connection_id)?; - dtlssrtpenc.set_property("is-client", true)?; - pipeline.add(&dtlssrtpenc)?; - rtpbin.connect("request-pt-map", false, move |values| { let f = || { debug!("rtpbin request-pt-map {:?}", values); @@ -462,20 +534,20 @@ impl JingleSession { } Ok(Some(caps.build())) } - else if pt == RTX_PAYLOAD_TYPE_VP8 || pt == RTX_PAYLOAD_TYPE_VP9 || pt == RTX_PAYLOAD_TYPE_H264 { + 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 pt == RTX_PAYLOAD_TYPE_VP8 { + .field("apt", if Some(pt) == vp8_rtx_payload_type { vp8_payload_type .context("missing VP8 payload type")? } - else if pt == RTX_PAYLOAD_TYPE_VP9 { + else if Some(pt) == vp9_rtx_payload_type { vp9_payload_type .context("missing VP9 payload type")? } - else if pt == RTX_PAYLOAD_TYPE_H264 { + else if Some(pt) == h264_rtx_payload_type { h264_payload_type .context("missing H264 payload type")? } @@ -502,23 +574,64 @@ impl JingleSession { } })?; + let handle = Handle::current(); + let inner_ = conference.inner.clone(); + rtpbin.connect("new-jitterbuffer", false, move |values| { + let handle = handle.clone(); + let inner_ = inner_.clone(); + let f = move || { + let rtpjitterbuffer: gstreamer::Element = values[1].get()?; + let session: u32 = values[2].get()?; + let ssrc: u32 = values[3].get()?; + debug!("new jitterbuffer created for session {} ssrc {}", session, ssrc); + + let source = handle.block_on(async move { + let locked_inner = inner_.lock().await; + let jingle_session = locked_inner + .jingle_session + .as_ref() + .context("not connected (no jingle session)")?; + Ok::<_, anyhow::Error>( + jingle_session + .remote_ssrc_map + .get(&ssrc) + .context(format!("unknown ssrc: {}", ssrc))? + .clone(), + ) + })?; + debug!("jitterbuffer is for remote source: {:?}", source); + if source.media_type == MediaType::Video { + debug!("enabling RTX for ssrc {}", ssrc); + rtpjitterbuffer.set_property("do-retransmission", true)?; + } + Ok::<_, anyhow::Error>(()) + }; + if let Err(e) = f() { + warn!("new-jitterbuffer: {:?}", e); + } + None + })?; + rtpbin.connect("request-aux-sender", false, move |values| { let f = move || { 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"); - if let Some(pt) = vp8_payload_type { - pt_map = pt_map.field(&pt.to_string(), &(RTX_PAYLOAD_TYPE_VP8 as u32)); + let mut ssrc_map = gstreamer::Structure::builder("application/x-rtp-ssrc-map"); + 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) = vp9_payload_type { - pt_map = pt_map.field(&pt.to_string(), &(RTX_PAYLOAD_TYPE_VP9 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) = h264_payload_type { - pt_map = pt_map.field(&pt.to_string(), &(RTX_PAYLOAD_TYPE_H264 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( @@ -552,14 +665,14 @@ impl JingleSession { let session: u32 = values[1].get()?; debug!("creating RTX receiver for session {}", session); let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map"); - if let Some(pt) = vp8_payload_type { - pt_map = pt_map.field(&pt.to_string(), RTX_PAYLOAD_TYPE_VP8 as u32); + 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) = vp9_payload_type { - pt_map = pt_map.field(&pt.to_string(), RTX_PAYLOAD_TYPE_VP9 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) = h264_payload_type { - pt_map = pt_map.field(&pt.to_string(), RTX_PAYLOAD_TYPE_H264 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 rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?; @@ -800,28 +913,34 @@ impl JingleSession { video_capsfilter.set_property("caps", video_caps.build())?; pipeline.add(&video_capsfilter)?; - debug!("linking audio payloader -> rtpbin"); + let rtpfunnel = gstreamer::ElementFactory::make("funnel", None)?; + pipeline.add(&rtpfunnel)?; + + debug!("linking audio payloader -> rtp funnel"); audio_sink_element.link(&audio_capsfilter)?; - audio_capsfilter.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?; + audio_capsfilter.link_pads(None, &rtpfunnel, Some("sink_0"))?; - debug!("linking video payloader -> rtpbin"); + debug!("linking video payloader -> rtp funnel"); video_sink_element.link(&video_capsfilter)?; - video_capsfilter.link_pads(None, &rtpbin, Some("send_rtp_sink_1"))?; + video_capsfilter.link_pads(None, &rtpfunnel, Some("sink_1"))?; - debug!("linking ICE <-> DTLS-SRTP"); - nicesrc.link(&dtlssrtpdec)?; - dtlssrtpenc.link(&nicesink)?; + debug!("linking rtp funnel -> rtpbin"); + rtpfunnel.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?; - debug!("linking rtpbin -> DTLS-SRTP encoder"); - rtpbin.link_pads(Some("send_rtp_src_0"), &dtlssrtpenc, Some("rtp_sink_0"))?; - rtpbin.link_pads(Some("send_rtcp_src_0"), &dtlssrtpenc, Some("rtcp_sink_0"))?; - rtpbin.link_pads(Some("send_rtp_src_1"), &dtlssrtpenc, Some("rtp_sink_1"))?; - rtpbin.link_pads(Some("send_rtcp_src_1"), &dtlssrtpenc, Some("rtcp_sink_1"))?; - - debug!("linking DTLS-SRTP decoder -> rtpbin"); + debug!("link dtlssrtpdec -> rtpbin"); dtlssrtpdec.link_pads(Some("rtp_src"), &rtpbin, Some("recv_rtp_sink_0"))?; dtlssrtpdec.link_pads(Some("rtcp_src"), &rtpbin, Some("recv_rtcp_sink_0"))?; + debug!("linking rtpbin -> dtlssrtpenc"); + rtpbin.link_pads(Some("send_rtp_src_0"), &dtlssrtpenc, Some("rtp_sink_0"))?; + rtpbin.link_pads(Some("send_rtcp_src_0"), &dtlssrtpenc, Some("rtcp_sink_0"))?; + + debug!("linking ice src -> dtlssrtpdec"); + nicesrc.link(&dtlssrtpdec)?; + + debug!("linking dtlssrtpenc -> ice sink"); + dtlssrtpenc.link_pads(Some("src"), &nicesink, Some("sink"))?; + let bus = pipeline.bus().context("failed to get pipeline bus")?; let (pipeline_state_null_tx, pipeline_state_null_rx) = oneshot::channel(); @@ -875,35 +994,76 @@ impl JingleSession { let mut description = RtpDescription::new(initiate_content.name.0.clone()); description.payload_types = if initiate_content.name.0 == "audio" { - vec![PayloadType::new( + let mut pt = PayloadType::new( opus_payload_type.context("no opus payload type in jingle session-initiate")?, "opus".to_owned(), 48000, 2, - )] + ); + pt.rtcp_fbs = opus_rtcp_fbs.clone().unwrap_or_default(); + vec![pt] } else { + let mut pts = vec![]; match conference.config.video_codec.as_str() { - "h264" => vec![PayloadType::new( - h264_payload_type.context("no h264 payload type in jingle session-initiate")?, - "H264".to_owned(), - 90000, - 1, - )], - "vp8" => vec![PayloadType::new( - vp8_payload_type.context("no vp8 payload type in jingle session-initiate")?, - "VP8".to_owned(), - 90000, - 1, - )], - "vp9" => vec![PayloadType::new( - vp9_payload_type.context("no vp9 payload type in jingle session-initiate")?, - "VP9".to_owned(), - 90000, - 1, - )], + "h264" => { + if let Some(h264_pt) = h264_payload_type { + let mut pt = PayloadType::new(h264_pt, "H264".to_owned(), 90000, 1); + pt.rtcp_fbs = h264_rtcp_fbs.clone().unwrap_or_default(); + pts.push(pt); + if let Some(rtx_pt) = h264_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: h264_pt.to_string(), + }]; + pts.push(rtx_pt); + } + } + else { + bail!("no h264 payload type in jingle session-initiate"); + } + }, + "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 }; description.rtcp_mux = Some(RtcpMux); @@ -912,11 +1072,21 @@ impl JingleSession { let label = Uuid::new_v4().to_string(); let cname = Uuid::new_v4().to_string(); + description.ssrc = Some(if initiate_content.name.0 == "audio" { + audio_ssrc.to_string() + } + else { + video_ssrc.to_string() + }); + description.ssrcs = if initiate_content.name.0 == "audio" { vec![jingle_ssma::Source::new(audio_ssrc.to_string())] } else { - vec![jingle_ssma::Source::new(video_ssrc.to_string())] + vec![ + jingle_ssma::Source::new(video_ssrc.to_string()), + jingle_ssma::Source::new(video_rtx_ssrc.to_string()), + ] }; for ssrc in description.ssrcs.iter_mut() { @@ -928,16 +1098,21 @@ impl JingleSession { name: "msid".to_owned(), value: Some(format!("{} {}", mslabel, label)), }); - ssrc.parameters.push(Parameter { - name: "mslabel".to_owned(), - value: Some(mslabel.clone()), - }); - ssrc.parameters.push(Parameter { - name: "label".to_owned(), - value: Some(label.clone()), - }); } + description.ssrc_groups = if initiate_content.name.0 == "audio" { + vec![] + } + else { + vec![jingle_ssma::Group { + semantics: "FID".to_owned(), + sources: vec![ + jingle_ssma::Source::new(video_ssrc.to_string()), + jingle_ssma::Source::new(video_rtx_ssrc.to_string()), + ], + }] + }; + if initiate_content.name.0 == "audio" { // TODO: fails to negotiate // if let Some(hdrext) = audio_hdrext_ssrc_audio_level {