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 }