github.com/pion/webrtc/v4@v4.0.1/icecandidate.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package webrtc
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/pion/ice/v4"
    10  )
    11  
    12  // ICECandidate represents a ice candidate
    13  type ICECandidate struct {
    14  	statsID        string
    15  	Foundation     string           `json:"foundation"`
    16  	Priority       uint32           `json:"priority"`
    17  	Address        string           `json:"address"`
    18  	Protocol       ICEProtocol      `json:"protocol"`
    19  	Port           uint16           `json:"port"`
    20  	Typ            ICECandidateType `json:"type"`
    21  	Component      uint16           `json:"component"`
    22  	RelatedAddress string           `json:"relatedAddress"`
    23  	RelatedPort    uint16           `json:"relatedPort"`
    24  	TCPType        string           `json:"tcpType"`
    25  }
    26  
    27  // Conversion for package ice
    28  
    29  func newICECandidatesFromICE(iceCandidates []ice.Candidate) ([]ICECandidate, error) {
    30  	candidates := []ICECandidate{}
    31  
    32  	for _, i := range iceCandidates {
    33  		c, err := newICECandidateFromICE(i)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		candidates = append(candidates, c)
    38  	}
    39  
    40  	return candidates, nil
    41  }
    42  
    43  func newICECandidateFromICE(i ice.Candidate) (ICECandidate, error) {
    44  	typ, err := convertTypeFromICE(i.Type())
    45  	if err != nil {
    46  		return ICECandidate{}, err
    47  	}
    48  	protocol, err := NewICEProtocol(i.NetworkType().NetworkShort())
    49  	if err != nil {
    50  		return ICECandidate{}, err
    51  	}
    52  
    53  	c := ICECandidate{
    54  		statsID:    i.ID(),
    55  		Foundation: i.Foundation(),
    56  		Priority:   i.Priority(),
    57  		Address:    i.Address(),
    58  		Protocol:   protocol,
    59  		Port:       uint16(i.Port()),
    60  		Component:  i.Component(),
    61  		Typ:        typ,
    62  		TCPType:    i.TCPType().String(),
    63  	}
    64  
    65  	if i.RelatedAddress() != nil {
    66  		c.RelatedAddress = i.RelatedAddress().Address
    67  		c.RelatedPort = uint16(i.RelatedAddress().Port)
    68  	}
    69  
    70  	return c, nil
    71  }
    72  
    73  func (c ICECandidate) toICE() (ice.Candidate, error) {
    74  	candidateID := c.statsID
    75  	switch c.Typ {
    76  	case ICECandidateTypeHost:
    77  		config := ice.CandidateHostConfig{
    78  			CandidateID: candidateID,
    79  			Network:     c.Protocol.String(),
    80  			Address:     c.Address,
    81  			Port:        int(c.Port),
    82  			Component:   c.Component,
    83  			TCPType:     ice.NewTCPType(c.TCPType),
    84  			Foundation:  c.Foundation,
    85  			Priority:    c.Priority,
    86  		}
    87  		return ice.NewCandidateHost(&config)
    88  	case ICECandidateTypeSrflx:
    89  		config := ice.CandidateServerReflexiveConfig{
    90  			CandidateID: candidateID,
    91  			Network:     c.Protocol.String(),
    92  			Address:     c.Address,
    93  			Port:        int(c.Port),
    94  			Component:   c.Component,
    95  			Foundation:  c.Foundation,
    96  			Priority:    c.Priority,
    97  			RelAddr:     c.RelatedAddress,
    98  			RelPort:     int(c.RelatedPort),
    99  		}
   100  		return ice.NewCandidateServerReflexive(&config)
   101  	case ICECandidateTypePrflx:
   102  		config := ice.CandidatePeerReflexiveConfig{
   103  			CandidateID: candidateID,
   104  			Network:     c.Protocol.String(),
   105  			Address:     c.Address,
   106  			Port:        int(c.Port),
   107  			Component:   c.Component,
   108  			Foundation:  c.Foundation,
   109  			Priority:    c.Priority,
   110  			RelAddr:     c.RelatedAddress,
   111  			RelPort:     int(c.RelatedPort),
   112  		}
   113  		return ice.NewCandidatePeerReflexive(&config)
   114  	case ICECandidateTypeRelay:
   115  		config := ice.CandidateRelayConfig{
   116  			CandidateID: candidateID,
   117  			Network:     c.Protocol.String(),
   118  			Address:     c.Address,
   119  			Port:        int(c.Port),
   120  			Component:   c.Component,
   121  			Foundation:  c.Foundation,
   122  			Priority:    c.Priority,
   123  			RelAddr:     c.RelatedAddress,
   124  			RelPort:     int(c.RelatedPort),
   125  		}
   126  		return ice.NewCandidateRelay(&config)
   127  	default:
   128  		return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ)
   129  	}
   130  }
   131  
   132  func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
   133  	switch t {
   134  	case ice.CandidateTypeHost:
   135  		return ICECandidateTypeHost, nil
   136  	case ice.CandidateTypeServerReflexive:
   137  		return ICECandidateTypeSrflx, nil
   138  	case ice.CandidateTypePeerReflexive:
   139  		return ICECandidateTypePrflx, nil
   140  	case ice.CandidateTypeRelay:
   141  		return ICECandidateTypeRelay, nil
   142  	default:
   143  		return ICECandidateType(t), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, t)
   144  	}
   145  }
   146  
   147  func (c ICECandidate) String() string {
   148  	ic, err := c.toICE()
   149  	if err != nil {
   150  		return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err)
   151  	}
   152  	return ic.String()
   153  }
   154  
   155  // ToJSON returns an ICECandidateInit
   156  // as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
   157  func (c ICECandidate) ToJSON() ICECandidateInit {
   158  	zeroVal := uint16(0)
   159  	emptyStr := ""
   160  	candidateStr := ""
   161  
   162  	candidate, err := c.toICE()
   163  	if err == nil {
   164  		candidateStr = candidate.Marshal()
   165  	}
   166  
   167  	return ICECandidateInit{
   168  		Candidate:     fmt.Sprintf("candidate:%s", candidateStr),
   169  		SDPMid:        &emptyStr,
   170  		SDPMLineIndex: &zeroVal,
   171  	}
   172  }