diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index 186edf578..c2007ec3e 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -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) diff --git a/pkg/rtc/types/ice.go b/pkg/rtc/types/ice.go index 1319a4e4c..a78237ded 100644 --- a/pkg/rtc/types/ice.go +++ b/pkg/rtc/types/ice.go @@ -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 { diff --git a/test/client/client.go b/test/client/client.go index 00b49012f..10362dece 100644 --- a/test/client/client.go +++ b/test/client/client.go @@ -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 } }