From 7a5c90dc0132d01af72c757e900aa6cc4a4f362a Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Wed, 3 Jul 2024 14:16:58 +0800 Subject: [PATCH] log non-trickle candidate in details (#2832) * log non-trickle candidate in details * fix test --- pkg/rtc/room.go | 6 +++ pkg/rtc/transport.go | 33 ++++++++------ pkg/rtc/transport_test.go | 4 +- pkg/rtc/types/ice.go | 90 +++++++++++++++++++++++++++++++++------ 4 files changed, 105 insertions(+), 28 deletions(-) diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index c5daee74f..6d1f28b69 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -1558,6 +1558,9 @@ func connectionDetailsFields(cds []*types.ICEConnectionDetails) []interface{} { } else if c.Filtered { cStr += "[filtered]" } + if c.Trickle { + cStr += "[trickle]" + } cStr += " " + c.Local.String() candidates = append(candidates, cStr) } @@ -1568,6 +1571,9 @@ func connectionDetailsFields(cds []*types.ICEConnectionDetails) []interface{} { } else if c.Filtered { cStr += "[filtered]" } + if c.Trickle { + cStr += "[trickle]" + } cStr += " " + c.Remote.String() candidates = append(candidates, cStr) } diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index 8e062f6d9..146861937 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -1366,7 +1366,7 @@ func (t *PCTransport) handleLocalICECandidate(e event) error { t.params.Logger.Debugw("filtering out local candidate", "candidate", c.String()) filtered = true } - t.connectionDetails.AddLocalCandidate(c, filtered) + t.connectionDetails.AddLocalCandidate(c, filtered, true) } if filtered { @@ -1395,7 +1395,7 @@ func (t *PCTransport) handleRemoteICECandidate(e event) error { filtered = true } - t.connectionDetails.AddRemoteCandidate(*c, filtered) + t.connectionDetails.AddRemoteCandidate(*c, filtered, true) if filtered { return nil } @@ -1422,7 +1422,7 @@ func (t *PCTransport) setNegotiationState(state transport.NegotiationState) { } } -func (t *PCTransport) filterCandidates(sd webrtc.SessionDescription, preferTCP bool) webrtc.SessionDescription { +func (t *PCTransport) filterCandidates(sd webrtc.SessionDescription, preferTCP, isLocal bool) webrtc.SessionDescription { parsed, err := sd.Unmarshal() if err != nil { t.params.Logger.Warnw("could not unmarshal SDP to filter candidates", err) @@ -1432,13 +1432,22 @@ func (t *PCTransport) filterCandidates(sd webrtc.SessionDescription, preferTCP b filterAttributes := func(attrs []sdp.Attribute) []sdp.Attribute { filteredAttrs := make([]sdp.Attribute, 0, len(attrs)) for _, a := range attrs { - if a.Key == sdp.AttrKeyCandidate { - if preferTCP { - if strings.Contains(a.Value, "tcp") { - filteredAttrs = append(filteredAttrs, a) - } - } else { + if a.IsICECandidate() { + c, err := ice.UnmarshalCandidate(a.Value) + if err != nil { + t.params.Logger.Errorw("failed to unmarshal candidate in sdp", err, "isLocal", isLocal, "sdp", sd.SDP) filteredAttrs = append(filteredAttrs, a) + continue + } + excluded := preferTCP && !c.NetworkType().IsTCP() + if !excluded { + filteredAttrs = append(filteredAttrs, a) + } + + if isLocal { + t.connectionDetails.AddLocalICECandidate(c, excluded, false) + } else { + t.connectionDetails.AddRemoteICECandidate(c, excluded, false) } } else { filteredAttrs = append(filteredAttrs, a) @@ -1566,7 +1575,7 @@ func (t *PCTransport) createAndSendOffer(options *webrtc.OfferOptions) error { // Filtered offer is sent to remote so that remote does not // see filtered candidates. // - offer = t.filterCandidates(offer, preferTCP) + offer = t.filterCandidates(offer, preferTCP, true) if preferTCP { t.params.Logger.Debugw("local offer (filtered)", "sdp", offer.SDP) } @@ -1616,7 +1625,7 @@ func (t *PCTransport) setRemoteDescription(sd webrtc.SessionDescription) error { if preferTCP { t.params.Logger.Debugw("remote description (unfiltered)", "type", sd.Type, "sdp", sd.SDP) } - sd = t.filterCandidates(sd, preferTCP) + sd = t.filterCandidates(sd, preferTCP, false) if preferTCP { t.params.Logger.Debugw("remote description (filtered)", "type", sd.Type, "sdp", sd.SDP) } @@ -1683,7 +1692,7 @@ func (t *PCTransport) createAndSendAnswer() error { // Filtered answer is sent to remote so that remote does not // see filtered candidates. // - answer = t.filterCandidates(answer, preferTCP) + answer = t.filterCandidates(answer, preferTCP, true) if preferTCP { t.params.Logger.Debugw("local answer (filtered)", "sdp", answer.SDP) } diff --git a/pkg/rtc/transport_test.go b/pkg/rtc/transport_test.go index 7bd1067b3..ab5cd5a88 100644 --- a/pkg/rtc/transport_test.go +++ b/pkg/rtc/transport_test.go @@ -416,7 +416,7 @@ func TestFilteringCandidates(t *testing.T) { // should not filter out UDP candidates if TCP is not preferred offer = *transport.pc.LocalDescription() - filteredOffer := transport.filterCandidates(offer, false) + filteredOffer := transport.filterCandidates(offer, false, true) require.EqualValues(t, offer.SDP, filteredOffer.SDP) parsed, err := offer.Unmarshal() @@ -494,7 +494,7 @@ func TestFilteringCandidates(t *testing.T) { require.Equal(t, 2, tcp) transport.SetPreferTCP(true) - filteredOffer = transport.filterCandidates(offer, true) + filteredOffer = transport.filterCandidates(offer, true, true) parsed, err = filteredOffer.Unmarshal() require.NoError(t, err) udp, tcp = getNumTransportTypeCandidates(parsed) diff --git a/pkg/rtc/types/ice.go b/pkg/rtc/types/ice.go index f628c4a30..a659e6d03 100644 --- a/pkg/rtc/types/ice.go +++ b/pkg/rtc/types/ice.go @@ -17,6 +17,7 @@ package types import ( + "fmt" "strings" "sync" @@ -43,6 +44,7 @@ type ICECandidateExtended struct { Remote ice.Candidate Selected bool Filtered bool + Trickle bool } type ICEConnectionDetails struct { @@ -85,6 +87,7 @@ func (d *ICEConnectionDetails) Clone() *ICEConnectionDetails { Local: c.Local, Filtered: c.Filtered, Selected: c.Selected, + Trickle: c.Trickle, }) } for _, c := range d.Remote { @@ -92,32 +95,48 @@ func (d *ICEConnectionDetails) Clone() *ICEConnectionDetails { Remote: c.Remote, Filtered: c.Filtered, Selected: c.Selected, + Trickle: c.Trickle, }) } return clone } -func (d *ICEConnectionDetails) AddLocalCandidate(c *webrtc.ICECandidate, filtered bool) { +func (d *ICEConnectionDetails) AddLocalCandidate(c *webrtc.ICECandidate, filtered, trickle bool) { d.lock.Lock() defer d.lock.Unlock() compFn := func(e *ICECandidateExtended) bool { return isCandidateEqualTo(e.Local, c) } - if slices.ContainsFunc[[]*ICECandidateExtended, *ICECandidateExtended](d.Local, compFn) { + if slices.ContainsFunc(d.Local, compFn) { return } d.Local = append(d.Local, &ICECandidateExtended{ Local: c, Filtered: filtered, + Trickle: trickle, }) } -func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, filtered bool) { +func (d *ICEConnectionDetails) AddLocalICECandidate(c ice.Candidate, filtered, trickle bool) { + candidate, err := unmarshalCandidate(c) + if err != nil { + d.logger.Errorw("could not unmarshal ice candidate", err, "candidate", c) + return + } + + d.AddLocalCandidate(candidate, filtered, trickle) +} + +func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, filtered, trickle bool) { candidate, err := unmarshalICECandidate(c) if err != nil { d.logger.Errorw("could not unmarshal candidate", err, "candidate", c) return } + d.AddRemoteICECandidate(candidate, filtered, trickle) +} + +func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, filtered, trickle bool) { if candidate == nil { // end-of-candidates candidate return @@ -126,14 +145,15 @@ func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, fil d.lock.Lock() defer d.lock.Unlock() compFn := func(e *ICECandidateExtended) bool { - return isICECandidateEqualTo(e.Remote, *candidate) + return isICECandidateEqualTo(e.Remote, candidate) } - if slices.ContainsFunc[[]*ICECandidateExtended, *ICECandidateExtended](d.Remote, compFn) { + if slices.ContainsFunc(d.Remote, compFn) { return } d.Remote = append(d.Remote, &ICECandidateExtended{ - Remote: *candidate, + Remote: candidate, Filtered: filtered, + Trickle: trickle, }) } @@ -148,7 +168,7 @@ func (d *ICEConnectionDetails) Clear() { func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) { d.lock.Lock() defer d.lock.Unlock() - remoteIdx := slices.IndexFunc[[]*ICECandidateExtended, *ICECandidateExtended](d.Remote, func(e *ICECandidateExtended) bool { + remoteIdx := slices.IndexFunc(d.Remote, func(e *ICECandidateExtended) bool { return isICECandidateEqualToCandidate(e.Remote, pair.Remote) }) if remoteIdx < 0 { @@ -162,15 +182,16 @@ func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) { return } d.Remote = append(d.Remote, &ICECandidateExtended{ - Remote: *candidate, + Remote: candidate, Filtered: false, + Trickle: true, }) remoteIdx = len(d.Remote) - 1 } remote := d.Remote[remoteIdx] remote.Selected = true - localIdx := slices.IndexFunc[[]*ICECandidateExtended, *ICECandidateExtended](d.Local, func(e *ICECandidateExtended) bool { + localIdx := slices.IndexFunc(d.Local, func(e *ICECandidateExtended) bool { return isCandidateEqualTo(e.Local, pair.Local) }) if localIdx < 0 { @@ -214,7 +235,6 @@ func isCandidateEqualTo(c1, c2 *webrtc.ICECandidate) bool { c1.Protocol == c2.Protocol && c1.Address == c2.Address && c1.Port == c2.Port && - c1.Component == c2.Component && c1.Foundation == c2.Foundation && c1.Priority == c2.Priority && c1.RelatedAddress == c2.RelatedAddress && @@ -233,7 +253,6 @@ func isICECandidateEqualTo(c1, c2 ice.Candidate) bool { c1.NetworkType() == c2.NetworkType() && c1.Address() == c2.Address() && c1.Port() == c2.Port() && - c1.Component() == c2.Component() && c1.Foundation() == c2.Foundation() && c1.Priority() == c2.Priority() && c1.RelatedAddress().Equal(c2.RelatedAddress()) && @@ -251,13 +270,12 @@ func isICECandidateEqualToCandidate(c1 ice.Candidate, c2 *webrtc.ICECandidate) b c1.NetworkType().NetworkShort() == c2.Protocol.String() && c1.Address() == c2.Address && c1.Port() == int(c2.Port) && - c1.Component() == c2.Component && c1.Foundation() == c2.Foundation && c1.Priority() == c2.Priority && c1.TCPType().String() == c2.TCPType } -func unmarshalICECandidate(c webrtc.ICECandidateInit) (*ice.Candidate, error) { +func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) { candidateValue := strings.TrimPrefix(c.Candidate, "candidate:") if candidateValue == "" { return nil, nil @@ -268,5 +286,49 @@ func unmarshalICECandidate(c webrtc.ICECandidateInit) (*ice.Candidate, error) { return nil, err } - return &candidate, nil + return candidate, nil +} + +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()) + } + + 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()) + } + + c := webrtc.ICECandidate{ + Foundation: i.Foundation(), + Priority: i.Priority(), + Address: i.Address(), + Protocol: protocol, + Port: uint16(i.Port()), + Component: i.Component(), + Typ: typ, + TCPType: i.TCPType().String(), + } + + if i.RelatedAddress() != nil { + c.RelatedAddress = i.RelatedAddress().Address + c.RelatedPort = uint16(i.RelatedAddress().Port) + } + + return &c, nil }