github.com/livekit/protocol@v1.39.3/sdp/sdp_test.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sdp 16 17 import ( 18 "testing" 19 20 pionsdp "github.com/pion/sdp/v3" 21 "github.com/pion/webrtc/v4" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestSDPUtilFunctions(t *testing.T) { 26 sdp := "v=0\r\no=- 4648475892259889561 3 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=ice-ufrag:1hhfzwf0ijpzm\r\na=ice-pwd:jm5puo2ab1op3vs59ca53bdk7s\r\na=fingerprint:sha-256 40:42:FB:47:87:52:BF:CB:EC:3A:DF:EB:06:DA:2D:B7:2F:59:42:10:23:7B:9D:4C:C9:58:DD:FF:A2:8F:17:67\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:0\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:96 H264/90000\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 goog-remb\r\na=fmtp:96 packetization-mode=1;profile-level-id=42e01f\r\na=ssrc:1505338584 cname:10000000b5810aac\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:1\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=ssrc:697641945 cname:10000000b5810aac\r\n" 27 sd := webrtc.SessionDescription{ 28 Type: webrtc.SDPTypeOffer, 29 SDP: sdp, 30 } 31 parsed, err := sd.Unmarshal() 32 require.NoError(t, err) 33 34 require.Equal(t, "0", GetMidValue(parsed.MediaDescriptions[0])) 35 require.Equal(t, "1", GetMidValue(parsed.MediaDescriptions[1])) 36 37 fp, alg, err := ExtractFingerprint(parsed) 38 require.NoError(t, err) 39 require.Equal(t, "40:42:FB:47:87:52:BF:CB:EC:3A:DF:EB:06:DA:2D:B7:2F:59:42:10:23:7B:9D:4C:C9:58:DD:FF:A2:8F:17:67", fp) 40 require.Equal(t, "sha-256", alg) 41 42 dtlsRole := ExtractDTLSRole(parsed) 43 require.Equal(t, webrtc.DTLSRoleServer, dtlsRole) 44 45 ufrag, pwd, err := ExtractICECredential(parsed) 46 require.NoError(t, err) 47 require.Equal(t, "1hhfzwf0ijpzm", ufrag) 48 require.Equal(t, "jm5puo2ab1op3vs59ca53bdk7s", pwd) 49 50 streamID, found := ExtractStreamID(parsed.MediaDescriptions[0]) 51 require.False(t, found) 52 require.Empty(t, streamID) 53 54 _, ok := GetSimulcastRids(parsed.MediaDescriptions[1]) 55 require.False(t, ok) 56 57 codecs, err := CodecsFromMediaDescription(parsed.MediaDescriptions[0]) 58 require.NoError(t, err) 59 expectedCodec := pionsdp.Codec{ 60 PayloadType: 96, 61 Name: "H264", 62 ClockRate: 90000, 63 Fmtp: "packetization-mode=1;profile-level-id=42e01f", 64 RTCPFeedback: []string{"nack", "goog-remb"}, 65 } 66 require.Equal(t, expectedCodec, codecs[0]) 67 68 bundleMid, found := GetBundleMid(parsed) 69 require.True(t, found) 70 require.Equal(t, "0", bundleMid) 71 } 72 73 func TestSDPFragment(t *testing.T) { 74 fragment := "a=ice-lite\r\na=ice-options:trickle ice2\r\na=group:BUNDLE 0 1\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\na=mid:0\r\na=ice-ufrag:ysXw\r\na=ice-pwd:vw5LmwG4y/e6dPP/zAP9Gp5k\r\na=candidate:1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1\r\na=candidate:3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2\r\na=candidate:473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1\r\na=candidate:2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2\r\na=candidate:393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active\r\n" 75 76 lite := true 77 expectedSDPFragment := SDPFragment{ 78 group: "BUNDLE 0 1", 79 ice: &sdpFragmentICE{ 80 options: "trickle ice2", 81 lite: &lite, 82 }, 83 media: &sdpFragmentMedia{ 84 info: "audio 9 UDP/TLS/RTP/SAVPF 111", 85 mid: "0", 86 ice: &sdpFragmentICE{ 87 ufrag: "ysXw", 88 pwd: "vw5LmwG4y/e6dPP/zAP9Gp5k", 89 }, 90 candidates: []string{ 91 "1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1", 92 "3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2", 93 "473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1", 94 "2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2", 95 "393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active", 96 }, 97 }, 98 } 99 100 sdpFragment := &SDPFragment{} 101 err := sdpFragment.Unmarshal(fragment) 102 require.NoError(t, err) 103 require.Equal(t, expectedSDPFragment, *sdpFragment) 104 105 expectedMarshalled := "a=group:BUNDLE 0 1\r\na=ice-lite\r\na=ice-options:trickle ice2\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\na=mid:0\r\na=ice-ufrag:ysXw\r\na=ice-pwd:vw5LmwG4y/e6dPP/zAP9Gp5k\r\na=candidate:1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1\r\na=candidate:3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2\r\na=candidate:473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1\r\na=candidate:2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2\r\na=candidate:393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active\r\n" 106 marshalled, err := sdpFragment.Marshal() 107 require.NoError(t, err) 108 require.Equal(t, expectedMarshalled, marshalled) 109 110 expectedCandidates := []string{ 111 "1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1", 112 "3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2", 113 "473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1", 114 "2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2", 115 "393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active", 116 } 117 118 sdpFragment1 := &SDPFragment{} 119 err = sdpFragment1.Unmarshal(marshalled) 120 require.NoError(t, err) 121 require.Equal(t, expectedSDPFragment, *sdpFragment1) 122 require.Equal(t, "0", sdpFragment1.Mid()) 123 require.Equal(t, expectedCandidates, sdpFragment1.Candidates()) 124 ufrag, pwd, err := sdpFragment1.ExtractICECredential() 125 require.NoError(t, err) 126 require.Equal(t, "ysXw", ufrag) 127 require.Equal(t, "vw5LmwG4y/e6dPP/zAP9Gp5k", pwd) 128 129 mismatchedMidFragment := "a=ice-options:trickle ice2\r\na=group:BUNDLE 0 1\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\na=mid:1\r\na=ice-ufrag:ysXw\r\na=ice-pwd:vw5LmwG4y/e6dPP/zAP9Gp5k\r\na=candidate:1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1\r\na=candidate:3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2\r\na=candidate:473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1\r\na=candidate:2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2\r\n" 130 sdpFragment2 := &SDPFragment{} 131 err = sdpFragment2.Unmarshal(mismatchedMidFragment) 132 require.Error(t, err) 133 134 sdp := "v=0\r\no=- 4648475892259889561 3 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=ice-options:trickle ice2\r\na=fingerprint:sha-256 40:42:FB:47:87:52:BF:CB:EC:3A:DF:EB:06:DA:2D:B7:2F:59:42:10:23:7B:9D:4C:C9:58:DD:FF:A2:8F:17:67\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:1hhfzwf0ijpzm\r\na=ice-pwd:jm5puo2ab1op3vs59ca53bdk7s\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:0\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:96 H264/90000\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 goog-remb\r\na=fmtp:96 packetization-mode=1;profile-level-id=42e01f\r\na=ssrc:1505338584 cname:10000000b5810aac\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:1\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=ssrc:697641945 cname:10000000b5810aac\r\n" 135 sd := webrtc.SessionDescription{ 136 Type: webrtc.SDPTypeOffer, 137 SDP: sdp, 138 } 139 parsed, _ := sd.Unmarshal() 140 err = sdpFragment1.PatchICECredentialAndCandidatesIntoSDP(parsed) 141 require.NoError(t, err) 142 143 ufrag, pwd, err = ExtractICECredential(parsed) 144 require.NoError(t, err) 145 require.Equal(t, "ysXw", ufrag) 146 require.Equal(t, "vw5LmwG4y/e6dPP/zAP9Gp5k", pwd) 147 148 candidates := []string{} 149 for _, a := range parsed.MediaDescriptions[0].Attributes { 150 if a.IsICECandidate() { 151 candidates = append(candidates, a.Value) 152 } 153 } 154 require.Equal(t, expectedCandidates, candidates) 155 156 sdpFragment3, err := ExtractSDPFragment(parsed) 157 require.NoError(t, err) 158 159 expectedSDPFragment3 := SDPFragment{ 160 group: "BUNDLE 0 1", 161 ice: &sdpFragmentICE{ 162 options: "trickle ice2", 163 }, 164 media: &sdpFragmentMedia{ 165 info: "video 9 UDP/TLS/RTP/SAVPF 96", 166 mid: "0", 167 ice: &sdpFragmentICE{ 168 ufrag: "ysXw", 169 pwd: "vw5LmwG4y/e6dPP/zAP9Gp5k", 170 }, 171 candidates: []string{ 172 "1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1", 173 "3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2", 174 "473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1", 175 "2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2", 176 "393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active", 177 }, 178 }, 179 } 180 require.Equal(t, expectedSDPFragment3, *sdpFragment3) 181 182 marshalledSDPFragment3, err := sdpFragment3.Marshal() 183 require.NoError(t, err) 184 expectedMarshalledSDPFragment3 := "a=group:BUNDLE 0 1\r\na=ice-options:trickle ice2\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\na=mid:0\r\na=ice-ufrag:ysXw\r\na=ice-pwd:vw5LmwG4y/e6dPP/zAP9Gp5k\r\na=candidate:1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1\r\na=candidate:3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2\r\na=candidate:473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1\r\na=candidate:2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2\r\na=candidate:393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active\r\n" 185 require.Equal(t, expectedMarshalledSDPFragment3, marshalledSDPFragment3) 186 }