mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-05 08:16:55 +08:00
131 lines
2.8 KiB
Go
131 lines
2.8 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
|
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
|
"github.com/pion/sdp/v3"
|
|
)
|
|
|
|
type Address struct {
|
|
Host string
|
|
Port string
|
|
Network string
|
|
Offset int
|
|
}
|
|
|
|
func (a *Address) Marshal() string {
|
|
host := a.Host
|
|
if host == "stun" {
|
|
ip, err := webrtc.GetCachedPublicIP()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
host = ip.String()
|
|
}
|
|
|
|
switch a.Network {
|
|
case "udp":
|
|
return webrtc.CandidateManualHostUDP(host, a.Port, a.Offset)
|
|
case "tcp":
|
|
return webrtc.CandidateManualHostTCPPassive(host, a.Port, a.Offset)
|
|
}
|
|
|
|
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) {
|
|
for _, address := range addresses {
|
|
if candidate := address.Marshal(); candidate != "" {
|
|
candidates = append(candidates, candidate)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func asyncCandidates(tr *ws.Transport, cons *webrtc.Conn) {
|
|
tr.WithContext(func(ctx map[any]any) {
|
|
if candidates, ok := ctx["candidate"].([]string); ok {
|
|
// process candidates that receive before this moment
|
|
for _, candidate := range candidates {
|
|
_ = cons.AddCandidate(candidate)
|
|
}
|
|
|
|
// remove already processed candidates
|
|
delete(ctx, "candidate")
|
|
}
|
|
|
|
// set variable for process candidates after this moment
|
|
ctx["webrtc"] = cons
|
|
})
|
|
|
|
for _, candidate := range GetCandidates() {
|
|
log.Trace().Str("candidate", candidate).Msg("[webrtc] config")
|
|
tr.Write(&ws.Message{Type: "webrtc/candidate", Value: candidate})
|
|
}
|
|
}
|
|
|
|
func syncCanditates(answer string) (string, error) {
|
|
if len(addresses) == 0 {
|
|
return answer, nil
|
|
}
|
|
|
|
sd := &sdp.SessionDescription{}
|
|
if err := sd.Unmarshal([]byte(answer)); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
md := sd.MediaDescriptions[0]
|
|
|
|
for _, candidate := range GetCandidates() {
|
|
md.WithPropertyAttribute(candidate)
|
|
}
|
|
|
|
data, err := sd.Marshal()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
func candidateHandler(tr *ws.Transport, msg *ws.Message) error {
|
|
// process incoming candidate in sync function
|
|
tr.WithContext(func(ctx map[any]any) {
|
|
candidate := msg.String()
|
|
log.Trace().Str("candidate", candidate).Msg("[webrtc] remote")
|
|
|
|
if cons, ok := ctx["webrtc"].(*webrtc.Conn); ok {
|
|
// if webrtc.Server already initialized - process candidate
|
|
_ = cons.AddCandidate(candidate)
|
|
} else {
|
|
// or collect candidate and process it later
|
|
list, _ := ctx["candidate"].([]string)
|
|
ctx["candidate"] = append(list, candidate)
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|