mirror of
https://github.com/pion/webrtc.git
synced 2025-10-04 23:02:48 +08:00

On Firefox, the RTCIceCandidate interface appears to just be an RTCIceCandidateInit in disguise. That is, it does not have properties for each individual component of the candidate line. It only has the raw SDP string in the candidate property. This change falls back to parsing the candidate line if some expected property is missing when preparing the candidate for the callback OnICECandidate. https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate https://caniuse.com/#feat=mdn-api_rtcicecandidate_priority
187 lines
4.9 KiB
Go
187 lines
4.9 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pion/ice"
|
|
"github.com/pion/sdp/v2"
|
|
)
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// Conversion for package ice
|
|
|
|
func newICECandidatesFromICE(iceCandidates []ice.Candidate) ([]ICECandidate, error) {
|
|
candidates := []ICECandidate{}
|
|
|
|
for _, i := range iceCandidates {
|
|
c, err := newICECandidateFromICE(i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
candidates = append(candidates, c)
|
|
}
|
|
|
|
return candidates, nil
|
|
}
|
|
|
|
func newICECandidateFromICE(i ice.Candidate) (ICECandidate, error) {
|
|
typ, err := convertTypeFromICE(i.Type())
|
|
if err != nil {
|
|
return ICECandidate{}, err
|
|
}
|
|
protocol, err := NewICEProtocol(i.NetworkType().NetworkShort())
|
|
if err != nil {
|
|
return ICECandidate{}, err
|
|
}
|
|
|
|
c := ICECandidate{
|
|
statsID: i.ID(),
|
|
Foundation: "foundation",
|
|
Priority: i.Priority(),
|
|
Address: i.Address(),
|
|
Protocol: protocol,
|
|
Port: uint16(i.Port()),
|
|
Component: i.Component(),
|
|
Typ: typ,
|
|
}
|
|
|
|
if i.RelatedAddress() != nil {
|
|
c.RelatedAddress = i.RelatedAddress().Address
|
|
c.RelatedPort = uint16(i.RelatedAddress().Port)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (c ICECandidate) toICE() (ice.Candidate, 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,
|
|
}
|
|
return ice.NewCandidateHost(&config)
|
|
case ICECandidateTypeSrflx:
|
|
config := ice.CandidateServerReflexiveConfig{
|
|
CandidateID: candidateID,
|
|
Network: c.Protocol.String(),
|
|
Address: c.Address,
|
|
Port: int(c.Port),
|
|
Component: c.Component,
|
|
RelAddr: c.RelatedAddress,
|
|
RelPort: int(c.RelatedPort),
|
|
}
|
|
return ice.NewCandidateServerReflexive(&config)
|
|
case ICECandidateTypePrflx:
|
|
config := ice.CandidatePeerReflexiveConfig{
|
|
CandidateID: candidateID,
|
|
Network: c.Protocol.String(),
|
|
Address: c.Address,
|
|
Port: int(c.Port),
|
|
Component: c.Component,
|
|
RelAddr: c.RelatedAddress,
|
|
RelPort: int(c.RelatedPort),
|
|
}
|
|
return ice.NewCandidatePeerReflexive(&config)
|
|
case ICECandidateTypeRelay:
|
|
config := ice.CandidateRelayConfig{
|
|
CandidateID: candidateID,
|
|
Network: c.Protocol.String(),
|
|
Address: c.Address,
|
|
Port: int(c.Port),
|
|
Component: c.Component,
|
|
RelAddr: c.RelatedAddress,
|
|
RelPort: int(c.RelatedPort),
|
|
}
|
|
return ice.NewCandidateRelay(&config)
|
|
default:
|
|
return nil, fmt.Errorf("unknown candidate type: %s", c.Typ)
|
|
}
|
|
}
|
|
|
|
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("unknown ICE candidate type: %s", 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()
|
|
}
|
|
|
|
func iceCandidateToSDP(c ICECandidate) sdp.ICECandidate {
|
|
return sdp.ICECandidate{
|
|
Foundation: c.Foundation,
|
|
Priority: c.Priority,
|
|
Address: c.Address,
|
|
Protocol: c.Protocol.String(),
|
|
Port: c.Port,
|
|
Component: c.Component,
|
|
Typ: c.Typ.String(),
|
|
RelatedAddress: c.RelatedAddress,
|
|
RelatedPort: c.RelatedPort,
|
|
}
|
|
}
|
|
|
|
func newICECandidateFromSDP(c sdp.ICECandidate) (ICECandidate, error) {
|
|
typ, err := NewICECandidateType(c.Typ)
|
|
if err != nil {
|
|
return ICECandidate{}, err
|
|
}
|
|
protocol, err := NewICEProtocol(c.Protocol)
|
|
if err != nil {
|
|
return ICECandidate{}, err
|
|
}
|
|
return ICECandidate{
|
|
Foundation: c.Foundation,
|
|
Priority: c.Priority,
|
|
Address: c.Address,
|
|
Protocol: protocol,
|
|
Port: c.Port,
|
|
Component: c.Component,
|
|
Typ: typ,
|
|
RelatedAddress: c.RelatedAddress,
|
|
RelatedPort: c.RelatedPort,
|
|
}, nil
|
|
}
|
|
|
|
// ToJSON returns an ICECandidateInit
|
|
// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
|
|
func (c ICECandidate) ToJSON() ICECandidateInit {
|
|
var sdpmLineIndex uint16
|
|
return ICECandidateInit{
|
|
Candidate: fmt.Sprintf("candidate:%s", iceCandidateToSDP(c).Marshal()),
|
|
SDPMLineIndex: &sdpmLineIndex,
|
|
}
|
|
}
|