@@ -158,11 +158,13 @@ impl PeerTransport {
158158 Some ( start_kbps. min ( ultimate_kbps) )
159159 }
160160
161- /// Munge SDP to change a=inactive to a=recvonly for audio m-lines in single PC mode
162- /// This is needed because WebRTC sometimes generates inactive direction even when
163- /// we set the transceiver to recvonly
164- /// We only fix the FIRST inactive audio m-line (the one we need for receiving)
165- fn munge_inactive_to_recvonly_for_audio ( sdp : & str ) -> String {
161+ /// Munge SDP to change a=inactive to a=recvonly for RTP media m-lines in single PC mode.
162+ /// This is needed because WebRTC can generate inactive direction even when transceivers
163+ /// were configured as recvonly.
164+ ///
165+ /// We intentionally limit this to RTP m-sections, so non-RTP sections (for example
166+ /// data-channel `m=application` sections) are not rewritten.
167+ fn munge_inactive_to_recvonly_for_media ( sdp : & str ) -> String {
166168 // Detect what line ending the original SDP uses
167169 let uses_crlf = sdp. contains ( "\r \n " ) ;
168170 let eol = if uses_crlf { "\r \n " } else { "\n " } ;
@@ -171,23 +173,23 @@ impl PeerTransport {
171173 if uses_crlf { sdp. split ( "\r \n " ) . collect ( ) } else { sdp. split ( '\n' ) . collect ( ) } ;
172174
173175 let mut out: Vec < String > = Vec :: with_capacity ( lines. len ( ) ) ;
174- let mut in_audio_section = false ;
175- let mut fixed_one = false ;
176+ let mut in_rtp_media_section = false ;
176177
177178 for line in lines {
178179 let l = line. trim ( ) ;
179180
180- // Track which media section we're in
181- if l. starts_with ( "m=audio" ) {
182- in_audio_section = true ;
183- } else if l. starts_with ( "m=" ) {
184- in_audio_section = false ;
181+ // Track whether the current m-section is RTP-based.
182+ if l. starts_with ( "m=" ) {
183+ // Example RTP m-line:
184+ // m=audio 9 UDP/TLS/RTP/SAVPF 111
185+ // Example data channel m-line:
186+ // m=application 9 UDP/DTLS/SCTP webrtc-datachannel
187+ in_rtp_media_section = l. contains ( "RTP/" ) ;
185188 }
186189
187- // Change a= inactive to a= recvonly for the first inactive audio section only
188- if in_audio_section && l == "a=inactive" && !fixed_one {
190+ // Change inactive to recvonly for RTP media m-sections.
191+ if in_rtp_media_section && l == "a=inactive" {
189192 out. push ( "a=recvonly" . to_string ( ) ) ;
190- fixed_one = true ;
191193 } else {
192194 out. push ( line. to_string ( ) ) ;
193195 }
@@ -352,9 +354,9 @@ impl PeerTransport {
352354 let mut sdp = offer. to_string ( ) ;
353355
354356 if inner. single_pc_mode {
355- // Fix inactive audio m-lines to recvonly for single PC mode
356- // WebRTC sometimes generates a=inactive even when transceiver is set to recvonly
357- let recvonly_munged = Self :: munge_inactive_to_recvonly_for_audio ( & sdp) ;
357+ // Fix inactive media m-lines to recvonly for single PC mode.
358+ // WebRTC can generate a=inactive even when transceivers are recvonly.
359+ let recvonly_munged = Self :: munge_inactive_to_recvonly_for_media ( & sdp) ;
358360 if recvonly_munged != sdp {
359361 match SessionDescription :: parse ( & recvonly_munged, offer. sdp_type ( ) ) {
360362 Ok ( parsed) => {
@@ -542,7 +544,7 @@ a=fmtp:98 profile-id=0;x-google-start-bitrate=1000\n";
542544 }
543545
544546 #[ test]
545- fn inactive_audio_is_munged_to_recvonly_once ( ) {
547+ fn inactive_media_is_munged_to_recvonly_for_all_rtp_sections ( ) {
546548 let sdp = "v=0\n \
547549 o=- 0 0 IN IP4 127.0.0.1\n \
548550 s=-\n \
@@ -552,12 +554,30 @@ a=inactive\n\
552554 a=rtpmap:111 opus/48000/2\n \
553555 m=video 9 UDP/TLS/RTP/SAVPF 96\n \
554556 a=inactive\n \
557+ m=text 9 UDP/TLS/RTP/SAVPF 98\n \
558+ a=inactive\n \
559+ m=audio 9 UDP/TLS/RTP/SAVPF 111\n \
560+ a=inactive\n ";
561+ let out = PeerTransport :: munge_inactive_to_recvonly_for_media ( sdp) ;
562+ assert ! ( out. contains( "m=audio 9 UDP/TLS/RTP/SAVPF 111\n a=recvonly\n " ) ) ;
563+ assert ! ( out. contains( "m=text 9 UDP/TLS/RTP/SAVPF 98\n a=recvonly\n " ) ) ;
564+ assert_eq ! ( out. matches( "a=recvonly" ) . count( ) , 4 ) ;
565+ assert_eq ! ( out. matches( "a=inactive" ) . count( ) , 0 ) ;
566+ }
567+
568+ #[ test]
569+ fn inactive_application_section_is_not_munged ( ) {
570+ let sdp = "v=0\n \
571+ o=- 0 0 IN IP4 127.0.0.1\n \
572+ s=-\n \
573+ t=0 0\n \
555574 m=audio 9 UDP/TLS/RTP/SAVPF 111\n \
575+ a=inactive\n \
576+ m=application 9 UDP/DTLS/SCTP webrtc-datachannel\n \
556577 a=inactive\n ";
557- let out = PeerTransport :: munge_inactive_to_recvonly_for_audio ( sdp) ;
578+ let out = PeerTransport :: munge_inactive_to_recvonly_for_media ( sdp) ;
558579 assert ! ( out. contains( "m=audio 9 UDP/TLS/RTP/SAVPF 111\n a=recvonly\n " ) ) ;
559- assert_eq ! ( out. matches( "a=recvonly" ) . count( ) , 1 ) ;
560- assert_eq ! ( out. matches( "a=inactive" ) . count( ) , 2 ) ;
580+ assert ! ( out. contains( "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\n a=inactive\n " ) ) ;
561581 }
562582
563583 #[ test]
0 commit comments