* SDP utilities to get simulcast RIDs and media stream track. Some slight variations from this PR (https://github.com/livekit/protocol/pull/1018) * test
187 lines
10 KiB
Go
187 lines
10 KiB
Go
// Copyright 2023 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package sdp
|
|
|
|
import (
|
|
"testing"
|
|
|
|
pionsdp "github.com/pion/sdp/v3"
|
|
"github.com/pion/webrtc/v4"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSDPUtilFunctions(t *testing.T) {
|
|
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"
|
|
sd := webrtc.SessionDescription{
|
|
Type: webrtc.SDPTypeOffer,
|
|
SDP: sdp,
|
|
}
|
|
parsed, err := sd.Unmarshal()
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "0", GetMidValue(parsed.MediaDescriptions[0]))
|
|
require.Equal(t, "1", GetMidValue(parsed.MediaDescriptions[1]))
|
|
|
|
fp, alg, err := ExtractFingerprint(parsed)
|
|
require.NoError(t, err)
|
|
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)
|
|
require.Equal(t, "sha-256", alg)
|
|
|
|
dtlsRole := ExtractDTLSRole(parsed)
|
|
require.Equal(t, webrtc.DTLSRoleServer, dtlsRole)
|
|
|
|
ufrag, pwd, err := ExtractICECredential(parsed)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "1hhfzwf0ijpzm", ufrag)
|
|
require.Equal(t, "jm5puo2ab1op3vs59ca53bdk7s", pwd)
|
|
|
|
streamID, found := ExtractStreamID(parsed.MediaDescriptions[0])
|
|
require.False(t, found)
|
|
require.Empty(t, streamID)
|
|
|
|
_, ok := GetSimulcastRids(parsed.MediaDescriptions[1])
|
|
require.False(t, ok)
|
|
|
|
codecs, err := CodecsFromMediaDescription(parsed.MediaDescriptions[0])
|
|
require.NoError(t, err)
|
|
expectedCodec := pionsdp.Codec{
|
|
PayloadType: 96,
|
|
Name: "H264",
|
|
ClockRate: 90000,
|
|
Fmtp: "packetization-mode=1;profile-level-id=42e01f",
|
|
RTCPFeedback: []string{"nack", "goog-remb"},
|
|
}
|
|
require.Equal(t, expectedCodec, codecs[0])
|
|
|
|
bundleMid, found := GetBundleMid(parsed)
|
|
require.True(t, found)
|
|
require.Equal(t, "0", bundleMid)
|
|
}
|
|
|
|
func TestSDPFragment(t *testing.T) {
|
|
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"
|
|
|
|
lite := true
|
|
expectedSDPFragment := SDPFragment{
|
|
group: "BUNDLE 0 1",
|
|
ice: &sdpFragmentICE{
|
|
options: "trickle ice2",
|
|
lite: &lite,
|
|
},
|
|
media: &sdpFragmentMedia{
|
|
info: "audio 9 UDP/TLS/RTP/SAVPF 111",
|
|
mid: "0",
|
|
ice: &sdpFragmentICE{
|
|
ufrag: "ysXw",
|
|
pwd: "vw5LmwG4y/e6dPP/zAP9Gp5k",
|
|
},
|
|
candidates: []string{
|
|
"1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1",
|
|
"3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2",
|
|
"473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1",
|
|
"2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2",
|
|
"393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active",
|
|
},
|
|
},
|
|
}
|
|
|
|
sdpFragment := &SDPFragment{}
|
|
err := sdpFragment.Unmarshal(fragment)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedSDPFragment, *sdpFragment)
|
|
|
|
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"
|
|
marshalled, err := sdpFragment.Marshal()
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedMarshalled, marshalled)
|
|
|
|
expectedCandidates := []string{
|
|
"1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1",
|
|
"3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2",
|
|
"473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1",
|
|
"2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2",
|
|
"393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active",
|
|
}
|
|
|
|
sdpFragment1 := &SDPFragment{}
|
|
err = sdpFragment1.Unmarshal(marshalled)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedSDPFragment, *sdpFragment1)
|
|
require.Equal(t, "0", sdpFragment1.Mid())
|
|
require.Equal(t, expectedCandidates, sdpFragment1.Candidates())
|
|
ufrag, pwd, err := sdpFragment1.ExtractICECredential()
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ysXw", ufrag)
|
|
require.Equal(t, "vw5LmwG4y/e6dPP/zAP9Gp5k", pwd)
|
|
|
|
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"
|
|
sdpFragment2 := &SDPFragment{}
|
|
err = sdpFragment2.Unmarshal(mismatchedMidFragment)
|
|
require.Error(t, err)
|
|
|
|
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"
|
|
sd := webrtc.SessionDescription{
|
|
Type: webrtc.SDPTypeOffer,
|
|
SDP: sdp,
|
|
}
|
|
parsed, _ := sd.Unmarshal()
|
|
err = sdpFragment1.PatchICECredentialAndCandidatesIntoSDP(parsed)
|
|
require.NoError(t, err)
|
|
|
|
ufrag, pwd, err = ExtractICECredential(parsed)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ysXw", ufrag)
|
|
require.Equal(t, "vw5LmwG4y/e6dPP/zAP9Gp5k", pwd)
|
|
|
|
candidates := []string{}
|
|
for _, a := range parsed.MediaDescriptions[0].Attributes {
|
|
if a.IsICECandidate() {
|
|
candidates = append(candidates, a.Value)
|
|
}
|
|
}
|
|
require.Equal(t, expectedCandidates, candidates)
|
|
|
|
sdpFragment3, err := ExtractSDPFragment(parsed)
|
|
require.NoError(t, err)
|
|
|
|
expectedSDPFragment3 := SDPFragment{
|
|
group: "BUNDLE 0 1",
|
|
ice: &sdpFragmentICE{
|
|
options: "trickle ice2",
|
|
},
|
|
media: &sdpFragmentMedia{
|
|
info: "video 9 UDP/TLS/RTP/SAVPF 96",
|
|
mid: "0",
|
|
ice: &sdpFragmentICE{
|
|
ufrag: "ysXw",
|
|
pwd: "vw5LmwG4y/e6dPP/zAP9Gp5k",
|
|
},
|
|
candidates: []string{
|
|
"1387637174 1 udp 2122260223 192.0.2.1 61764 typ host generation 0 ufrag EsAw network-id 1",
|
|
"3471623853 1 udp 2122194687 198.51.100.2 61765 typ host generation 0 ufrag EsAw network-id 2",
|
|
"473322822 1 tcp 1518280447 192.0.2.1 9 typ host tcptype active generation 0 ufrag EsAw network-id 1",
|
|
"2154773085 1 tcp 1518214911 198.51.100.2 9 typ host tcptype active generation 0 ufrag EsAw network-id 2",
|
|
"393455558 0 tcp 1518283007 [2401:4900:633c:959f:2037:680c:7c40:b3db] 9 typ host tcptype active",
|
|
},
|
|
},
|
|
}
|
|
require.Equal(t, expectedSDPFragment3, *sdpFragment3)
|
|
|
|
marshalledSDPFragment3, err := sdpFragment3.Marshal()
|
|
require.NoError(t, err)
|
|
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"
|
|
require.Equal(t, expectedMarshalledSDPFragment3, marshalledSDPFragment3)
|
|
}
|