Skip to content

Commit c3c7178

Browse files
committed
Add ice transport api to get selected pair stats
In use cases like SFU, it is useful to get just the selected candidate pair stats to have access to current RTT on the peer connection. The standard has a way to do `GetSelectedCandidatePair` on `ICETransport`, but does not have a way to get stats of that pair. Although not in standard, adding a method to `ICETransport` to get selected candidate pair along similar lines of above method.
1 parent c088755 commit c3c7178

File tree

4 files changed

+76
-36
lines changed

4 files changed

+76
-36
lines changed

icegatherer.go

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -306,46 +306,12 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
306306
for _, candidatePairStats := range agent.GetCandidatePairsStats() {
307307
collector.Collecting()
308308

309-
state, err := toStatsICECandidatePairState(candidatePairStats.State)
309+
stats, err := toICECandidatePairStats(candidatePairStats)
310310
if err != nil {
311311
g.log.Error(err.Error())
312+
continue
312313
}
313314

314-
pairID := newICECandidatePairStatsID(candidatePairStats.LocalCandidateID,
315-
candidatePairStats.RemoteCandidateID)
316-
317-
stats := ICECandidatePairStats{
318-
Timestamp: statsTimestampFrom(candidatePairStats.Timestamp),
319-
Type: StatsTypeCandidatePair,
320-
ID: pairID,
321-
// TransportID:
322-
LocalCandidateID: candidatePairStats.LocalCandidateID,
323-
RemoteCandidateID: candidatePairStats.RemoteCandidateID,
324-
State: state,
325-
Nominated: candidatePairStats.Nominated,
326-
PacketsSent: candidatePairStats.PacketsSent,
327-
PacketsReceived: candidatePairStats.PacketsReceived,
328-
BytesSent: candidatePairStats.BytesSent,
329-
BytesReceived: candidatePairStats.BytesReceived,
330-
LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp),
331-
LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp),
332-
FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp),
333-
LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp),
334-
LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp),
335-
TotalRoundTripTime: candidatePairStats.TotalRoundTripTime,
336-
CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime,
337-
AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate,
338-
AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate,
339-
CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount,
340-
RequestsReceived: candidatePairStats.RequestsReceived,
341-
RequestsSent: candidatePairStats.RequestsSent,
342-
ResponsesReceived: candidatePairStats.ResponsesReceived,
343-
ResponsesSent: candidatePairStats.ResponsesSent,
344-
RetransmissionsReceived: candidatePairStats.RetransmissionsReceived,
345-
RetransmissionsSent: candidatePairStats.RetransmissionsSent,
346-
ConsentRequestsSent: candidatePairStats.ConsentRequestsSent,
347-
ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp),
348-
}
349315
collector.Collect(stats.ID, stats)
350316
}
351317

@@ -407,3 +373,23 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
407373
collector.Done()
408374
}(collector, agent)
409375
}
376+
377+
func (g *ICEGatherer) getSelectedCandidatePairStats() (ICECandidatePairStats, bool) {
378+
agent := g.getAgent()
379+
if agent == nil {
380+
return ICECandidatePairStats{}, false
381+
}
382+
383+
selectedCandidatePairStats, isAvailable := agent.GetSelectedCandidatePairStats()
384+
if !isAvailable {
385+
return ICECandidatePairStats{}, false
386+
}
387+
388+
stats, err := toICECandidatePairStats(selectedCandidatePairStats)
389+
if err != nil {
390+
g.log.Error(err.Error())
391+
return ICECandidatePairStats{}, false
392+
}
393+
394+
return stats, true
395+
}

icetransport.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) {
7070
return NewICECandidatePair(&local, &remote), nil
7171
}
7272

