Refactor WebRTC candidates processing

This commit is contained in:
Alexey Khit
2023-02-24 11:28:14 +03:00
parent 3fb917f00f
commit 4328d2a573
4 changed files with 105 additions and 45 deletions

View File

@@ -4,39 +4,67 @@ import (
"github.com/AlexxIT/go2rtc/cmd/api" "github.com/AlexxIT/go2rtc/cmd/api"
"github.com/AlexxIT/go2rtc/pkg/webrtc" "github.com/AlexxIT/go2rtc/pkg/webrtc"
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
"strconv"
"strings"
) )
var candidates []string type Address struct {
var networks = []string{"udp", "tcp"} Host string
Port int
}
var addresses []Address
func AddCandidate(address string) { func AddCandidate(address string) {
candidates = append(candidates, address) var port int
// try to get port from address string
if i := strings.LastIndexByte(address, ':'); i > 0 {
if v, _ := strconv.Atoi(address[i+1:]); v != 0 {
address = address[:i]
port = v
}
}
// use default WebRTC port
if port == 0 {
port, _ = strconv.Atoi(Port)
}
addresses = append(addresses, Address{Host: address, Port: port})
}
func GetCandidates() (candidates []string) {
for _, address := range addresses {
// using stun server for receive public IP-address
if address.Host == "stun" {
ip, err := webrtc.GetCachedPublicIP()
if err != nil {
continue
}
// this is a copy, original host unchanged
address.Host = ip.String()
}
candidates = append(
candidates,
webrtc.CandidateHostUDP(address.Host, address.Port),
webrtc.CandidateHostTCPPassive(address.Host, address.Port),
)
}
return
} }
func asyncCandidates(tr *api.Transport) { func asyncCandidates(tr *api.Transport) {
for _, address := range candidates { for _, candidate := range GetCandidates() {
address, err := webrtc.LookupIP(address) log.Trace().Str("candidate", candidate).Msg("[webrtc] config")
if err != nil { tr.Write(&api.Message{Type: "webrtc/candidate", Value: candidate})
log.Warn().Err(err).Caller().Send()
continue
}
for _, network := range networks {
cand, err := webrtc.NewCandidate(network, address)
if err != nil {
log.Warn().Err(err).Caller().Send()
continue
}
log.Trace().Str("candidate", cand).Msg("[webrtc] config")
tr.Write(&api.Message{Type: "webrtc/candidate", Value: cand})
}
} }
} }
func syncCanditates(answer string) (string, error) { func syncCanditates(answer string) (string, error) {
if len(candidates) == 0 { if len(addresses) == 0 {
return answer, nil return answer, nil
} }
@@ -52,23 +80,8 @@ func syncCanditates(answer string) (string, error) {
md.Attributes = md.Attributes[:len(md.Attributes)-1] md.Attributes = md.Attributes[:len(md.Attributes)-1]
} }
for _, address := range candidates { for _, candidate := range GetCandidates() {
var err error md.WithPropertyAttribute(candidate)
address, err = webrtc.LookupIP(address)
if err != nil {
log.Warn().Err(err).Msg("[webrtc] candidate")
continue
}
for _, network := range networks {
cand, err := webrtc.NewCandidate(network, address)
if err != nil {
log.Warn().Err(err).Msg("[webrtc] candidate")
continue
}
md.WithPropertyAttribute(cand)
}
} }
if end { if end {

View File

@@ -58,7 +58,9 @@ func Init() {
return pionAPI.NewPeerConnection(pionConf) return pionAPI.NewPeerConnection(pionConf)
} }
candidates = cfg.Mod.Candidates for _, candidate := range cfg.Mod.Candidates {
AddCandidate(candidate)
}
api.HandleWS("webrtc", asyncHandler) api.HandleWS("webrtc", asyncHandler)
api.HandleWS("webrtc/offer", asyncHandler) api.HandleWS("webrtc/offer", asyncHandler)

View File

@@ -7,6 +7,7 @@ import (
"github.com/pion/ice/v2" "github.com/pion/ice/v2"
"github.com/pion/stun" "github.com/pion/stun"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"hash/crc32"
"net" "net"
"strconv" "strconv"
"strings" "strings"
@@ -157,3 +158,35 @@ func MimeType(codec *streamer.Codec) string {
} }
panic("not implemented") panic("not implemented")
} }
const PriorityHost = (1 << 24) * uint32(126)
const PriorityLocalUDP = (1 << 8) * uint32(65535)
const PriorityLocalTCPPassive = (1 << 8) * uint32((1<<13)*4+8191)
const PriorityComponentRTP = uint32(256 - ice.ComponentRTP)
func CandidateHostUDP(host string, port int) string {
foundation := crc32.ChecksumIEEE([]byte("host" + host + "udp4"))
priority := PriorityHost + PriorityLocalUDP + PriorityComponentRTP
// 1. Foundation
// 2. Component, always 1 because RTP
// 3. udp or tcp
// 4. Priority
// 5. Host - IP4 or IP6 or domain name
// 6. Port
// 7. typ host
return fmt.Sprintf(
"candidate:%d 1 udp %d %s %d typ host",
foundation, priority, host, port,
)
}
func CandidateHostTCPPassive(address string, port int) string {
foundation := crc32.ChecksumIEEE([]byte("host" + address + "tcp4"))
priority := PriorityHost + PriorityLocalTCPPassive + PriorityComponentRTP
return fmt.Sprintf(
"candidate:%d 1 tcp %d %s %d typ host tcptype passive",
foundation, priority, address, port,
)
}

View File

@@ -5,19 +5,31 @@ import (
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing" "testing"
) )
func TestName(t *testing.T) { func TestCandidates(t *testing.T) {
i, _ := ice.NewCandidateHost(&ice.CandidateHostConfig{ conf := &ice.CandidateHostConfig{
Network: "udp",
Address: "192.168.1.123",
Port: 8555,
Component: ice.ComponentRTP,
}
cand, err := ice.NewCandidateHost(conf)
require.Nil(t, err)
assert.Equal(t, "candidate:"+cand.Marshal(), CandidateHostUDP(conf.Address, conf.Port))
conf = &ice.CandidateHostConfig{
Network: "tcp", Network: "tcp",
Address: "192.168.1.123", Address: "192.168.1.123",
Port: 8555, Port: 8555,
Component: ice.ComponentRTP, Component: ice.ComponentRTP,
TCPType: ice.TCPTypePassive, TCPType: ice.TCPTypePassive,
}) }
a := i.Marshal() cand, err = ice.NewCandidateHost(conf)
println(a) require.Nil(t, err)
assert.Equal(t, "candidate:"+cand.Marshal(), CandidateHostTCPPassive(conf.Address, conf.Port))
} }
func TestPublicIP(t *testing.T) { func TestPublicIP(t *testing.T) {