Add support for logging RTP/RTCP packets

This commit is contained in:
Jasper Hugo 2022-03-07 10:04:30 +07:00
parent 43afe4696a
commit d44546e8c6
8 changed files with 172 additions and 51 deletions

70
Cargo.lock generated
View File

@ -683,6 +683,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "ipnet"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.3" version = "0.10.3"
@ -762,6 +768,7 @@ dependencies = [
"rand", "rand",
"rcgen", "rcgen",
"ring", "ring",
"rtcp",
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"serde", "serde",
@ -1063,6 +1070,17 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.5",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.0" version = "0.12.0"
@ -1070,7 +1088,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core", "parking_lot_core 0.9.1",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
] ]
[[package]] [[package]]
@ -1272,6 +1304,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rtcp"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d9625e47edb43aca711ec826ad12154d364ada9e60f4e6f8d40471b3e1e156"
dependencies = [
"bytes",
"thiserror",
"webrtc-util",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.20.4" version = "0.20.4"
@ -1653,6 +1696,7 @@ dependencies = [
"mio", "mio",
"num_cpus", "num_cpus",
"once_cell", "once_cell",
"parking_lot 0.12.0",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
@ -1781,7 +1825,7 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
dependencies = [ dependencies = [
"parking_lot", "parking_lot 0.12.0",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
@ -1996,6 +2040,28 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "webrtc-util"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b69bdea720881eddee50bd969be052ed95f04ccc5a6f684495131afb87e31c"
dependencies = [
"async-trait",
"bitflags",
"bytes",
"cc",
"ipnet",
"lazy_static",
"libc",
"log",
"nix",
"parking_lot 0.11.2",
"rand",
"thiserror",
"tokio",
"winapi",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View File

@ -23,6 +23,7 @@ cocoa = { version = "0.24", default-features = false }
[features] [features]
default = ["tls-rustls-native-roots"] default = ["tls-rustls-native-roots"]
log-rtp = ["lib-gst-meet/log-rtp"]
tls-insecure = ["lib-gst-meet/tls-insecure"] tls-insecure = ["lib-gst-meet/tls-insecure"]
tls-native = ["lib-gst-meet/tls-native"] tls-native = ["lib-gst-meet/tls-native"]
tls-native-vendored = ["lib-gst-meet/tls-native-vendored"] tls-native-vendored = ["lib-gst-meet/tls-native-vendored"]

View File

@ -58,6 +58,12 @@ struct Opt {
help = "Disable TLS certificate verification (use with extreme caution)" help = "Disable TLS certificate verification (use with extreme caution)"
)] )]
tls_insecure: bool, tls_insecure: bool,
#[cfg(feature = "log-rtp")]
#[structopt(
long,
help = "Log all RTP/RTCP packets at DEBUG level"
)]
log_rtp: bool,
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
@ -154,6 +160,8 @@ async fn main_inner() -> Result<()> {
region, region,
video_codec, video_codec,
recv_pipeline_participant_template, recv_pipeline_participant_template,
#[cfg(feature = "log-rtp")]
log_rtp,
.. ..
} = opt; } = opt;
@ -164,6 +172,10 @@ async fn main_inner() -> Result<()> {
region, region,
video_codec, video_codec,
extra_muc_features: vec![], extra_muc_features: vec![],
start_bitrate: 800,
stereo: false,
#[cfg(feature = "log-rtp")]
log_rtp,
}; };
let main_loop = glib::MainLoop::new(None, false); let main_loop = glib::MainLoop::new(None, false);

View File

@ -17,3 +17,7 @@ tracing = { version = "0.1", default-features = false }
[lib] [lib]
name = "gstmeet" name = "gstmeet"
crate_type = ["staticlib", "cdylib"] crate_type = ["staticlib", "cdylib"]
[features]
default = []
log-rtp = ["lib-gst-meet/log-rtp"]

View File

@ -156,6 +156,13 @@ pub unsafe extern "C" fn gstmeet_connection_join_conference(
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
extra_muc_features: vec![], extra_muc_features: vec![],
// TODO
start_bitrate: 800,
stereo: false,
#[cfg(feature = "log-rtp")]
log_rtp: false,
}; };
(*context) (*context)
.runtime .runtime

View File