73+
// GetSelectedCandidatePairStats returns the selected candidate pair stats on which packets are sent
74+
// if there is no selected pair empty stats, false is returned to indicate stats not available
75+
func (t *ICETransport) GetSelectedCandidatePairStats() (ICECandidatePairStats, bool) {
76+
return t.gatherer.getSelectedCandidatePairStats()
77+
}
78+
7379
// NewICETransport creates a new NewICETransport.
7480
func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) *ICETransport {
7581
iceTransport := &ICETransport{

icetransport_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,29 @@ func TestICETransport_GetSelectedCandidatePair(t *testing.T) {
9898
offererSelectedPair, err := offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair()
9999
assert.NoError(t, err)
100100
assert.Nil(t, offererSelectedPair)
101+
_, statsAvailable := offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats()
102+
assert.False(t, statsAvailable)
101103

102104
answererSelectedPair, err := answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair()
103105
assert.NoError(t, err)
104106
assert.Nil(t, answererSelectedPair)
107+
_, statsAvailable = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats()
108+
assert.False(t, statsAvailable)
105109

106110
assert.NoError(t, signalPair(offerer, answerer))
107111
peerConnectionConnected.Wait()
108112

109113
offererSelectedPair, err = offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair()
110114
assert.NoError(t, err)
111115
assert.NotNil(t, offererSelectedPair)
116+
_, statsAvailable = offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats()
117+
assert.True(t, statsAvailable)
112118

113119
answererSelectedPair, err = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair()
114120
assert.NoError(t, err)
115121
assert.NotNil(t, answererSelectedPair)
122+
_, statsAvailable = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats()
123+
assert.True(t, statsAvailable)
116124

117125
closePairNow(t, offerer, answerer)
118126
}

stats.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,46 @@ func toStatsICECandidatePairState(state ice.CandidatePairState) (StatsICECandida
16961696
}
16971697
}
16981698

1699+
func toICECandidatePairStats(candidatePairStats ice.CandidatePairStats) (ICECandidatePairStats, error) {
1700+
state, err := toStatsICECandidatePairState(candidatePairStats.State)
1701+
if err != nil {
1702+
return ICECandidatePairStats{}, err
1703+
}
1704+
1705+
return ICECandidatePairStats{
1706+
Timestamp: statsTimestampFrom(candidatePairStats.Timestamp),
1707+
Type: StatsTypeCandidatePair,
1708+
ID: newICECandidatePairStatsID(candidatePairStats.LocalCandidateID, candidatePairStats.RemoteCandidateID),
1709+
// TransportID:
1710+
LocalCandidateID: candidatePairStats.LocalCandidateID,
1711+
RemoteCandidateID: candidatePairStats.RemoteCandidateID,
1712+
State: state,
1713+
Nominated: candidatePairStats.Nominated,
1714+
PacketsSent: candidatePairStats.PacketsSent,
1715+
PacketsReceived: candidatePairStats.PacketsReceived,
1716+
BytesSent: candidatePairStats.BytesSent,
1717+
BytesReceived: candidatePairStats.BytesReceived,
1718+
LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp),
1719+
LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp),
1720+
FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp),
1721+
LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp),
1722+
LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp),
1723+
TotalRoundTripTime: candidatePairStats.TotalRoundTripTime,
1724+
CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime,
1725+
AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate,
1726+
AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate,
1727+
CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount,
1728+
RequestsReceived: candidatePairStats.RequestsReceived,
1729+
RequestsSent: candidatePairStats.RequestsSent,
1730+
ResponsesReceived: candidatePairStats.ResponsesReceived,
1731+
ResponsesSent: candidatePairStats.ResponsesSent,
1732+
RetransmissionsReceived: candidatePairStats.RetransmissionsReceived,
1733+
RetransmissionsSent: candidatePairStats.RetransmissionsSent,
1734+
ConsentRequestsSent: candidatePairStats.ConsentRequestsSent,
1735+
ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp),
1736+
}, nil
1737+
}
1738+
16991739
const (
17001740
// StatsICECandidatePairStateFrozen means a check for this pair hasn't been
17011741
// performed, and it can't yet be performed until some other check succeeds,

0 commit comments

Comments
 (0)