mirror of
https://github.com/livekit/livekit.git
synced 2026-05-25 12:04:47 +00:00
Store concrete ICE candidate for remote candidates. (#4458)
This commit is contained in:
+5
-5
@@ -2331,7 +2331,7 @@ func connectionDetailsFields(infos []*types.ICEConnectionInfo) []any {
|
||||
if c.Trickle {
|
||||
cStr += "[trickle]"
|
||||
}
|
||||
cStr += " " + c.Local.String()
|
||||
cStr += " " + c.Candidate.String()
|
||||
candidates = append(candidates, cStr)
|
||||
}
|
||||
for _, c := range info.Remote {
|
||||
@@ -2344,11 +2344,11 @@ func connectionDetailsFields(infos []*types.ICEConnectionInfo) []any {
|
||||
if c.Trickle {
|
||||
cStr += "[trickle]"
|
||||
}
|
||||
cStr += " " + fmt.Sprintf("%s %s %s:%d", c.Remote.NetworkType(), c.Remote.Type(), MaybeTruncateIP(c.Remote.Address()), c.Remote.Port())
|
||||
if relatedAddress := c.Remote.RelatedAddress(); relatedAddress != nil {
|
||||
relatedAddr := MaybeTruncateIP(relatedAddress.Address)
|
||||
cStr += " " + fmt.Sprintf("%s %s %s:%d", c.Candidate.Protocol.String(), c.Candidate.Typ.String(), MaybeTruncateIP(c.Candidate.Address), c.Candidate.Port)
|
||||
if relatedAddress := c.Candidate.RelatedAddress; relatedAddress != "" {
|
||||
relatedAddr := MaybeTruncateIP(relatedAddress)
|
||||
if relatedAddr != "" {
|
||||
cStr += " " + fmt.Sprintf(" related %s:%d", relatedAddr, relatedAddress.Port)
|
||||
cStr += " " + fmt.Sprintf(" related %s:%d", relatedAddr, c.Candidate.RelatedPort)
|
||||
}
|
||||
}
|
||||
candidates = append(candidates, cStr)
|
||||
|
||||
+59
-95
@@ -74,9 +74,7 @@ func (i ICEConnectionType) ReporterType() roomobs.ConnectionType {
|
||||
// --------------------------------------------
|
||||
|
||||
type ICECandidateExtended struct {
|
||||
// only one of local or remote is set. This is due to type foo in Pion
|
||||
Local *webrtc.ICECandidate
|
||||
Remote ice.Candidate
|
||||
Candidate *webrtc.ICECandidate
|
||||
SelectedOrder int
|
||||
Filtered bool
|
||||
Trickle bool
|
||||
@@ -135,7 +133,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
|
||||
}
|
||||
for _, c := range d.Local {
|
||||
info.Local = append(info.Local, &ICECandidateExtended{
|
||||
Local: c.Local,
|
||||
Candidate: c.Candidate,
|
||||
Filtered: c.Filtered,
|
||||
SelectedOrder: c.SelectedOrder,
|
||||
Trickle: c.Trickle,
|
||||
@@ -143,7 +141,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
|
||||
}
|
||||
for _, c := range d.Remote {
|
||||
info.Remote = append(info.Remote, &ICECandidateExtended{
|
||||
Remote: c.Remote,
|
||||
Candidate: c.Candidate,
|
||||
Filtered: c.Filtered,
|
||||
SelectedOrder: c.SelectedOrder,
|
||||
Trickle: c.Trickle,
|
||||
@@ -163,15 +161,15 @@ func (d *ICEConnectionDetails) AddLocalCandidate(c *webrtc.ICECandidate, filtere
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
compFn := func(e *ICECandidateExtended) bool {
|
||||
return isCandidateEqualTo(e.Local, c)
|
||||
return isCandidateEqualTo(e.Candidate, c)
|
||||
}
|
||||
if slices.ContainsFunc(d.Local, compFn) {
|
||||
return
|
||||
}
|
||||
d.Local = append(d.Local, &ICECandidateExtended{
|
||||
Local: c,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
Candidate: c,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,24 +184,30 @@ func (d *ICEConnectionDetails) AddLocalICECandidate(c ice.Candidate, filtered, t
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, filtered, trickle, canUpdate bool) {
|
||||
candidate, err := unmarshalICECandidate(c)
|
||||
iceCandidate, err := unmarshalICECandidate(c)
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal candidate", err, "candidate", c)
|
||||
return
|
||||
}
|
||||
d.AddRemoteICECandidate(candidate, filtered, trickle, canUpdate)
|
||||
d.AddRemoteICECandidate(iceCandidate, filtered, trickle, canUpdate)
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, filtered, trickle, canUpdate bool) {
|
||||
if candidate == nil {
|
||||
func (d *ICEConnectionDetails) AddRemoteICECandidate(iceCandidate ice.Candidate, filtered, trickle, canUpdate bool) {
|
||||
if iceCandidate == nil {
|
||||
// end-of-candidates candidate
|
||||
return
|
||||
}
|
||||
|
||||
candidate, err := unmarshalCandidate(iceCandidate)
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal ice candidate", err, "candidate", iceCandidate)
|
||||
return
|
||||
}
|
||||
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
indexFn := func(e *ICECandidateExtended) bool {
|
||||
return isICECandidateEqualTo(e.Remote, candidate)
|
||||
return isCandidateEqualTo(e.Candidate, candidate)
|
||||
}
|
||||
if idx := slices.IndexFunc(d.Remote, indexFn); idx != -1 {
|
||||
if canUpdate {
|
||||
@@ -213,9 +217,9 @@ func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, fi
|
||||
return
|
||||
}
|
||||
d.Remote = append(d.Remote, &ICECandidateExtended{
|
||||
Remote: candidate,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
Candidate: candidate,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
})
|
||||
d.updateConnectionTypeLocked()
|
||||
}
|
||||
@@ -235,22 +239,14 @@ func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) {
|
||||
d.selectedCount++
|
||||
|
||||
remoteIdx := slices.IndexFunc(d.Remote, func(e *ICECandidateExtended) bool {
|
||||
return isICECandidateEqualToCandidate(e.Remote, pair.Remote)
|
||||
return isCandidateEqualTo(e.Candidate, pair.Remote)
|
||||
})
|
||||
if remoteIdx < 0 {
|
||||
// it's possible for prflx candidates to be generated by Pion, we'll add them
|
||||
candidate, err := unmarshalICECandidate(pair.Remote.ToJSON())
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal remote candidate", err, "candidate", pair.Remote)
|
||||
return
|
||||
}
|
||||
if candidate == nil {
|
||||
return
|
||||
}
|
||||
d.Remote = append(d.Remote, &ICECandidateExtended{
|
||||
Remote: candidate,
|
||||
Filtered: false,
|
||||
Trickle: false,
|
||||
Candidate: pair.Remote,
|
||||
Filtered: false,
|
||||
Trickle: false,
|
||||
})
|
||||
remoteIdx = len(d.Remote) - 1
|
||||
}
|
||||
@@ -258,7 +254,7 @@ func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) {
|
||||
d.updateConnectionTypeLocked()
|
||||
|
||||
localIdx := slices.IndexFunc(d.Local, func(e *ICECandidateExtended) bool {
|
||||
return isCandidateEqualTo(e.Local, pair.Local)
|
||||
return isCandidateEqualTo(e.Candidate, pair.Local)
|
||||
})
|
||||
if localIdx < 0 {
|
||||
d.logger.Errorw("could not match local candidate", nil, "local", pair.Local)
|
||||
@@ -286,33 +282,33 @@ func (d *ICEConnectionDetails) updateConnectionTypeLocked() {
|
||||
return
|
||||
}
|
||||
|
||||
remoteCandidate := selectedRemoteCandidate.Remote
|
||||
switch remoteCandidate.NetworkType() {
|
||||
case ice.NetworkTypeUDP4, ice.NetworkTypeUDP6:
|
||||
remoteCandidate := selectedRemoteCandidate.Candidate
|
||||
switch remoteCandidate.Protocol {
|
||||
case webrtc.ICEProtocolUDP:
|
||||
d.Type = ICEConnectionTypeUDP
|
||||
|
||||
case ice.NetworkTypeTCP4, ice.NetworkTypeTCP6:
|
||||
case webrtc.ICEProtocolTCP:
|
||||
d.Type = ICEConnectionTypeTCP
|
||||
}
|
||||
|
||||
switch remoteCandidate.Type() {
|
||||
case ice.CandidateTypeRelay:
|
||||
switch remoteCandidate.Typ {
|
||||
case webrtc.ICECandidateTypeRelay:
|
||||
d.Type = ICEConnectionTypeTURN
|
||||
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
case webrtc.ICECandidateTypePrflx:
|
||||
// if the remote relay candidate pings us *before* we get a relay candidate,
|
||||
// Pion would have created a prflx candidate with the same address as the relay candidate.
|
||||
// to report an accurate connection type, we'll compare to see if existing relay candidates match
|
||||
for _, other := range d.Remote {
|
||||
or := other.Remote
|
||||
if or.Type() == ice.CandidateTypeRelay &&
|
||||
remoteCandidate.Address() == or.Address() &&
|
||||
or := other.Candidate
|
||||
if or.Typ == webrtc.ICECandidateTypeRelay &&
|
||||
remoteCandidate.Address == or.Address &&
|
||||
// NOTE: port is not compared as relayed address reported by TURN ALLOCATE from
|
||||
// pion/turn server -> client and later sent from client -> server via ICE Trickle does not
|
||||
// match port of `prflx` candidate learnt via TURN path. TODO-INVESTIGATE: how and why doesn't
|
||||
// port match?
|
||||
//remoteCanddiate.Port() == or.Port() &&
|
||||
remoteCandidate.NetworkType().NetworkShort() == or.NetworkType().NetworkShort() {
|
||||
// remoteCandidate.Port == or.Port &&
|
||||
remoteCandidate.Protocol == or.Protocol {
|
||||
d.Type = ICEConnectionTypeTURN
|
||||
break
|
||||
}
|
||||
@@ -340,39 +336,6 @@ func isCandidateEqualTo(c1, c2 *webrtc.ICECandidate) bool {
|
||||
c1.TCPType == c2.TCPType
|
||||
}
|
||||
|
||||
func isICECandidateEqualTo(c1, c2 ice.Candidate) bool {
|
||||
if c1 == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
|
||||
return false
|
||||
}
|
||||
return c1.Type() == c2.Type() &&
|
||||
c1.NetworkType() == c2.NetworkType() &&
|
||||
c1.Address() == c2.Address() &&
|
||||
c1.Port() == c2.Port() &&
|
||||
c1.Foundation() == c2.Foundation() &&
|
||||
c1.Priority() == c2.Priority() &&
|
||||
c1.RelatedAddress().Equal(c2.RelatedAddress()) &&
|
||||
c1.TCPType() == c2.TCPType()
|
||||
}
|
||||
|
||||
func isICECandidateEqualToCandidate(c1 ice.Candidate, c2 *webrtc.ICECandidate) bool {
|
||||
if c1 == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
|
||||
return false
|
||||
}
|
||||
return c1.Type().String() == c2.Typ.String() &&
|
||||
c1.NetworkType().NetworkShort() == c2.Protocol.String() &&
|
||||
c1.Address() == c2.Address &&
|
||||
c1.Port() == int(c2.Port) &&
|
||||
c1.Foundation() == c2.Foundation &&
|
||||
c1.Priority() == c2.Priority &&
|
||||
c1.TCPType().String() == c2.TCPType
|
||||
}
|
||||
|
||||
func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
|
||||
candidateValue := strings.TrimPrefix(c.Candidate, "candidate:")
|
||||
if candidateValue == "" {
|
||||
@@ -388,28 +351,14 @@ func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
|
||||
}
|
||||
|
||||
func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
|
||||
var typ webrtc.ICECandidateType
|
||||
switch i.Type() {
|
||||
case ice.CandidateTypeHost:
|
||||
typ = webrtc.ICECandidateTypeHost
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
typ = webrtc.ICECandidateTypeSrflx
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
typ = webrtc.ICECandidateTypePrflx
|
||||
case ice.CandidateTypeRelay:
|
||||
typ = webrtc.ICECandidateTypeRelay
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown candidate type: %s", i.Type())
|
||||
typ, err := convertTypeFromICE(i.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var protocol webrtc.ICEProtocol
|
||||
switch strings.ToLower(i.NetworkType().NetworkShort()) {
|
||||
case "udp":
|
||||
protocol = webrtc.ICEProtocolUDP
|
||||
case "tcp":
|
||||
protocol = webrtc.ICEProtocolTCP
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown network type: %s", i.NetworkType())
|
||||
protocol, err := webrtc.NewICEProtocol(i.NetworkType().NetworkShort())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := webrtc.ICECandidate{
|
||||
@@ -431,6 +380,21 @@ func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func convertTypeFromICE(t ice.CandidateType) (webrtc.ICECandidateType, error) {
|
||||
switch t {
|
||||
case ice.CandidateTypeHost:
|
||||
return webrtc.ICECandidateTypeHost, nil
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
return webrtc.ICECandidateTypeSrflx, nil
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
return webrtc.ICECandidateTypePrflx, nil
|
||||
case ice.CandidateTypeRelay:
|
||||
return webrtc.ICECandidateTypeRelay, nil
|
||||
default:
|
||||
return webrtc.ICECandidateType(t), fmt.Errorf("unknown ice candidate type: %s", t)
|
||||
}
|
||||
}
|
||||
|
||||
func IsCandidateMDNS(candidate webrtc.ICECandidateInit) bool {
|
||||
c, err := unmarshalICECandidate(candidate)
|
||||
if err != nil {
|
||||
|
||||
@@ -1355,7 +1355,7 @@ func (c *RTCClient) IsLocalCandidateRelaySelected() bool {
|
||||
return false
|
||||
}
|
||||
for _, local := range info.Local {
|
||||
if local.SelectedOrder > 0 && local.Local != nil && local.Local.Typ == webrtc.ICECandidateTypeRelay {
|
||||
if local.SelectedOrder > 0 && local.Candidate != nil && local.Candidate.Typ == webrtc.ICECandidateTypeRelay {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user