diff --git a/rust/gst-nmos-rs/src/sdp.rs b/rust/gst-nmos-rs/src/sdp.rs index f5b616d..aaba44e 100644 --- a/rust/gst-nmos-rs/src/sdp.rs +++ b/rust/gst-nmos-rs/src/sdp.rs @@ -160,7 +160,7 @@ pub(crate) mod defaults { /// for unicast destinations (pinned by /// `gst_sdp_strips_ttl_for_unicast_c_lines`), so this /// constant only takes effect for multicast `c=` lines. - pub(crate) const MULTICAST_TTL: u32 = 64; + pub(crate) const MULTICAST_TTL: u32 = 32; /// RTP clock rate for RFC 4175 video (§6.1 `rate`). /// Fixed at 90 kHz regardless of frame rate, depth, or @@ -946,7 +946,7 @@ pub(crate) fn resource_name_from_transport(text: &str) -> Result, /// when no `t=` block was added /// a=x-nvnmos-name: ← if Some (session-level) /// m= RTP/AVP -/// c=IN IP4 /64 +/// c=IN IP4 /32 /// a=rtpmap: ... ← from `set_media_from_caps` /// a=fmtp: ... ← from `set_media_from_caps` /// a=ts-refclk:ptp=IEEE1588-2008:traceable ← if session.emit_ptp_ts_refclk @@ -2843,8 +2843,8 @@ mod tests { } #[test] - fn defaults_multicast_ttl_is_64() { - assert_eq!(defaults::MULTICAST_TTL, 64); + fn defaults_multicast_ttl_is_32() { + assert_eq!(defaults::MULTICAST_TTL, 32); } /// Pins `gst-sdp`'s `SDPMedia::add_connection` behaviour so diff --git a/rust/gst-nmos-rs/src/sdp_passthrough.rs b/rust/gst-nmos-rs/src/sdp_passthrough.rs index 5bf91c2..1384d65 100644 --- a/rust/gst-nmos-rs/src/sdp_passthrough.rs +++ b/rust/gst-nmos-rs/src/sdp_passthrough.rs @@ -267,10 +267,10 @@ fn replace_connection_address(m: &mut SDPMediaRef, address: &str) -> Result<(), let conn = m.connection(0).ok_or(SdpError::MissingConnection)?; let nettype = conn.nettype().unwrap_or("IN"); let addrtype = conn.addrtype().unwrap_or("IP4"); - let ttl = if is_multicast_address(address) { - defaults::MULTICAST_TTL - } else { - conn.ttl() + let ttl = match (is_multicast_address(address), conn.ttl()) { + (true, 0) => defaults::MULTICAST_TTL, + (true, ttl) => ttl, + (false, _) => 0, }; let new_conn = SDPConnection::new(nettype, addrtype, address, ttl, conn.addr_number()); m.replace_connection(0, new_conn) @@ -448,4 +448,57 @@ mod tests { "unresolvable override must drop stale iface: {out}", ); } + + #[test] + fn multicast_destination_override_preserves_nonzero_ttl() { + init_gst(); + let input = VIDEO_WITH_STALE_IFACE.replace("239.1.1.1/64", "239.1.1.1/127"); + let overrides = SdpOverrides { + destination_ip: Some("239.1.1.2"), + ..Default::default() + }; + let out = + passthrough_with_overrides(&input, &overrides, DualLegPassthroughPolicy::RejectDualLeg) + .expect("splice"); + assert!( + out.contains("c=IN IP4 239.1.1.2/127"), + "multicast override must preserve a non-zero TTL: {out}", + ); + } + + #[test] + fn unicast_to_multicast_destination_override_uses_default_ttl() { + init_gst(); + let input = VIDEO_WITH_STALE_IFACE.replace("239.1.1.1/64", "192.0.2.20"); + let overrides = SdpOverrides { + destination_ip: Some("239.1.1.2"), + ..Default::default() + }; + let out = + passthrough_with_overrides(&input, &overrides, DualLegPassthroughPolicy::RejectDualLeg) + .expect("splice"); + assert!( + out.contains(&format!("c=IN IP4 239.1.1.2/{}", defaults::MULTICAST_TTL)), + "multicast override must supply the default TTL: {out}", + ); + } + + #[test] + fn multicast_to_unicast_destination_override_omits_ttl() { + init_gst(); + let overrides = SdpOverrides { + destination_ip: Some("192.0.2.20"), + ..Default::default() + }; + let out = passthrough_with_overrides( + VIDEO_WITH_STALE_IFACE, + &overrides, + DualLegPassthroughPolicy::RejectDualLeg, + ) + .expect("splice"); + assert!( + out.contains("c=IN IP4 192.0.2.20\r\n"), + "unicast override must omit the TTL suffix: {out}", + ); + } } diff --git a/src/nvnmos_impl.cpp b/src/nvnmos_impl.cpp index b7f25b3..6fc3177 100644 --- a/src/nvnmos_impl.cpp +++ b/src/nvnmos_impl.cpp @@ -1224,6 +1224,12 @@ namespace nvnmos sdp_params.origin.session_version = utility::ostringstreamed(sdp::ntp_now() >> 32); auto& transport_params = nmos::fields::transport_params(nmos::fields::endpoint_active(connection_sender.data)); + if (0 == sdp_params.connection_data.ttl) + { + // match the nmos::sdp_parameters value-constructor default; ignored by nmos::make_session_description + // for IPv4 unicast addresses + sdp_params.connection_data.ttl = 32; + } // use nmos::make_session_description rather than impl::make_session_description for /transportfile // because e.g. the custom SDP attributes in nvnmos::attributes are only for 'internal' use