github.com/pion/webrtc/v3@v3.2.24/icegatherer.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build !js
     5  // +build !js
     6  
     7  package webrtc
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  	"sync/atomic"
    13  
    14  	"github.com/pion/ice/v2"
    15  	"github.com/pion/logging"
    16  	"github.com/pion/stun"
    17  )
    18  
    19  // ICEGatherer gathers local host, server reflexive and relay
    20  // candidates, as well as enabling the retrieval of local Interactive
    21  // Connectivity Establishment (ICE) parameters which can be
    22  // exchanged in signaling.
    23  type ICEGatherer struct {
    24  	lock  sync.RWMutex
    25  	log   logging.LeveledLogger
    26  	state ICEGathererState
    27  
    28  	validatedServers []*stun.URI
    29  	gatherPolicy     ICETransportPolicy
    30  
    31  	agent *ice.Agent
    32  
    33  	onLocalCandidateHandler atomic.Value // func(candidate *ICECandidate)
    34  	onStateChangeHandler    atomic.Value // func(state ICEGathererState)
    35  
    36  	// Used for GatheringCompletePromise
    37  	onGatheringCompleteHandler atomic.Value // func()
    38  
    39  	api *API
    40  }
    41  
    42  // NewICEGatherer creates a new NewICEGatherer.
    43  // This constructor is part of the ORTC API. It is not
    44  // meant to be used together with the basic WebRTC API.
    45  func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, error) {
    46  	var validatedServers []*stun.URI
    47  	if len(opts.ICEServers) > 0 {
    48  		for _, server := range opts.ICEServers {
    49  			url, err := server.urls()
    50  			if err != nil {
    51  				return nil, err
    52  			}
    53  			validatedServers = append(validatedServers, url...)
    54  		}
    55  	}
    56  
    57  	return &ICEGatherer{
    58  		state:            ICEGathererStateNew,
    59  		gatherPolicy:     opts.ICEGatherPolicy,
    60  		validatedServers: validatedServers,
    61  		api:              api,
    62  		log:              api.settingEngine.LoggerFactory.NewLogger("ice"),
    63  	}, nil
    64  }
    65  
    66  func (g *ICEGatherer) createAgent() error {
    67  	g.lock.Lock()
    68  	defer g.lock.Unlock()
    69  
    70  	if g.agent != nil || g.State() != ICEGathererStateNew {
    71  		return nil
    72  	}
    73  
    74  	candidateTypes := []ice.CandidateType{}
    75  	if g.api.settingEngine.candidates.ICELite {
    76  		candidateTypes = append(candidateTypes, ice.CandidateTypeHost)
    77  	} else if g.gatherPolicy == ICETransportPolicyRelay {
    78  		candidateTypes = append(candidateTypes, ice.CandidateTypeRelay)
    79  	}
    80  
    81  	var nat1To1CandiTyp ice.CandidateType
    82  	switch g.api.settingEngine.candidates.NAT1To1IPCandidateType {
    83  	case ICECandidateTypeHost:
    84  		nat1To1CandiTyp = ice.CandidateTypeHost
    85  	case ICECandidateTypeSrflx:
    86  		nat1To1CandiTyp = ice.CandidateTypeServerReflexive
    87  	default:
    88  		nat1To1CandiTyp = ice.CandidateTypeUnspecified
    89  	}
    90  
    91  	mDNSMode := g.api.settingEngine.candidates.MulticastDNSMode
    92  	if mDNSMode != ice.MulticastDNSModeDisabled && mDNSMode != ice.MulticastDNSModeQueryAndGather {
    93  		// If enum is in state we don't recognized default to MulticastDNSModeQueryOnly
    94  		mDNSMode = ice.MulticastDNSModeQueryOnly
    95  	}
    96  
    97  	config := &ice.AgentConfig{
    98  		Lite:                   g.api.settingEngine.candidates.ICELite,
    99  		Urls:                   g.validatedServers,
   100  		PortMin:                g.api.settingEngine.ephemeralUDP.PortMin,
   101  		PortMax:                g.api.settingEngine.ephemeralUDP.PortMax,
   102  		DisconnectedTimeout:    g.api.settingEngine.timeout.ICEDisconnectedTimeout,
   103  		FailedTimeout:          g.api.settingEngine.timeout.ICEFailedTimeout,
   104  		KeepaliveInterval:      g.api.settingEngine.timeout.ICEKeepaliveInterval,
   105  		LoggerFactory:          g.api.settingEngine.LoggerFactory,
   106  		CandidateTypes:         candidateTypes,
   107  		HostAcceptanceMinWait:  g.api.settingEngine.timeout.ICEHostAcceptanceMinWait,
   108  		SrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICESrflxAcceptanceMinWait,
   109  		PrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICEPrflxAcceptanceMinWait,
   110  		RelayAcceptanceMinWait: g.api.settingEngine.timeout.ICERelayAcceptanceMinWait,
   111  		InterfaceFilter:        g.api.settingEngine.candidates.InterfaceFilter,
   112  		IPFilter:               g.api.settingEngine.candidates.IPFilter,
   113  		NAT1To1IPs:             g.api.settingEngine.candidates.NAT1To1IPs,
   114  		NAT1To1IPCandidateType: nat1To1CandiTyp,
   115  		IncludeLoopback:        g.api.settingEngine.candidates.IncludeLoopbackCandidate,
   116  		Net:                    g.api.settingEngine.net,
   117  		MulticastDNSMode:       mDNSMode,
   118  		MulticastDNSHostName:   g.api.settingEngine.candidates.MulticastDNSHostName,
   119  		LocalUfrag:             g.api.settingEngine.candidates.UsernameFragment,
   120  		LocalPwd:               g.api.settingEngine.candidates.Password,
   121  		TCPMux:                 g.api.settingEngine.iceTCPMux,
   122  		UDPMux:                 g.api.settingEngine.iceUDPMux,
   123  		ProxyDialer:            g.api.settingEngine.iceProxyDialer,
   124  		DisableActiveTCP:       g.api.settingEngine.iceDisableActiveTCP,
   125  	}
   126  
   127  	requestedNetworkTypes := g.api.settingEngine.candidates.ICENetworkTypes
   128  	if len(requestedNetworkTypes) == 0 {
   129  		requestedNetworkTypes = supportedNetworkTypes()
   130  	}
   131  
   132  	for _, typ := range requestedNetworkTypes {
   133  		config.NetworkTypes = append(config.NetworkTypes, ice.NetworkType(typ))
   134  	}
   135  
   136  	agent, err := ice.NewAgent(config)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	g.agent = agent
   142  	return nil
   143  }
   144  
   145  // Gather ICE candidates.
   146  func (g *ICEGatherer) Gather() error {
   147  	if err := g.createAgent(); err != nil {
   148  		return err
   149  	}
   150  
   151  	agent := g.getAgent()
   152  	// it is possible agent had just been closed
   153  	if agent == nil {
   154  		return fmt.Errorf("%w: unable to gather", errICEAgentNotExist)
   155  	}
   156  
   157  	g.setState(ICEGathererStateGathering)
   158  	if err := agent.OnCandidate(func(candidate ice.Candidate) {
   159  		onLocalCandidateHandler := func(*ICECandidate) {}
   160  		if handler, ok := g.onLocalCandidateHandler.Load().(func(candidate *ICECandidate)); ok && handler != nil {
   161  			onLocalCandidateHandler = handler
   162  		}
   163  
   164  		onGatheringCompleteHandler := func() {}
   165  		if handler, ok := g.onGatheringCompleteHandler.Load().(func()); ok && handler != nil {
   166  			onGatheringCompleteHandler = handler
   167  		}
   168  
   169  		if candidate != nil {
   170  			c, err := newICECandidateFromICE(candidate)
   171  			if err != nil {
   172  				g.log.Warnf("Failed to convert ice.Candidate: %s", err)
   173  				return
   174  			}
   175  			onLocalCandidateHandler(&c)
   176  		} else {
   177  			g.setState(ICEGathererStateComplete)
   178  
   179  			onGatheringCompleteHandler()
   180  			onLocalCandidateHandler(nil)
   181  		}
   182  	}); err != nil {
   183  		return err
   184  	}
   185  	return agent.GatherCandidates()
   186  }
   187  
   188  // Close prunes all local candidates, and closes the ports.
   189  func (g *ICEGatherer) Close() error {
   190  	g.lock.Lock()
   191  	defer g.lock.Unlock()
   192  
   193  	if g.agent == nil {
   194  		return nil
   195  	} else if err := g.agent.Close(); err != nil {
   196  		return err
   197  	}
   198  
   199  	g.agent = nil
   200  	g.setState(ICEGathererStateClosed)
   201  
   202  	return nil
   203  }
   204  
   205  // GetLocalParameters returns the ICE parameters of the ICEGatherer.
   206  func (g *ICEGatherer) GetLocalParameters() (ICEParameters, error) {
   207  	if err := g.createAgent(); err != nil {
   208  		return ICEParameters{}, err
   209  	}
   210  
   211  	agent := g.getAgent()
   212  	// it is possible agent had just been closed
   213  	if agent == nil {
   214  		return ICEParameters{}, fmt.Errorf("%w: unable to get local parameters", errICEAgentNotExist)
   215  	}
   216  
   217  	frag, pwd, err := agent.GetLocalUserCredentials()
   218  	if err != nil {
   219  		return ICEParameters{}, err
   220  	}
   221  
   222  	return ICEParameters{
   223  		UsernameFragment: frag,
   224  		Password:         pwd,
   225  		ICELite:          false,
   226  	}, nil
   227  }
   228  
   229  // GetLocalCandidates returns the sequence of valid local candidates associated with the ICEGatherer.
   230  func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) {
   231  	if err := g.createAgent(); err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	agent := g.getAgent()
   236  	// it is possible agent had just been closed
   237  	if agent == nil {
   238  		return nil, fmt.Errorf("%w: unable to get local candidates", errICEAgentNotExist)
   239  	}
   240  
   241  	iceCandidates, err := agent.GetLocalCandidates()
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	return newICECandidatesFromICE(iceCandidates)
   247  }
   248  
   249  // OnLocalCandidate sets an event handler which fires when a new local ICE candidate is available
   250  // Take note that the handler will be called with a nil pointer when gathering is finished.
   251  func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) {
   252  	g.onLocalCandidateHandler.Store(f)
   253  }
   254  
   255  // OnStateChange fires any time the ICEGatherer changes
   256  func (g *ICEGatherer) OnStateChange(f func(ICEGathererState)) {
   257  	g.onStateChangeHandler.Store(f)
   258  }
   259  
   260  // State indicates the current state of the ICE gatherer.
   261  func (g *ICEGatherer) State() ICEGathererState {
   262  	return atomicLoadICEGathererState(&g.state)
   263  }
   264  
   265  func (g *ICEGatherer) setState(s ICEGathererState) {
   266  	atomicStoreICEGathererState(&g.state, s)
   267  
   268  	if handler, ok := g.onStateChangeHandler.Load().(func(state ICEGathererState)); ok && handler != nil {
   269  		handler(s)
   270  	}
   271  }
   272  
   273  func (g *ICEGatherer) getAgent() *ice.Agent {
   274  	g.lock.RLock()
   275  	defer g.lock.RUnlock()
   276  	return g.agent
   277  }
   278  
   279  func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
   280  	agent := g.getAgent()
   281  	if agent == nil {
   282  		return
   283  	}
   284  
   285  	collector.Collecting()
   286  	go func(collector *statsReportCollector, agent *ice.Agent) {
   287  		for _, candidatePairStats := range agent.GetCandidatePairsStats() {
   288  			collector.Collecting()
   289  
   290  			state, err := toStatsICECandidatePairState(candidatePairStats.State)
   291  			if err != nil {
   292  				g.log.Error(err.Error())
   293  			}
   294  
   295  			pairID := newICECandidatePairStatsID(candidatePairStats.LocalCandidateID,
   296  				candidatePairStats.RemoteCandidateID)
   297  
   298  			stats := ICECandidatePairStats{
   299  				Timestamp: statsTimestampFrom(candidatePairStats.Timestamp),
   300  				Type:      StatsTypeCandidatePair,
   301  				ID:        pairID,
   302  				// TransportID:
   303  				LocalCandidateID:            candidatePairStats.LocalCandidateID,
   304  				RemoteCandidateID:           candidatePairStats.RemoteCandidateID,
   305  				State:                       state,
   306  				Nominated:                   candidatePairStats.Nominated,
   307  				PacketsSent:                 candidatePairStats.PacketsSent,
   308  				PacketsReceived:             candidatePairStats.PacketsReceived,
   309  				BytesSent:                   candidatePairStats.BytesSent,
   310  				BytesReceived:               candidatePairStats.BytesReceived,
   311  				LastPacketSentTimestamp:     statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp),
   312  				LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp),
   313  				FirstRequestTimestamp:       statsTimestampFrom(candidatePairStats.FirstRequestTimestamp),
   314  				LastRequestTimestamp:        statsTimestampFrom(candidatePairStats.LastRequestTimestamp),
   315  				LastResponseTimestamp:       statsTimestampFrom(candidatePairStats.LastResponseTimestamp),
   316  				TotalRoundTripTime:          candidatePairStats.TotalRoundTripTime,
   317  				CurrentRoundTripTime:        candidatePairStats.CurrentRoundTripTime,
   318  				AvailableOutgoingBitrate:    candidatePairStats.AvailableOutgoingBitrate,
   319  				AvailableIncomingBitrate:    candidatePairStats.AvailableIncomingBitrate,
   320  				CircuitBreakerTriggerCount:  candidatePairStats.CircuitBreakerTriggerCount,
   321  				RequestsReceived:            candidatePairStats.RequestsReceived,
   322  				RequestsSent:                candidatePairStats.RequestsSent,
   323  				ResponsesReceived:           candidatePairStats.ResponsesReceived,
   324  				ResponsesSent:               candidatePairStats.ResponsesSent,
   325  				RetransmissionsReceived:     candidatePairStats.RetransmissionsReceived,
   326  				RetransmissionsSent:         candidatePairStats.RetransmissionsSent,
   327  				ConsentRequestsSent:         candidatePairStats.ConsentRequestsSent,
   328  				ConsentExpiredTimestamp:     statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp),
   329  			}
   330  			collector.Collect(stats.ID, stats)
   331  		}
   332  
   333  		for _, candidateStats := range agent.GetLocalCandidatesStats() {
   334  			collector.Collecting()
   335  
   336  			networkType, err := getNetworkType(candidateStats.NetworkType)
   337  			if err != nil {
   338  				g.log.Error(err.Error())
   339  			}
   340  
   341  			candidateType, err := getCandidateType(candidateStats.CandidateType)
   342  			if err != nil {
   343  				g.log.Error(err.Error())
   344  			}
   345  
   346  			stats := ICECandidateStats{
   347  				Timestamp:     statsTimestampFrom(candidateStats.Timestamp),
   348  				ID:            candidateStats.ID,
   349  				Type:          StatsTypeLocalCandidate,
   350  				IP:            candidateStats.IP,
   351  				Port:          int32(candidateStats.Port),
   352  				Protocol:      networkType.Protocol(),
   353  				CandidateType: candidateType,
   354  				Priority:      int32(candidateStats.Priority),
   355  				URL:           candidateStats.URL,
   356  				RelayProtocol: candidateStats.RelayProtocol,
   357  				Deleted:       candidateStats.Deleted,
   358  			}
   359  			collector.Collect(stats.ID, stats)
   360  		}
   361  
   362  		for _, candidateStats := range agent.GetRemoteCandidatesStats() {
   363  			collector.Collecting()
   364  			networkType, err := getNetworkType(candidateStats.NetworkType)
   365  			if err != nil {
   366  				g.log.Error(err.Error())
   367  			}
   368  
   369  			candidateType, err := getCandidateType(candidateStats.CandidateType)
   370  			if err != nil {
   371  				g.log.Error(err.Error())
   372  			}
   373  
   374  			stats := ICECandidateStats{
   375  				Timestamp:     statsTimestampFrom(candidateStats.Timestamp),
   376  				ID:            candidateStats.ID,
   377  				Type:          StatsTypeRemoteCandidate,
   378  				IP:            candidateStats.IP,
   379  				Port:          int32(candidateStats.Port),
   380  				Protocol:      networkType.Protocol(),
   381  				CandidateType: candidateType,
   382  				Priority:      int32(candidateStats.Priority),
   383  				URL:           candidateStats.URL,
   384  				RelayProtocol: candidateStats.RelayProtocol,
   385  			}
   386  			collector.Collect(stats.ID, stats)
   387  		}
   388  		collector.Done()
   389  	}(collector, agent)
   390  }