mirror of
https://github.com/netbirdio/ice.git
synced 2026-05-22 17:10:58 -07:00
Initial stats
Add basic candidate and candidate pair stats
This commit is contained in:
committed by
Hugo Arregui
parent
da599f54ff
commit
1ff70e0ee1
@@ -973,6 +973,113 @@ func (a *Agent) closeMulticastConn() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetCandidatePairsStats returns a list of candidate pair stats
|
||||
func (a *Agent) GetCandidatePairsStats() []CandidatePairStats {
|
||||
resultChan := make(chan []CandidatePairStats)
|
||||
err := a.run(func(agent *Agent) {
|
||||
result := make([]CandidatePairStats, 0, len(agent.checklist))
|
||||
for _, cp := range agent.checklist {
|
||||
stat := CandidatePairStats{
|
||||
Timestamp: time.Now(),
|
||||
LocalCandidateID: cp.local.ID(),
|
||||
RemoteCandidateID: cp.remote.ID(),
|
||||
State: cp.state,
|
||||
// Nominated bool
|
||||
// PacketsSent uint32
|
||||
// PacketsReceived uint32
|
||||
// BytesSent uint64
|
||||
// BytesReceived uint64
|
||||
// LastPacketSentTimestamp time.Time
|
||||
// LastPacketReceivedTimestamp time.Time
|
||||
// FirstRequestTimestamp time.Time
|
||||
// LastRequestTimestamp time.Time
|
||||
// LastResponseTimestamp time.Time
|
||||
// TotalRoundTripTime float64
|
||||
// CurrentRoundTripTime float64
|
||||
// AvailableOutgoingBitrate float64
|
||||
// AvailableIncomingBitrate float64
|
||||
// CircuitBreakerTriggerCount uint32
|
||||
// RequestsReceived uint64
|
||||
// RequestsSent uint64
|
||||
// ResponsesReceived uint64
|
||||
// ResponsesSent uint64
|
||||
// RetransmissionsReceived uint64
|
||||
// RetransmissionsSent uint64
|
||||
// ConsentRequestsSent uint64
|
||||
// ConsentExpiredTimestamp time.Time
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
resultChan <- result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("error getting candidate pairs stats %v", err)
|
||||
return []CandidatePairStats{}
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
// GetLocalCandidatesStats returns a list of local candidates stats
|
||||
func (a *Agent) GetLocalCandidatesStats() []CandidateStats {
|
||||
resultChan := make(chan []CandidateStats)
|
||||
err := a.run(func(agent *Agent) {
|
||||
result := make([]CandidateStats, 0, len(agent.localCandidates))
|
||||
for networkType, localCandidates := range agent.localCandidates {
|
||||
for _, c := range localCandidates {
|
||||
stat := CandidateStats{
|
||||
Timestamp: time.Now(),
|
||||
ID: c.ID(),
|
||||
NetworkType: networkType,
|
||||
IP: c.Address(),
|
||||
Port: c.Port(),
|
||||
CandidateType: c.Type(),
|
||||
Priority: c.Priority(),
|
||||
// URL string
|
||||
RelayProtocol: "udp",
|
||||
// Deleted bool
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
}
|
||||
resultChan <- result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("error getting candidate pairs stats %v", err)
|
||||
return []CandidateStats{}
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
// GetRemoteCandidatesStats returns a list of remote candidates stats
|
||||
func (a *Agent) GetRemoteCandidatesStats() []CandidateStats {
|
||||
resultChan := make(chan []CandidateStats)
|
||||
err := a.run(func(agent *Agent) {
|
||||
result := make([]CandidateStats, 0, len(agent.remoteCandidates))
|
||||
for networkType, localCandidates := range agent.remoteCandidates {
|
||||
for _, c := range localCandidates {
|
||||
stat := CandidateStats{
|
||||
Timestamp: time.Now(),
|
||||
ID: c.ID(),
|
||||
NetworkType: networkType,
|
||||
IP: c.Address(),
|
||||
Port: c.Port(),
|
||||
CandidateType: c.Type(),
|
||||
Priority: c.Priority(),
|
||||
// URL string
|
||||
RelayProtocol: "udp",
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
}
|
||||
resultChan <- result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("error getting candidate pairs stats %v", err)
|
||||
return []CandidateStats{}
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
// Role represents ICE agent role, which can be controlling or controlled.
|
||||
type Role byte
|
||||
|
||||
|
||||
+303
-1
@@ -116,7 +116,7 @@ func TestPairPriority(t *testing.T) {
|
||||
p = a.addPair(hostLocal, remote)
|
||||
}
|
||||
|
||||
p.state = candidatePairStateValid
|
||||
p.state = CandidatePairStateSucceeded
|
||||
bestPair := a.getBestValidCandidatePair()
|
||||
if bestPair.String() != (&candidatePair{remote: remote, local: hostLocal}).String() {
|
||||
t.Fatalf("Unexpected bestPair %s (expected remote: %s)", bestPair, remote)
|
||||
@@ -617,3 +617,305 @@ func TestInvalidGather(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCandidatePairStats(t *testing.T) {
|
||||
// avoid deadlocks?
|
||||
defer test.TimeOut(1 * time.Second).Stop()
|
||||
|
||||
a, err := NewAgent(&AgentConfig{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create agent: %s", err)
|
||||
}
|
||||
|
||||
hostLocal, err := NewCandidateHost(
|
||||
"udp",
|
||||
"192.168.1.1", 19216,
|
||||
1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct local host candidate: %s", err)
|
||||
}
|
||||
|
||||
relayRemote, err := NewCandidateRelay(
|
||||
"udp",
|
||||
"1.2.3.4", 2340,
|
||||
1,
|
||||
"4.3.2.1", 43210,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote relay candidate: %s", err)
|
||||
}
|
||||
|
||||
srflxRemote, err := NewCandidateServerReflexive(
|
||||
"udp",
|
||||
"10.10.10.2", 19218,
|
||||
1,
|
||||
"4.3.2.1", 43212,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote srflx candidate: %s", err)
|
||||
}
|
||||
|
||||
prflxRemote, err := NewCandidatePeerReflexive(
|
||||
"udp",
|
||||
"10.10.10.2", 19217,
|
||||
1,
|
||||
"4.3.2.1", 43211,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote prflx candidate: %s", err)
|
||||
}
|
||||
|
||||
hostRemote, err := NewCandidateHost(
|
||||
"udp",
|
||||
"1.2.3.5", 12350,
|
||||
1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote host candidate: %s", err)
|
||||
}
|
||||
|
||||
for _, remote := range []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} {
|
||||
p := a.findPair(hostLocal, remote)
|
||||
|
||||
if p == nil {
|
||||
a.addPair(hostLocal, remote)
|
||||
}
|
||||
}
|
||||
|
||||
p := a.findPair(hostLocal, prflxRemote)
|
||||
p.state = CandidatePairStateFailed
|
||||
|
||||
stats := a.GetCandidatePairsStats()
|
||||
if len(stats) != 4 {
|
||||
t.Fatal("expected 4 candidate pairs stats")
|
||||
}
|
||||
|
||||
var relayPairStat, srflxPairStat, prflxPairStat, hostPairStat CandidatePairStats
|
||||
|
||||
for _, cps := range stats {
|
||||
if cps.LocalCandidateID != hostLocal.ID() {
|
||||
t.Fatal("invalid local candidate id")
|
||||
}
|
||||
switch cps.RemoteCandidateID {
|
||||
case relayRemote.ID():
|
||||
relayPairStat = cps
|
||||
case srflxRemote.ID():
|
||||
srflxPairStat = cps
|
||||
case prflxRemote.ID():
|
||||
prflxPairStat = cps
|
||||
case hostRemote.ID():
|
||||
hostPairStat = cps
|
||||
default:
|
||||
t.Fatal("invalid remote candidate ID")
|
||||
}
|
||||
}
|
||||
|
||||
if relayPairStat.RemoteCandidateID != relayRemote.ID() {
|
||||
t.Fatal("missing host-relay pair stat")
|
||||
}
|
||||
|
||||
if srflxPairStat.RemoteCandidateID != srflxRemote.ID() {
|
||||
t.Fatal("missing host-srflx pair stat")
|
||||
}
|
||||
|
||||
if prflxPairStat.RemoteCandidateID != prflxRemote.ID() {
|
||||
t.Fatal("missing host-prflx pair stat")
|
||||
}
|
||||
|
||||
if hostPairStat.RemoteCandidateID != hostRemote.ID() {
|
||||
t.Fatal("missing host-host pair stat")
|
||||
}
|
||||
|
||||
if prflxPairStat.State != CandidatePairStateFailed {
|
||||
t.Fatalf("expected host-prfflx pair to have state failed, it has state %s instead",
|
||||
prflxPairStat.State.String())
|
||||
}
|
||||
|
||||
if err := a.Close(); err != nil {
|
||||
t.Fatalf("Error on agent.Close(): %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalCandidateStats(t *testing.T) {
|
||||
// avoid deadlocks?
|
||||
defer test.TimeOut(1 * time.Second).Stop()
|
||||
|
||||
a, err := NewAgent(&AgentConfig{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create agent: %s", err)
|
||||
}
|
||||
|
||||
hostLocal, err := NewCandidateHost(
|
||||
"udp",
|
||||
"192.168.1.1", 19216,
|
||||
1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct local host candidate: %s", err)
|
||||
}
|
||||
|
||||
srflxLocal, err := NewCandidateServerReflexive(
|
||||
"udp",
|
||||
"192.168.1.1", 19217,
|
||||
1,
|
||||
"4.3.2.1", 43212,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct local srflx candidate: %s", err)
|
||||
}
|
||||
|
||||
a.localCandidates[NetworkTypeUDP4] = []Candidate{hostLocal, srflxLocal}
|
||||
|
||||
localStats := a.GetLocalCandidatesStats()
|
||||
if len(localStats) != 2 {
|
||||
t.Fatalf("expected 2 local candidates stats, got %d instead", len(localStats))
|
||||
}
|
||||
|
||||
var hostLocalStat, srflxLocalStat CandidateStats
|
||||
for _, stats := range localStats {
|
||||
var candidate Candidate
|
||||
switch stats.ID {
|
||||
case hostLocal.ID():
|
||||
hostLocalStat = stats
|
||||
candidate = hostLocal
|
||||
case srflxLocal.ID():
|
||||
srflxLocalStat = stats
|
||||
candidate = srflxLocal
|
||||
default:
|
||||
t.Fatal("invalid local candidate ID")
|
||||
}
|
||||
|
||||
if stats.CandidateType != candidate.Type() {
|
||||
t.Fatal("invalid stats CandidateType")
|
||||
}
|
||||
|
||||
if stats.Priority != candidate.Priority() {
|
||||
t.Fatal("invalid stats CandidateType")
|
||||
}
|
||||
|
||||
if stats.IP != candidate.Address() {
|
||||
t.Fatal("invalid stats IP")
|
||||
}
|
||||
}
|
||||
|
||||
if hostLocalStat.ID != hostLocal.ID() {
|
||||
t.Fatal("missing host local stat")
|
||||
}
|
||||
|
||||
if srflxLocalStat.ID != srflxLocal.ID() {
|
||||
t.Fatal("missing srflx local stat")
|
||||
}
|
||||
|
||||
if err := a.Close(); err != nil {
|
||||
t.Fatalf("Error on agent.Close(): %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteCandidateStats(t *testing.T) {
|
||||
// avoid deadlocks?
|
||||
defer test.TimeOut(1 * time.Second).Stop()
|
||||
|
||||
a, err := NewAgent(&AgentConfig{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create agent: %s", err)
|
||||
}
|
||||
|
||||
relayRemote, err := NewCandidateRelay(
|
||||
"udp",
|
||||
"1.2.3.4", 12340,
|
||||
1,
|
||||
"4.3.2.1", 43210,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote relay candidate: %s", err)
|
||||
}
|
||||
|
||||
srflxRemote, err := NewCandidateServerReflexive(
|
||||
"udp",
|
||||
"10.10.10.2", 19218,
|
||||
1,
|
||||
"4.3.2.1", 43212,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote srflx candidate: %s", err)
|
||||
}
|
||||
|
||||
prflxRemote, err := NewCandidatePeerReflexive(
|
||||
"udp",
|
||||
"10.10.10.2", 19217,
|
||||
1,
|
||||
"4.3.2.1", 43211,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote prflx candidate: %s", err)
|
||||
}
|
||||
|
||||
hostRemote, err := NewCandidateHost(
|
||||
"udp",
|
||||
"1.2.3.5", 12350,
|
||||
1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct remote host candidate: %s", err)
|
||||
}
|
||||
|
||||
a.remoteCandidates[NetworkTypeUDP4] = []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote}
|
||||
|
||||
remoteStats := a.GetRemoteCandidatesStats()
|
||||
if len(remoteStats) != 4 {
|
||||
t.Fatalf("expected 4 remote candidates stats, got %d instead", len(remoteStats))
|
||||
}
|
||||
var relayRemoteStat, srflxRemoteStat, prflxRemoteStat, hostRemoteStat CandidateStats
|
||||
for _, stats := range remoteStats {
|
||||
var candidate Candidate
|
||||
switch stats.ID {
|
||||
case relayRemote.ID():
|
||||
relayRemoteStat = stats
|
||||
candidate = relayRemote
|
||||
case srflxRemote.ID():
|
||||
srflxRemoteStat = stats
|
||||
candidate = srflxRemote
|
||||
case prflxRemote.ID():
|
||||
prflxRemoteStat = stats
|
||||
candidate = prflxRemote
|
||||
case hostRemote.ID():
|
||||
hostRemoteStat = stats
|
||||
candidate = hostRemote
|
||||
default:
|
||||
t.Fatal("invalid remote candidate ID")
|
||||
}
|
||||
|
||||
if stats.CandidateType != candidate.Type() {
|
||||
t.Fatal("invalid stats CandidateType")
|
||||
}
|
||||
|
||||
if stats.Priority != candidate.Priority() {
|
||||
t.Fatal("invalid stats CandidateType")
|
||||
}
|
||||
|
||||
if stats.IP != candidate.Address() {
|
||||
t.Fatal("invalid stats IP")
|
||||
}
|
||||
}
|
||||
|
||||
if relayRemoteStat.ID != relayRemote.ID() {
|
||||
t.Fatal("missing relay remote stat")
|
||||
}
|
||||
|
||||
if srflxRemoteStat.ID != srflxRemote.ID() {
|
||||
t.Fatal("missing srflx remote stat")
|
||||
}
|
||||
|
||||
if prflxRemoteStat.ID != prflxRemote.ID() {
|
||||
t.Fatal("missing prflx remote stat")
|
||||
}
|
||||
|
||||
if hostRemoteStat.ID != hostRemote.ID() {
|
||||
t.Fatal("missing host remote stat")
|
||||
}
|
||||
|
||||
if err := a.Close(); err != nil {
|
||||
t.Fatalf("Error on agent.Close(): %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ const (
|
||||
|
||||
// Candidate represents an ICE candidate
|
||||
type Candidate interface {
|
||||
ID() string
|
||||
Component() uint16
|
||||
Address() string
|
||||
LastReceived() time.Time
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type candidateBase struct {
|
||||
id string
|
||||
networkType NetworkType
|
||||
candidateType CandidateType
|
||||
|
||||
@@ -31,6 +32,11 @@ type candidateBase struct {
|
||||
closedCh chan struct{}
|
||||
}
|
||||
|
||||
// ID returns Candidate ID
|
||||
func (c *candidateBase) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
// Address returns Candidate Address
|
||||
func (c *candidateBase) Address() string {
|
||||
return c.address
|
||||
|
||||
@@ -14,8 +14,14 @@ type CandidateHost struct {
|
||||
|
||||
// NewCandidateHost creates a new host candidate
|
||||
func NewCandidateHost(network string, address string, port int, component uint16) (*CandidateHost, error) {
|
||||
candidateID, err := generateCandidateID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &CandidateHost{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
address: address,
|
||||
candidateType: CandidateTypeHost,
|
||||
component: component,
|
||||
|
||||
@@ -19,8 +19,14 @@ func NewCandidatePeerReflexive(network string, address string, port int, compone
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidateID, err := generateCandidateID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidatePeerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypePeerReflexive,
|
||||
address: address,
|
||||
|
||||
@@ -29,8 +29,14 @@ func NewCandidateRelay(network string, address string, port int, component uint1
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidateID, err := generateCandidateID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidateRelay{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeRelay,
|
||||
address: address,
|
||||
|
||||
@@ -19,8 +19,14 @@ func NewCandidateServerReflexive(network string, address string, port int, compo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidateID, err := generateCandidateID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidateServerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeServerReflexive,
|
||||
address: address,
|
||||
|
||||
@@ -2,7 +2,6 @@ package ice
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MulticastDNSMode represents the different Multicast modes ICE can run in
|
||||
@@ -28,5 +27,5 @@ func generateMulticastDNSName() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%X-%X-%X-%X-%X.local", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil
|
||||
return generateRandString("", ".local")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package ice
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CandidatePairStats contains ICE candidate pair statistics
|
||||
type CandidatePairStats struct {
|
||||
// Timestamp is the timestamp associated with this object.
|
||||
Timestamp time.Time
|
||||
|
||||
// LocalCandidateID is the ID of the local candidate
|
||||
LocalCandidateID string
|
||||
|
||||
// RemoteCandidateID is the ID of the remote candidate
|
||||
RemoteCandidateID string
|
||||
|
||||
// State represents the state of the checklist for the local and remote
|
||||
// candidates in a pair.
|
||||
State CandidatePairState
|
||||
|
||||
// Nominated is true when this valid pair that should be used for media
|
||||
// if it is the highest-priority one amongst those whose nominated flag is set
|
||||
Nominated bool
|
||||
|
||||
// PacketsSent represents the total number of packets sent on this candidate pair.
|
||||
PacketsSent uint32
|
||||
|
||||
// PacketsReceived represents the total number of packets received on this candidate pair.
|
||||
PacketsReceived uint32
|
||||
|
||||
// BytesSent represents the total number of payload bytes sent on this candidate pair
|
||||
// not including headers or padding.
|
||||
BytesSent uint64
|
||||
|
||||
// BytesReceived represents the total number of payload bytes received on this candidate pair
|
||||
// not including headers or padding.
|
||||
BytesReceived uint64
|
||||
|
||||
// LastPacketSentTimestamp represents the timestamp at which the last packet was
|
||||
// sent on this particular candidate pair, excluding STUN packets.
|
||||
LastPacketSentTimestamp time.Time
|
||||
|
||||
// LastPacketReceivedTimestamp represents the timestamp at which the last packet
|
||||
// was received on this particular candidate pair, excluding STUN packets.
|
||||
LastPacketReceivedTimestamp time.Time
|
||||
|
||||
// FirstRequestTimestamp represents the timestamp at which the first STUN request
|
||||
// was sent on this particular candidate pair.
|
||||
FirstRequestTimestamp time.Time
|
||||
|
||||
// LastRequestTimestamp represents the timestamp at which the last STUN request
|
||||
// was sent on this particular candidate pair. The average interval between two
|
||||
// consecutive connectivity checks sent can be calculated with
|
||||
// (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent.
|
||||
LastRequestTimestamp time.Time
|
||||
|
||||
// LastResponseTimestamp represents the timestamp at which the last STUN response
|
||||
// was received on this particular candidate pair.
|
||||
LastResponseTimestamp time.Time
|
||||
|
||||
// TotalRoundTripTime represents the sum of all round trip time measurements
|
||||
// in seconds since the beginning of the session, based on STUN connectivity
|
||||
// check responses (ResponsesReceived), including those that reply to requests
|
||||
// that are sent in order to verify consent. The average round trip time can
|
||||
// be computed from TotalRoundTripTime by dividing it by ResponsesReceived.
|
||||
TotalRoundTripTime float64
|
||||
|
||||
// CurrentRoundTripTime represents the latest round trip time measured in seconds,
|
||||
// computed from both STUN connectivity checks, including those that are sent
|
||||
// for consent verification.
|
||||
CurrentRoundTripTime float64
|
||||
|
||||
// AvailableOutgoingBitrate is calculated by the underlying congestion control
|
||||
// by combining the available bitrate for all the outgoing RTP streams using
|
||||
// this candidate pair. The bitrate measurement does not count the size of the
|
||||
// IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
|
||||
// in RFC 3890, i.e., it is measured in bits per second and the bitrate is calculated
|
||||
// over a 1 second window.
|
||||
AvailableOutgoingBitrate float64
|
||||
|
||||
// AvailableIncomingBitrate is calculated by the underlying congestion control
|
||||
// by combining the available bitrate for all the incoming RTP streams using
|
||||
// this candidate pair. The bitrate measurement does not count the size of the
|
||||
// IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
|
||||
// in RFC 3890, i.e., it is measured in bits per second and the bitrate is
|
||||
// calculated over a 1 second window.
|
||||
AvailableIncomingBitrate float64
|
||||
|
||||
// CircuitBreakerTriggerCount represents the number of times the circuit breaker
|
||||
// is triggered for this particular 5-tuple, ceasing transmission.
|
||||
CircuitBreakerTriggerCount uint32
|
||||
|
||||
// RequestsReceived represents the total number of connectivity check requests
|
||||
// received (including retransmissions). It is impossible for the receiver to
|
||||
// tell whether the request was sent in order to check connectivity or check
|
||||
// consent, so all connectivity checks requests are counted here.
|
||||
RequestsReceived uint64
|
||||
|
||||
// RequestsSent represents the total number of connectivity check requests
|
||||
// sent (not including retransmissions).
|
||||
RequestsSent uint64
|
||||
|
||||
// ResponsesReceived represents the total number of connectivity check responses received.
|
||||
ResponsesReceived uint64
|
||||
|
||||
// ResponsesSent epresents the total number of connectivity check responses sent.
|
||||
// Since we cannot distinguish connectivity check requests and consent requests,
|
||||
// all responses are counted.
|
||||
ResponsesSent uint64
|
||||
|
||||
// RetransmissionsReceived represents the total number of connectivity check
|
||||
// request retransmissions received.
|
||||
RetransmissionsReceived uint64
|
||||
|
||||
// RetransmissionsSent represents the total number of connectivity check
|
||||
// request retransmissions sent.
|
||||
RetransmissionsSent uint64
|
||||
|
||||
// ConsentRequestsSent represents the total number of consent requests sent.
|
||||
ConsentRequestsSent uint64
|
||||
|
||||
// ConsentExpiredTimestamp represents the timestamp at which the latest valid
|
||||
// STUN binding response expired.
|
||||
ConsentExpiredTimestamp time.Time
|
||||
}
|
||||
|
||||
// CandidateStats contains ICE candidate statistics related to the ICETransport objects.
|
||||
type CandidateStats struct {
|
||||
// Timestamp is the timestamp associated with this object.
|
||||
Timestamp time.Time
|
||||
|
||||
// ID is the candidate ID
|
||||
ID string
|
||||
|
||||
// NetworkType represents the type of network interface used by the base of a
|
||||
// local candidate (the address the ICE agent sends from). Only present for
|
||||
// local candidates; it's not possible to know what type of network interface
|
||||
// a remote candidate is using.
|
||||
//
|
||||
// Note:
|
||||
// This stat only tells you about the network interface used by the first "hop";
|
||||
// it's possible that a connection will be bottlenecked by another type of network.
|
||||
// For example, when using Wi-Fi tethering, the networkType of the relevant candidate
|
||||
// would be "wifi", even when the next hop is over a cellular connection.
|
||||
NetworkType NetworkType
|
||||
|
||||
// IP is the IP address of the candidate, allowing for IPv4 addresses and
|
||||
// IPv6 addresses, but fully qualified domain names (FQDNs) are not allowed.
|
||||
IP string
|
||||
|
||||
// Port is the port number of the candidate.
|
||||
Port int
|
||||
|
||||
// CandidateType is the "Type" field of the ICECandidate.
|
||||
CandidateType CandidateType
|
||||
|
||||
// Priority is the "Priority" field of the ICECandidate.
|
||||
Priority uint32
|
||||
|
||||
// URL is the URL of the TURN or STUN server indicated in the that translated
|
||||
// this IP address. It is the URL address surfaced in an PeerConnectionICEEvent.
|
||||
URL string
|
||||
|
||||
// RelayProtocol is the protocol used by the endpoint to communicate with the
|
||||
// TURN server. This is only present for local candidates. Valid values for
|
||||
// the TURN URL protocol is one of udp, tcp, or tls.
|
||||
RelayProtocol string
|
||||
|
||||
// Deleted is true if the candidate has been deleted/freed. For host candidates,
|
||||
// this means that any network resources (typically a socket) associated with the
|
||||
// candidate have been released. For TURN candidates, this means the TURN allocation
|
||||
// is no longer active.
|
||||
//
|
||||
// Only defined for local candidates. For remote candidates, this property is not applicable.
|
||||
Deleted bool
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
@@ -73,3 +74,18 @@ func addrEqual(a, b net.Addr) bool {
|
||||
|
||||
return aType == bType && aIP.Equal(bIP) && aPort == bPort
|
||||
}
|
||||
|
||||
func generateCandidateID() (string, error) {
|
||||
return generateRandString("candidate:", "")
|
||||
}
|
||||
|
||||
func generateRandString(prefix, sufix string) (string, error) {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b) //nolint
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%X-%X-%X-%X-%X%s", prefix, b[0:4], b[4:6], b[6:8], b[8:10], b[10:], sufix), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user