mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-06 00:37:00 +08:00
Update WebRTC candidates logic
This commit is contained in:
@@ -50,7 +50,7 @@ func Init() {
|
|||||||
|
|
||||||
log.Info().Str("addr", address).Msg("[ngrok] add external candidate for WebRTC")
|
log.Info().Str("addr", address).Msg("[ngrok] add external candidate for WebRTC")
|
||||||
|
|
||||||
webrtc.AddCandidate(address)
|
webrtc.AddCandidate(address, "tcp")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -1,3 +1,17 @@
|
|||||||
|
## Config
|
||||||
|
|
||||||
|
- supported networks: IPv4 (default), IPv6, both
|
||||||
|
- supported TCP: fixed port (default), disabled
|
||||||
|
- supported UDP: random port (default), fixed port
|
||||||
|
|
||||||
|
| Config examples | IPv4 | IPv6 | TCP | UDP |
|
||||||
|
|------------------------------|------|------|-------|--------|
|
||||||
|
| `listen: "0.0.0.0:8555/tcp"` | yes | no | fixed | random |
|
||||||
|
| `listen: "0.0.0.0:8555/udp"` | yes | no | no | fixed |
|
||||||
|
| `listen: "[::]:8555/tcp"` | no | yes | fixed | random |
|
||||||
|
| `listen: ":8555"` | yes | yes | fixed | fixed |
|
||||||
|
| `listen: ""` | yes | yes | no | random |
|
||||||
|
|
||||||
## Userful links
|
## Userful links
|
||||||
|
|
||||||
- https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html
|
- https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html
|
||||||
|
@@ -1,58 +1,66 @@
|
|||||||
package webrtc
|
package webrtc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||||
"github.com/pion/sdp/v3"
|
"github.com/pion/sdp/v3"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Address struct {
|
type Address struct {
|
||||||
Host string
|
Host string
|
||||||
Port int
|
Port string
|
||||||
|
Network string
|
||||||
|
Offset int
|
||||||
}
|
}
|
||||||
|
|
||||||
var addresses []Address
|
func (a *Address) Marshal() string {
|
||||||
|
host := a.Host
|
||||||
func AddCandidate(address string) {
|
if host == "stun" {
|
||||||
var port int
|
ip, err := webrtc.GetCachedPublicIP()
|
||||||
|
if err != nil {
|
||||||
// try to get port from address string
|
return ""
|
||||||
if i := strings.LastIndexByte(address, ':'); i > 0 {
|
|
||||||
if v, _ := strconv.Atoi(address[i+1:]); v != 0 {
|
|
||||||
address = address[:i]
|
|
||||||
port = v
|
|
||||||
}
|
}
|
||||||
|
host = ip.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default WebRTC port
|
switch a.Network {
|
||||||
if port == 0 {
|
case "udp":
|
||||||
port, _ = strconv.Atoi(Port)
|
return webrtc.CandidateManualHostUDP(host, a.Port, a.Offset)
|
||||||
|
case "tcp":
|
||||||
|
return webrtc.CandidateManualHostTCPPassive(host, a.Port, a.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
addresses = append(addresses, Address{Host: address, Port: port})
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var addresses []*Address
|
||||||
|
|
||||||
|
func AddCandidate(address, network string) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := -1 - len(addresses) // every next candidate will have a lower priority
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "tcp", "udp":
|
||||||
|
addresses = append(addresses, &Address{host, port, network, offset})
|
||||||
|
default:
|
||||||
|
addresses = append(
|
||||||
|
addresses, &Address{host, port, "udp", offset}, &Address{host, port, "tcp", offset},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCandidates() (candidates []string) {
|
func GetCandidates() (candidates []string) {
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
// using stun server for receive public IP-address
|
if candidate := address.Marshal(); candidate != "" {
|
||||||
if address.Host == "stun" {
|
candidates = append(candidates, candidate)
|
||||||
ip, err := webrtc.GetCachedPublicIP()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// this is a copy, original host unchanged
|
|
||||||
address.Host = ip.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
candidates = append(
|
|
||||||
candidates,
|
|
||||||
webrtc.CandidateManualHostUDP(address.Host, address.Port),
|
|
||||||
webrtc.CandidateManualHostTCPPassive(address.Host, address.Port),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -55,7 +55,7 @@ func kinesisClient(rawURL string, query url.Values, desc string) (core.Producer,
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
// 3. Create Peer Connection
|
// 3. Create Peer Connection
|
||||||
api, err := webrtc.NewAPI("")
|
api, err := webrtc.NewAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ func openIPCClient(rawURL string, query url.Values) (core.Producer, error) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
// 3. Create Peer Connection
|
// 3. Create Peer Connection
|
||||||
api, err := webrtc.NewAPI("")
|
api, err := webrtc.NewAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/internal/api"
|
"github.com/AlexxIT/go2rtc/internal/api"
|
||||||
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
||||||
@@ -32,10 +32,20 @@ func Init() {
|
|||||||
|
|
||||||
log = app.GetLogger("webrtc")
|
log = app.GetLogger("webrtc")
|
||||||
|
|
||||||
address := cfg.Mod.Listen
|
address, network, _ := strings.Cut(cfg.Mod.Listen, "/")
|
||||||
|
|
||||||
|
var candidateHost []string
|
||||||
|
for _, candidate := range cfg.Mod.Candidates {
|
||||||
|
if strings.HasPrefix(candidate, "host:") {
|
||||||
|
candidateHost = append(candidateHost, candidate[5:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
AddCandidate(candidate, network)
|
||||||
|
}
|
||||||
|
|
||||||
// create pionAPI with custom codecs list and custom network settings
|
// create pionAPI with custom codecs list and custom network settings
|
||||||
serverAPI, err := webrtc.NewAPI(address)
|
serverAPI, err := webrtc.NewServerAPI(address, network, candidateHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Caller().Send()
|
log.Error().Err(err).Caller().Send()
|
||||||
return
|
return
|
||||||
@@ -46,9 +56,8 @@ func Init() {
|
|||||||
|
|
||||||
if address != "" {
|
if address != "" {
|
||||||
log.Info().Str("addr", address).Msg("[webrtc] listen")
|
log.Info().Str("addr", address).Msg("[webrtc] listen")
|
||||||
_, Port, _ = net.SplitHostPort(address)
|
|
||||||
|
|
||||||
clientAPI, _ = webrtc.NewAPI("")
|
clientAPI, _ = webrtc.NewAPI()
|
||||||
}
|
}
|
||||||
|
|
||||||
pionConf := pion.Configuration{
|
pionConf := pion.Configuration{
|
||||||
@@ -65,10 +74,6 @@ func Init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, candidate := range cfg.Mod.Candidates {
|
|
||||||
AddCandidate(candidate)
|
|
||||||
}
|
|
||||||
|
|
||||||
// async WebRTC server (two API versions)
|
// async WebRTC server (two API versions)
|
||||||
ws.HandleFunc("webrtc", asyncHandler)
|
ws.HandleFunc("webrtc", asyncHandler)
|
||||||
ws.HandleFunc("webrtc/offer", asyncHandler)
|
ws.HandleFunc("webrtc/offer", asyncHandler)
|
||||||
@@ -81,7 +86,6 @@ func Init() {
|
|||||||
streams.HandleFunc("webrtc", streamsHandler)
|
streams.HandleFunc("webrtc", streamsHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
var Port string
|
|
||||||
var log zerolog.Logger
|
var log zerolog.Logger
|
||||||
|
|
||||||
var PeerConnection func(active bool) (*pion.PeerConnection, error)
|
var PeerConnection func(active bool) (*pion.PeerConnection, error)
|
||||||
|
@@ -2,10 +2,11 @@ package hass
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||||
pion "github.com/pion/webrtc/v3"
|
pion "github.com/pion/webrtc/v3"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@@ -48,7 +49,7 @@ func NewClient(rawURL string) (*Client, error) {
|
|||||||
defer hassAPI.Close()
|
defer hassAPI.Close()
|
||||||
|
|
||||||
// 2. Create WebRTC client
|
// 2. Create WebRTC client
|
||||||
rtcAPI, err := webrtc.NewAPI("")
|
rtcAPI, err := webrtc.NewAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,11 @@ package nest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||||
pion "github.com/pion/webrtc/v3"
|
pion "github.com/pion/webrtc/v3"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@@ -34,7 +35,7 @@ func NewClient(rawURL string) (*Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rtcAPI, err := webrtc.NewAPI("")
|
rtcAPI, err := webrtc.NewAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ func (c *Client) Connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Create Peer Connection
|
// 4. Create Peer Connection
|
||||||
api, err := webrtc.NewAPI("")
|
api, err := webrtc.NewAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,7 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
|
||||||
"github.com/pion/interceptor"
|
"github.com/pion/interceptor"
|
||||||
"github.com/pion/webrtc/v3"
|
"github.com/pion/webrtc/v3"
|
||||||
)
|
)
|
||||||
@@ -13,7 +11,11 @@ import (
|
|||||||
// https://ffmpeg.org/ffmpeg-all.html#Muxer
|
// https://ffmpeg.org/ffmpeg-all.html#Muxer
|
||||||
const ReceiveMTU = 1472
|
const ReceiveMTU = 1472
|
||||||
|
|
||||||
func NewAPI(address string) (*webrtc.API, error) {
|
func NewAPI() (*webrtc.API, error) {
|
||||||
|
return NewServerAPI("", "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerAPI(address, network string, candidateHost []string) (*webrtc.API, error) {
|
||||||
// for debug logs add to env: `PION_LOG_DEBUG=all`
|
// for debug logs add to env: `PION_LOG_DEBUG=all`
|
||||||
m := &webrtc.MediaEngine{}
|
m := &webrtc.MediaEngine{}
|
||||||
//if err := m.RegisterDefaultCodecs(); err != nil {
|
//if err := m.RegisterDefaultCodecs(); err != nil {
|
||||||
@@ -35,34 +37,33 @@ func NewAPI(address string) (*webrtc.API, error) {
|
|||||||
return name != "hassio" && name != "docker0"
|
return name != "hassio" && name != "docker0"
|
||||||
})
|
})
|
||||||
|
|
||||||
// disable mDNS listener
|
|
||||||
s.SetICEMulticastDNSMode(ice.MulticastDNSModeDisabled)
|
|
||||||
|
|
||||||
// UDP6 may have problems with DNS resolving for STUN servers
|
|
||||||
s.SetNetworkTypes([]webrtc.NetworkType{
|
|
||||||
webrtc.NetworkTypeUDP4, webrtc.NetworkTypeTCP4,
|
|
||||||
webrtc.NetworkTypeUDP6, webrtc.NetworkTypeTCP6,
|
|
||||||
})
|
|
||||||
|
|
||||||
// fix https://github.com/pion/webrtc/pull/2407
|
// fix https://github.com/pion/webrtc/pull/2407
|
||||||
s.SetDTLSInsecureSkipHelloVerify(true)
|
s.SetDTLSInsecureSkipHelloVerify(true)
|
||||||
|
|
||||||
s.SetReceiveMTU(ReceiveMTU)
|
s.SetReceiveMTU(ReceiveMTU)
|
||||||
|
|
||||||
|
s.SetNAT1To1IPs(candidateHost, webrtc.ICECandidateTypeHost)
|
||||||
|
|
||||||
|
// by default enable IPv4 + IPv6 modes
|
||||||
|
s.SetNetworkTypes([]webrtc.NetworkType{
|
||||||
|
webrtc.NetworkTypeUDP4, webrtc.NetworkTypeTCP4,
|
||||||
|
webrtc.NetworkTypeUDP6, webrtc.NetworkTypeTCP6,
|
||||||
|
})
|
||||||
|
|
||||||
if address != "" {
|
if address != "" {
|
||||||
address, network, _ := strings.Cut(address, "/")
|
|
||||||
if network == "" || network == "udp" {
|
|
||||||
if ln, err := net.ListenPacket("udp", address); err == nil {
|
|
||||||
udpMux := webrtc.NewICEUDPMux(nil, ln)
|
|
||||||
s.SetICEUDPMux(udpMux)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if network == "" || network == "tcp" {
|
if network == "" || network == "tcp" {
|
||||||
if ln, err := net.Listen("tcp", address); err == nil {
|
if ln, err := net.Listen("tcp", address); err == nil {
|
||||||
tcpMux := webrtc.NewICETCPMux(nil, ln, 8)
|
tcpMux := webrtc.NewICETCPMux(nil, ln, 8)
|
||||||
s.SetICETCPMux(tcpMux)
|
s.SetICETCPMux(tcpMux)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if network == "" || network == "udp" {
|
||||||
|
if ln, err := net.ListenPacket("udp", address); err == nil {
|
||||||
|
udpMux := webrtc.NewICEUDPMux(nil, ln)
|
||||||
|
s.SetICEUDPMux(udpMux)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return webrtc.NewAPI(
|
return webrtc.NewAPI(
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestClient(t *testing.T) {
|
func TestClient(t *testing.T) {
|
||||||
api, err := NewAPI("")
|
api, err := NewAPI()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
pc, err := api.NewPeerConnection(webrtc.Configuration{})
|
pc, err := api.NewPeerConnection(webrtc.Configuration{})
|
||||||
|
@@ -260,17 +260,15 @@ func MimeType(codec *core.Codec) string {
|
|||||||
// for server reflexive candidates, 110 for peer reflexive candidates,
|
// for server reflexive candidates, 110 for peer reflexive candidates,
|
||||||
// and 0 for relayed candidates.
|
// and 0 for relayed candidates.
|
||||||
|
|
||||||
// We use new priority 120 for Manual Host. It is lower than real Host,
|
const PriorityTypeHostUDP = (1 << 24) * int(126)
|
||||||
// but more then any other candidates.
|
const PriorityTypeHostTCP = (1 << 24) * int(126-27)
|
||||||
|
const PriorityLocalUDP = (1 << 8) * int(65535)
|
||||||
|
const PriorityLocalTCPPassive = (1 << 8) * int((1<<13)*4+8191)
|
||||||
|
const PriorityComponentRTP = 1 * int(256-ice.ComponentRTP)
|
||||||
|
|
||||||
const PriorityManualHost = (1 << 24) * uint32(120)
|
func CandidateManualHostUDP(host, port string, offset int) string {
|
||||||
const PriorityLocalUDP = (1 << 8) * uint32(65535)
|
|
||||||
const PriorityLocalTCPPassive = (1 << 8) * uint32((1<<13)*4+8191)
|
|
||||||
const PriorityComponentRTP = uint32(256 - ice.ComponentRTP)
|
|
||||||
|
|
||||||
func CandidateManualHostUDP(host string, port int) string {
|
|
||||||
foundation := crc32.ChecksumIEEE([]byte("host" + host + "udp4"))
|
foundation := crc32.ChecksumIEEE([]byte("host" + host + "udp4"))
|
||||||
priority := PriorityManualHost + PriorityLocalUDP + PriorityComponentRTP
|
priority := PriorityTypeHostUDP + PriorityLocalUDP + PriorityComponentRTP + offset
|
||||||
|
|
||||||
// 1. Foundation
|
// 1. Foundation
|
||||||
// 2. Component, always 1 because RTP
|
// 2. Component, always 1 because RTP
|
||||||
@@ -279,19 +277,15 @@ func CandidateManualHostUDP(host string, port int) string {
|
|||||||
// 5. Host - IP4 or IP6 or domain name
|
// 5. Host - IP4 or IP6 or domain name
|
||||||
// 6. Port
|
// 6. Port
|
||||||
// 7. typ host
|
// 7. typ host
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf("candidate:%d 1 udp %d %s %s typ host", foundation, priority, host, port)
|
||||||
"candidate:%d 1 udp %d %s %d typ host",
|
|
||||||
foundation, priority, host, port,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CandidateManualHostTCPPassive(address string, port int) string {
|
func CandidateManualHostTCPPassive(host, port string, offset int) string {
|
||||||
foundation := crc32.ChecksumIEEE([]byte("host" + address + "tcp4"))
|
foundation := crc32.ChecksumIEEE([]byte("host" + host + "tcp4"))
|
||||||
priority := PriorityManualHost + PriorityLocalTCPPassive + PriorityComponentRTP
|
priority := PriorityTypeHostTCP + PriorityLocalTCPPassive + PriorityComponentRTP + offset
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"candidate:%d 1 tcp %d %s %d typ host tcptype passive",
|
"candidate:%d 1 tcp %d %s %s typ host tcptype passive", foundation, priority, host, port,
|
||||||
foundation, priority, address, port,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user