@ -32,6 +32,7 @@ pem = { version = "1", default-features = false }
rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] }
rcgen = { version = "0.9", default-features = false } rcgen = { version = "0.9", default-features = false }
ring = { version = "0.16", default-features = false } ring = { version = "0.16", default-features = false }
rtcp = { version = "0.6", default-features = false, optional = true }
rustls = { version = "0.20", default-features = false, features = ["logging", "tls12"], optional = true } rustls = { version = "0.20", default-features = false, features = ["logging", "tls12"], optional = true }
rustls-native-certs = { version = "0.6", default-features = false, optional = true } rustls-native-certs = { version = "0.6", default-features = false, optional = true }
serde = { version = "1", default-features = false, features = ["derive"] } serde = { version = "1", default-features = false, features = ["derive"] }
@ -55,6 +56,7 @@ xmpp-parsers = { git = "https://gitlab.com/xmpp-rs/xmpp-rs.git", default-feature
# Ideally we would enable rustls/dangerous_configuration only when tls-insecure is enabled, but until weak-dep-features is stabilised, that # Ideally we would enable rustls/dangerous_configuration only when tls-insecure is enabled, but until weak-dep-features is stabilised, that
# would cause rustls to always be pulled in. # would cause rustls to always be pulled in.
default = ["tls-rustls-native-roots"] default = ["tls-rustls-native-roots"]
log-rtp = ["rtcp"]
tls-insecure = [] tls-insecure = []
tls-native = ["tokio-tungstenite/native-tls", "native-tls"] tls-native = ["tokio-tungstenite/native-tls", "native-tls"]
tls-native-vendored = ["tokio-tungstenite/native-tls-vendored", "native-tls/vendored"] tls-native-vendored = ["tokio-tungstenite/native-tls-vendored", "native-tls/vendored"]

View File

@ -75,6 +75,10 @@ pub struct JitsiConferenceConfig {
pub region: Option<String>, pub region: Option<String>,
pub video_codec: String, pub video_codec: String,
pub extra_muc_features: Vec<String>, pub extra_muc_features: Vec<String>,
pub start_bitrate: u32,
pub stereo: bool,
#[cfg(feature = "log-rtp")]
pub log_rtp: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -140,10 +144,8 @@ impl JitsiConference {
machine_uid: Uuid::new_v4().to_string(), machine_uid: Uuid::new_v4().to_string(),
room: config.muc.to_string(), room: config.muc.to_string(),
properties: hashmap! { properties: hashmap! {
// Disable voice processing "stereo".to_string() => config.stereo.to_string(),
// TODO put this in config "startBitrate".to_string() => config.start_bitrate.to_string(),
"stereo".to_string() => "true".to_string(),
"startBitrate".to_string() => "800".to_string(),
}, },
}; };

View File

@ -5,6 +5,8 @@ use futures::stream::StreamExt;
use glib::{ObjectExt, ToValue}; use glib::{ObjectExt, ToValue};
use gstreamer::prelude::{ElementExt, GObjectExtManualGst, GstBinExt, PadExt}; use gstreamer::prelude::{ElementExt, GObjectExtManualGst, GstBinExt, PadExt};
use gstreamer_rtp::{prelude::RTPHeaderExtensionExt, RTPHeaderExtension}; use gstreamer_rtp::{prelude::RTPHeaderExtensionExt, RTPHeaderExtension};
#[cfg(feature = "log-rtp")]
use gstreamer_rtp::RTPBuffer;
use jitsi_xmpp_parsers::{ use jitsi_xmpp_parsers::{
jingle::{Action, Content, Description, Jingle, Transport}, jingle::{Action, Content, Description, Jingle, Transport},
jingle_dtls_srtp::Fingerprint, jingle_dtls_srtp::Fingerprint,
@ -126,7 +128,6 @@ impl Codec {
struct ParsedRtpDescription { struct ParsedRtpDescription {
codecs: Vec<Codec>, codecs: Vec<Codec>,
audio_hdrext_ssrc_audio_level: Option<u16>, audio_hdrext_ssrc_audio_level: Option<u16>,
audio_hdrext_transport_cc: Option<u16>,
video_hdrext_transport_cc: Option<u16>, video_hdrext_transport_cc: Option<u16>,
} }
@ -190,7 +191,6 @@ impl JingleSession {
let mut vp8 = None; let mut vp8 = None;
let mut vp9 = None; let mut vp9 = 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 video_hdrext_transport_cc = None; let mut video_hdrext_transport_cc = None;
if description.media == "audio" { if description.media == "audio" {
@ -209,9 +209,6 @@ impl JingleSession {
if hdrext.uri == RTP_HDREXT_SSRC_AUDIO_LEVEL { if hdrext.uri == RTP_HDREXT_SSRC_AUDIO_LEVEL {
audio_hdrext_ssrc_audio_level = Some(hdrext.id); audio_hdrext_ssrc_audio_level = Some(hdrext.id);
} }
else if hdrext.uri == RTP_HDREXT_TRANSPORT_CC {
audio_hdrext_transport_cc = Some(hdrext.id);
}
} }
} }
else if description.media == "video" { else if description.media == "video" {
@ -274,8 +271,6 @@ impl JingleSession {
} }
} }
for hdrext in description.hdrexts.iter() { for hdrext in description.hdrexts.iter() {
// TODO: .parse::<u8>() wont be needed after updating xmpp-parsers, it is now a u16 as
// defined in the XEP and related RFC.
if hdrext.uri == RTP_HDREXT_TRANSPORT_CC { if hdrext.uri == RTP_HDREXT_TRANSPORT_CC {
video_hdrext_transport_cc = Some(hdrext.id); video_hdrext_transport_cc = Some(hdrext.id);
} }
@ -325,7 +320,6 @@ impl JingleSession {
Ok(Some(ParsedRtpDescription { Ok(Some(ParsedRtpDescription {
codecs, codecs,
audio_hdrext_ssrc_audio_level, audio_hdrext_ssrc_audio_level,
audio_hdrext_transport_cc,
video_hdrext_transport_cc, video_hdrext_transport_cc,
})) }))
} }
@ -467,7 +461,6 @@ impl JingleSession {
let mut ice_transport = None; let mut ice_transport = None;
let mut codecs = vec![]; let mut codecs = vec![];
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 video_hdrext_transport_cc = None; let mut video_hdrext_transport_cc = None;
let mut remote_ssrc_map = HashMap::new(); let mut remote_ssrc_map = HashMap::new();
@ -480,8 +473,6 @@ impl JingleSession {
codecs.extend(description.codecs); codecs.extend(description.codecs);
audio_hdrext_ssrc_audio_level = audio_hdrext_ssrc_audio_level =
audio_hdrext_ssrc_audio_level.or(description.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);
video_hdrext_transport_cc = video_hdrext_transport_cc =
video_hdrext_transport_cc.or(description.video_hdrext_transport_cc); video_hdrext_transport_cc.or(description.video_hdrext_transport_cc);
} }
@ -592,9 +583,6 @@ impl JingleSession {
if let Some(hdrext) = audio_hdrext_ssrc_audio_level { if let Some(hdrext) = audio_hdrext_ssrc_audio_level {
caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL); caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL);
} }
if let Some(hdrext) = audio_hdrext_transport_cc {
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
}
} }
else { else {
// A video codec, as the only audio codec we support is Opus. // A video codec, as the only audio codec we support is Opus.
@ -829,14 +817,6 @@ impl JingleSession {
let hdrext = RTPHeaderExtension::create_from_uri(&ext_uri) let hdrext = RTPHeaderExtension::create_from_uri(&ext_uri)
.context("failed to create hdrext")?; .context("failed to create hdrext")?;
hdrext.set_id(ext_id); hdrext.set_id(ext_id);
// if ext_uri == RTP_HDREXT_SSRC_AUDIO_LEVEL {
// }
// else if ext_uri == RTP_HDREXT_TRANSPORT_CC {
// // hdrext.set_property("n-streams", 2u32);
// }
// else {
// bail!("unknown rtp hdrext: {}", ext_uri);
// };
Ok::<_, anyhow::Error>(hdrext) Ok::<_, anyhow::Error>(hdrext)
}; };
match f() { match f() {
@ -941,14 +921,6 @@ impl JingleSession {
let hdrext = let hdrext =
RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?; RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?;
hdrext.set_id(ext_id); hdrext.set_id(ext_id);
// if ext_uri == RTP_HDREXT_SSRC_AUDIO_LEVEL {
// }
// else if ext_uri == RTP_HDREXT_TRANSPORT_CC {
// // hdrext.set_property("n-streams", 2u32);
// }
// else {
// bail!("unknown rtp hdrext: {}", ext_uri);
// }
Ok::<_, anyhow::Error>(hdrext) Ok::<_, anyhow::Error>(hdrext)
}; };
match f() { match f() {
@ -995,12 +967,6 @@ impl JingleSession {
let hdrext = let hdrext =
RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?; RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?;
hdrext.set_id(ext_id); hdrext.set_id(ext_id);
if ext_uri == RTP_HDREXT_TRANSPORT_CC {
// hdrext.set_property("n-streams", 2u32);
}
else {
bail!("unknown rtp hdrext: {}", ext_uri);
}
Ok::<_, anyhow::Error>(hdrext) Ok::<_, anyhow::Error>(hdrext)
}; };
match f() { match f() {
@ -1029,13 +995,79 @@ impl JingleSession {
debug!("linking rtpfunnel -> rtpbin"); debug!("linking rtpfunnel -> rtpbin");
rtpfunnel.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?; rtpfunnel.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?;
let rtp_recv_identity = gstreamer::ElementFactory::make("identity", None)?;
pipeline.add(&rtp_recv_identity)?;
let rtcp_recv_identity = gstreamer::ElementFactory::make("identity", None)?;
pipeline.add(&rtcp_recv_identity)?;
let rtp_send_identity = gstreamer::ElementFactory::make("identity", None)?;
pipeline.add(&rtp_send_identity)?;
let rtcp_send_identity = gstreamer::ElementFactory::make("identity", None)?;
pipeline.add(&rtcp_send_identity)?;
#[cfg(feature = "log-rtp")]
if conference.config.log_rtp {
let make_rtp_logger = |direction: &'static str| {
move |values: &[glib::Value]| -> Option<glib::Value> {
let f = || {
let buffer: gstreamer::Buffer = values[1].get()?;
let rtp_buffer = RTPBuffer::from_buffer_readable(&buffer)?;
debug!(
ssrc=rtp_buffer.ssrc(),
seq=rtp_buffer.seq(),
ts=rtp_buffer.timestamp(),
marker=rtp_buffer.is_marker(),
payload_size=rtp_buffer.payload_size(),
"RTP {}",
direction,
);
Ok::<_, anyhow::Error>(())
};
if let Err(e) = f() {
warn!("RTP {}: {:?}", direction, e);
}
None
}
};
let make_rtcp_logger = |direction: &'static str| {
move |values: &[glib::Value]| -> Option<glib::Value> {
let f = || {
let buffer: gstreamer::Buffer = values[1].get()?;
let mut buf = [0u8; 1500];
buffer.copy_to_slice(0, &mut buf[..buffer.size()]).map_err(|_| anyhow!("invalid RTCP packet size"))?;
let decoded = rtcp::packet::unmarshal(&mut &buf[..buffer.size()])?;
debug!(
"RTCP {} size={}\n{:?}",
direction,
buffer.size(),
decoded,
);
Ok::<_, anyhow::Error>(())
};
if let Err(e) = f() {
warn!("RTCP {}: {:?}", direction, e);
}
None
}
};
rtp_recv_identity.connect("handoff", false, make_rtp_logger("RECV"));
rtp_send_identity.connect("handoff", false, make_rtp_logger("SEND"));
rtcp_recv_identity.connect("handoff", false, make_rtcp_logger("RECV"));
rtcp_send_identity.connect("handoff", false, make_rtcp_logger("SEND"));
}
debug!("link dtlssrtpdec -> rtpbin"); debug!("link dtlssrtpdec -> rtpbin");
dtlssrtpdec.link_pads(Some("rtp_src"), &rtpbin, Some("recv_rtp_sink_0"))?; dtlssrtpdec.link_pads(Some("rtp_src"), &rtp_recv_identity, None)?;
dtlssrtpdec.link_pads(Some("rtcp_src"), &rtpbin, Some("recv_rtcp_sink_0"))?; rtp_recv_identity.link_pads(None, &rtpbin, Some("recv_rtp_sink_0"))?;
dtlssrtpdec.link_pads(Some("rtcp_src"), &rtcp_recv_identity, None)?;
rtcp_recv_identity.link_pads(None, &rtpbin, Some("recv_rtcp_sink_0"))?;
debug!("linking rtpbin -> dtlssrtpenc"); debug!("linking rtpbin -> dtlssrtpenc");
rtpbin.link_pads(Some("send_rtp_src_0"), &dtlssrtpenc, Some("rtp_sink_0"))?; rtpbin.link_pads(Some("send_rtp_src_0"), &rtp_send_identity, None)?;
rtpbin.link_pads(Some("send_rtcp_src_0"), &dtlssrtpenc, Some("rtcp_sink_0"))?; rtp_send_identity.link_pads(None, &dtlssrtpenc, Some("rtp_sink_0"))?;
rtpbin.link_pads(Some("send_rtcp_src_0"), &rtcp_send_identity, None)?;
rtcp_send_identity.link_pads(None, &dtlssrtpenc, Some("rtcp_sink_0"))?;
debug!("linking ice src -> dtlssrtpdec"); debug!("linking ice src -> dtlssrtpdec");
nicesrc.link(&dtlssrtpdec)?; nicesrc.link(&dtlssrtpdec)?;
@ -1183,11 +1215,6 @@ impl JingleSession {
RTP_HDREXT_SSRC_AUDIO_LEVEL.to_owned(), RTP_HDREXT_SSRC_AUDIO_LEVEL.to_owned(),
)); ));
} }
if let Some(hdrext) = audio_hdrext_transport_cc {
description
.hdrexts
.push(RtpHdrext::new(hdrext, RTP_HDREXT_TRANSPORT_CC.to_owned()));
}
} }
else if initiate_content.name.0 == "video" { else if initiate_content.name.0 == "video" {
if let Some(hdrext) = video_hdrext_transport_cc { if let Some(hdrext) = video_hdrext_transport_cc {