diff --git a/icegatherer.go b/icegatherer.go index 06b1df3d15..b15c72b545 100644 --- a/icegatherer.go +++ b/icegatherer.go @@ -308,46 +308,12 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) { for _, candidatePairStats := range agent.GetCandidatePairsStats() { collector.Collecting() - state, err := toStatsICECandidatePairState(candidatePairStats.State) + stats, err := toICECandidatePairStats(candidatePairStats) if err != nil { g.log.Error(err.Error()) + continue } - pairID := newICECandidatePairStatsID(candidatePairStats.LocalCandidateID, - candidatePairStats.RemoteCandidateID) - - stats := ICECandidatePairStats{ - Timestamp: statsTimestampFrom(candidatePairStats.Timestamp), - Type: StatsTypeCandidatePair, - ID: pairID, - // TransportID: - LocalCandidateID: candidatePairStats.LocalCandidateID, - RemoteCandidateID: candidatePairStats.RemoteCandidateID, - State: state, - Nominated: candidatePairStats.Nominated, - PacketsSent: candidatePairStats.PacketsSent, - PacketsReceived: candidatePairStats.PacketsReceived, - BytesSent: candidatePairStats.BytesSent, - BytesReceived: candidatePairStats.BytesReceived, - LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp), - LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp), - FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp), - LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp), - LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp), - TotalRoundTripTime: candidatePairStats.TotalRoundTripTime, - CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime, - AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate, - AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate, - CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount, - RequestsReceived: candidatePairStats.RequestsReceived, - RequestsSent: candidatePairStats.RequestsSent, - ResponsesReceived: candidatePairStats.ResponsesReceived, - ResponsesSent: candidatePairStats.ResponsesSent, - RetransmissionsReceived: candidatePairStats.RetransmissionsReceived, - RetransmissionsSent: candidatePairStats.RetransmissionsSent, - ConsentRequestsSent: candidatePairStats.ConsentRequestsSent, - ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp), - } collector.Collect(stats.ID, stats) } @@ -409,3 +375,23 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) { collector.Done() }(collector, agent) } + +func (g *ICEGatherer) getSelectedCandidatePairStats() (ICECandidatePairStats, bool) { + agent := g.getAgent() + if agent == nil { + return ICECandidatePairStats{}, false + } + + selectedCandidatePairStats, isAvailable := agent.GetSelectedCandidatePairStats() + if !isAvailable { + return ICECandidatePairStats{}, false + } + + stats, err := toICECandidatePairStats(selectedCandidatePairStats) + if err != nil { + g.log.Error(err.Error()) + return ICECandidatePairStats{}, false + } + + return stats, true +} diff --git a/icetransport.go b/icetransport.go index e7b8f8ec1c..a86d4f94f1 100644 --- a/icetransport.go +++ b/icetransport.go @@ -70,6 +70,12 @@ func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) { return NewICECandidatePair(&local, &remote), nil } +// GetSelectedCandidatePairStats returns the selected candidate pair stats on which packets are sent +// if there is no selected pair empty stats, false is returned to indicate stats not available +func (t *ICETransport) GetSelectedCandidatePairStats() (ICECandidatePairStats, bool) { + return t.gatherer.getSelectedCandidatePairStats() +} + // NewICETransport creates a new NewICETransport. func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) *ICETransport { iceTransport := &ICETransport{ diff --git a/icetransport_test.go b/icetransport_test.go index 0d7d55e042..bcbe0bd295 100644 --- a/icetransport_test.go +++ b/icetransport_test.go @@ -98,10 +98,14 @@ func TestICETransport_GetSelectedCandidatePair(t *testing.T) { offererSelectedPair, err := offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair() assert.NoError(t, err) assert.Nil(t, offererSelectedPair) + _, statsAvailable := offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats() + assert.False(t, statsAvailable) answererSelectedPair, err := answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair() assert.NoError(t, err) assert.Nil(t, answererSelectedPair) + _, statsAvailable = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats() + assert.False(t, statsAvailable) assert.NoError(t, signalPair(offerer, answerer)) peerConnectionConnected.Wait() @@ -109,10 +113,14 @@ func TestICETransport_GetSelectedCandidatePair(t *testing.T) { offererSelectedPair, err = offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair() assert.NoError(t, err) assert.NotNil(t, offererSelectedPair) + _, statsAvailable = offerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats() + assert.True(t, statsAvailable) answererSelectedPair, err = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePair() assert.NoError(t, err) assert.NotNil(t, answererSelectedPair) + _, statsAvailable = answerer.SCTP().Transport().ICETransport().GetSelectedCandidatePairStats() + assert.True(t, statsAvailable) closePairNow(t, offerer, answerer) } diff --git a/stats.go b/stats.go index abb0b2ed9e..7e4350449a 100644 --- a/stats.go +++ b/stats.go @@ -1965,6 +1965,46 @@ func toStatsICECandidatePairState(state ice.CandidatePairState) (StatsICECandida } } +func toICECandidatePairStats(candidatePairStats ice.CandidatePairStats) (ICECandidatePairStats, error) { + state, err := toStatsICECandidatePairState(candidatePairStats.State) + if err != nil { + return ICECandidatePairStats{}, err + } + + return ICECandidatePairStats{ + Timestamp: statsTimestampFrom(candidatePairStats.Timestamp), + Type: StatsTypeCandidatePair, + ID: newICECandidatePairStatsID(candidatePairStats.LocalCandidateID, candidatePairStats.RemoteCandidateID), + // TransportID: + LocalCandidateID: candidatePairStats.LocalCandidateID, + RemoteCandidateID: candidatePairStats.RemoteCandidateID, + State: state, + Nominated: candidatePairStats.Nominated, + PacketsSent: candidatePairStats.PacketsSent, + PacketsReceived: candidatePairStats.PacketsReceived, + BytesSent: candidatePairStats.BytesSent, + BytesReceived: candidatePairStats.BytesReceived, + LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp), + LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp), + FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp), + LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp), + LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp), + TotalRoundTripTime: candidatePairStats.TotalRoundTripTime, + CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime, + AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate, + AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate, + CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount, + RequestsReceived: candidatePairStats.RequestsReceived, + RequestsSent: candidatePairStats.RequestsSent, + ResponsesReceived: candidatePairStats.ResponsesReceived, + ResponsesSent: candidatePairStats.ResponsesSent, + RetransmissionsReceived: candidatePairStats.RetransmissionsReceived, + RetransmissionsSent: candidatePairStats.RetransmissionsSent, + ConsentRequestsSent: candidatePairStats.ConsentRequestsSent, + ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp), + }, nil +} + const ( // StatsICECandidatePairStateFrozen means a check for this pair hasn't been // performed, and it can't yet be performed until some other check succeeds,