snapshot of https://github.com/pion/webrtc.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s
This commit is contained in:
244
icecandidate.go
Normal file
244
icecandidate.go
Normal file
@@ -0,0 +1,244 @@
|
||||
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/ice/v4"
|
||||
)
|
||||
|
||||
// ICECandidate represents a ice candidate.
|
||||
type ICECandidate struct {
|
||||
statsID string
|
||||
Foundation string `json:"foundation"`
|
||||
Priority uint32 `json:"priority"`
|
||||
Address string `json:"address"`
|
||||
Protocol ICEProtocol `json:"protocol"`
|
||||
Port uint16 `json:"port"`
|
||||
Typ ICECandidateType `json:"type"`
|
||||
Component uint16 `json:"component"`
|
||||
RelatedAddress string `json:"relatedAddress"`
|
||||
RelatedPort uint16 `json:"relatedPort"`
|
||||
TCPType string `json:"tcpType"`
|
||||
SDPMid string `json:"sdpMid"`
|
||||
SDPMLineIndex uint16 `json:"sdpMLineIndex"`
|
||||
extensions string
|
||||
}
|
||||
|
||||
// Conversion for package ice.
|
||||
func newICECandidatesFromICE(
|
||||
iceCandidates []ice.Candidate,
|
||||
sdpMid string,
|
||||
sdpMLineIndex uint16,
|
||||
) ([]ICECandidate, error) {
|
||||
candidates := []ICECandidate{}
|
||||
|
||||
for _, i := range iceCandidates {
|
||||
c, err := newICECandidateFromICE(i, sdpMid, sdpMLineIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidates = append(candidates, c)
|
||||
}
|
||||
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
func newICECandidateFromICE(candidate ice.Candidate, sdpMid string, sdpMLineIndex uint16) (ICECandidate, error) {
|
||||
typ, err := convertTypeFromICE(candidate.Type())
|
||||
if err != nil {
|
||||
return ICECandidate{}, err
|
||||
}
|
||||
protocol, err := NewICEProtocol(candidate.NetworkType().NetworkShort())
|
||||
if err != nil {
|
||||
return ICECandidate{}, err
|
||||
}
|
||||
|
||||
newCandidate := ICECandidate{
|
||||
statsID: candidate.ID(),
|
||||
Foundation: candidate.Foundation(),
|
||||
Priority: candidate.Priority(),
|
||||
Address: candidate.Address(),
|
||||
Protocol: protocol,
|
||||
Port: uint16(candidate.Port()), //nolint:gosec // G115
|
||||
Component: candidate.Component(),
|
||||
Typ: typ,
|
||||
TCPType: candidate.TCPType().String(),
|
||||
SDPMid: sdpMid,
|
||||
SDPMLineIndex: sdpMLineIndex,
|
||||
}
|
||||
|
||||
newCandidate.setExtensions(candidate.Extensions())
|
||||
|
||||
if candidate.RelatedAddress() != nil {
|
||||
newCandidate.RelatedAddress = candidate.RelatedAddress().Address
|
||||
newCandidate.RelatedPort = uint16(candidate.RelatedAddress().Port) //nolint:gosec // G115
|
||||
}
|
||||
|
||||
return newCandidate, nil
|
||||
}
|
||||
|
||||
// ToICE converts ICECandidate to ice.Candidate.
|
||||
func (c ICECandidate) ToICE() (cand ice.Candidate, err error) {
|
||||
candidateID := c.statsID
|
||||
switch c.Typ {
|
||||
case ICECandidateTypeHost:
|
||||
config := ice.CandidateHostConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
TCPType: ice.NewTCPType(c.TCPType),
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
}
|
||||
|
||||
cand, err = ice.NewCandidateHost(&config)
|
||||
case ICECandidateTypeSrflx:
|
||||
config := ice.CandidateServerReflexiveConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
|
||||
cand, err = ice.NewCandidateServerReflexive(&config)
|
||||
case ICECandidateTypePrflx:
|
||||
config := ice.CandidatePeerReflexiveConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
|
||||
cand, err = ice.NewCandidatePeerReflexive(&config)
|
||||
case ICECandidateTypeRelay:
|
||||
config := ice.CandidateRelayConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
|
||||
cand, err = ice.NewCandidateRelay(&config)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ)
|
||||
}
|
||||
|
||||
if cand != nil && err == nil {
|
||||
err = c.exportExtensions(cand)
|
||||
}
|
||||
|
||||
return cand, err
|
||||
}
|
||||
|
||||
func (c *ICECandidate) setExtensions(ext []ice.CandidateExtension) {
|
||||
var extensions strings.Builder
|
||||
|
||||
for i := range ext {
|
||||
if i > 0 {
|
||||
extensions.WriteString(" ")
|
||||
}
|
||||
|
||||
extensions.WriteString(ext[i].Key + " " + ext[i].Value)
|
||||
}
|
||||
|
||||
c.extensions = extensions.String()
|
||||
}
|
||||
|
||||
func (c *ICECandidate) exportExtensions(cand ice.Candidate) error {
|
||||
extensions := c.extensions
|
||||
var ext ice.CandidateExtension
|
||||
var field string
|
||||
|
||||
for i, start := 0, 0; i < len(extensions); i++ {
|
||||
switch {
|
||||
case extensions[i] == ' ':
|
||||
field = extensions[start:i]
|
||||
start = i + 1
|
||||
case i == len(extensions)-1:
|
||||
field = extensions[start:]
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Extension keys can't be empty
|
||||
hasKey := ext.Key != ""
|
||||
if !hasKey {
|
||||
ext.Key = field
|
||||
} else {
|
||||
ext.Value = field
|
||||
}
|
||||
|
||||
// Extension value can be empty
|
||||
if hasKey || i == len(extensions)-1 {
|
||||
if err := cand.AddExtension(ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext = ice.CandidateExtension{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
|
||||
switch t {
|
||||
case ice.CandidateTypeHost:
|
||||
return ICECandidateTypeHost, nil
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
return ICECandidateTypeSrflx, nil
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
return ICECandidateTypePrflx, nil
|
||||
case ice.CandidateTypeRelay:
|
||||
return ICECandidateTypeRelay, nil
|
||||
default:
|
||||
return ICECandidateType(t), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, t)
|
||||
}
|
||||
}
|
||||
|
||||
func (c ICECandidate) String() string {
|
||||
ic, err := c.ToICE()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err)
|
||||
}
|
||||
|
||||
return ic.String()
|
||||
}
|
||||
|
||||
// ToJSON returns an ICECandidateInit
|
||||
// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
|
||||
func (c ICECandidate) ToJSON() ICECandidateInit {
|
||||
candidateStr := ""
|
||||
|
||||
candidate, err := c.ToICE()
|
||||
if err == nil {
|
||||
candidateStr = candidate.Marshal()
|
||||
}
|
||||
|
||||
return ICECandidateInit{
|
||||
Candidate: fmt.Sprintf("candidate:%s", candidateStr),
|
||||
SDPMid: &c.SDPMid,
|
||||
SDPMLineIndex: &c.SDPMLineIndex,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user