host our own shit
This commit is contained in:
@@ -103,6 +103,7 @@ func handleWebrtcPublish(ctx context.Context, env *runtime.Env, cmdID string, pa
|
||||
PublishToken: token,
|
||||
TLSInsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify,
|
||||
TLSCAPath: env.Cfg.TLSCAPath,
|
||||
ICEServers: parseICEServers(payload["iceServers"]),
|
||||
HasVideo: hasVideo,
|
||||
HasAudio: hasAudio,
|
||||
}
|
||||
@@ -155,6 +156,7 @@ func handleWebrtcP2POffer(ctx context.Context, env *runtime.Env, cmdID string, p
|
||||
kindStr := string(kind)
|
||||
|
||||
callbacks := webrtcpub.P2POfferCallbacks{
|
||||
ICEServers: parseICEServers(payload["iceServers"]),
|
||||
OnICE: func(c webrtcpub.ICECandidate) {
|
||||
if c.Candidate == "" {
|
||||
return
|
||||
@@ -203,6 +205,64 @@ func handleWebrtcP2POffer(ctx context.Context, env *runtime.Env, cmdID string, p
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseICEServers(value interface{}) []webrtcpub.ICEServer {
|
||||
items, ok := value.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
servers := make([]webrtcpub.ICEServer, 0, len(items))
|
||||
for _, item := range items {
|
||||
if len(servers) >= 8 {
|
||||
break
|
||||
}
|
||||
entry, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var urls []string
|
||||
switch raw := entry["urls"].(type) {
|
||||
case string:
|
||||
urls = appendICEURL(urls, raw)
|
||||
case []interface{}:
|
||||
for _, value := range raw {
|
||||
if url, ok := value.(string); ok && len(urls) < 8 {
|
||||
urls = appendICEURL(urls, url)
|
||||
}
|
||||
}
|
||||
case []string:
|
||||
for _, url := range raw {
|
||||
if len(urls) < 8 {
|
||||
urls = appendICEURL(urls, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(urls) == 0 {
|
||||
continue
|
||||
}
|
||||
username, _ := entry["username"].(string)
|
||||
credential, _ := entry["credential"].(string)
|
||||
servers = append(servers, webrtcpub.ICEServer{URLs: urls, Username: username, Credential: credential})
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
func appendICEURL(urls []string, value string) []string {
|
||||
if len(value) == 0 || len(value) > 512 {
|
||||
return urls
|
||||
}
|
||||
valid := false
|
||||
for _, prefix := range []string{"stun:", "stuns:", "turn:", "turns:"} {
|
||||
if len(value) >= len(prefix) && value[:len(prefix)] == prefix {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return urls
|
||||
}
|
||||
return append(urls, value)
|
||||
}
|
||||
|
||||
func handleWebrtcP2PIce(_ context.Context, env *runtime.Env, cmdID string, payload map[string]interface{}) error {
|
||||
sessionID, _ := payload["sessionId"].(string)
|
||||
candidate, _ := payload["candidate"].(string)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package handlers
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseICEServers(t *testing.T) {
|
||||
servers := parseICEServers([]interface{}{
|
||||
map[string]interface{}{"urls": []interface{}{"stun:turn.example.test:3478"}},
|
||||
map[string]interface{}{
|
||||
"urls": []interface{}{"turn:turn.example.test:3478?transport=udp", "turn:turn.example.test:3478?transport=tcp", "https://invalid.example"},
|
||||
"username": "expiry:agent",
|
||||
"credential": "derived-secret",
|
||||
},
|
||||
})
|
||||
if len(servers) != 2 {
|
||||
t.Fatalf("server count = %d, want 2", len(servers))
|
||||
}
|
||||
if len(servers[1].URLs) != 2 {
|
||||
t.Fatalf("TURN URL count = %d, want 2", len(servers[1].URLs))
|
||||
}
|
||||
if servers[1].Username != "expiry:agent" || servers[1].Credential != "derived-secret" {
|
||||
t.Fatal("TURN credentials were not preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseICEServersRejectsInvalidInput(t *testing.T) {
|
||||
servers := parseICEServers([]interface{}{
|
||||
map[string]interface{}{"urls": "https://not-an-ice-server"},
|
||||
"invalid",
|
||||
})
|
||||
if len(servers) != 0 {
|
||||
t.Fatalf("server count = %d, want 0", len(servers))
|
||||
}
|
||||
}
|
||||
@@ -112,11 +112,15 @@ func StartP2POffer(ctx context.Context, kind Kind, sessionID string, offerSDP st
|
||||
webrtc.WithMediaEngine(mediaEngine),
|
||||
webrtc.WithInterceptorRegistry(interceptorRegistry),
|
||||
)
|
||||
pc, err := api.NewPeerConnection(webrtc.Configuration{
|
||||
ICEServers: []webrtc.ICEServer{
|
||||
{URLs: []string{"stun:stun.l.google.com:19302"}},
|
||||
},
|
||||
})
|
||||
iceServers := make([]webrtc.ICEServer, 0, len(opts.ICEServers))
|
||||
for _, server := range opts.ICEServers {
|
||||
iceServers = append(iceServers, webrtc.ICEServer{
|
||||
URLs: server.URLs,
|
||||
Username: server.Username,
|
||||
Credential: server.Credential,
|
||||
})
|
||||
}
|
||||
pc, err := api.NewPeerConnection(webrtc.Configuration{ICEServers: iceServers})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new peer connection: %w", err)
|
||||
}
|
||||
|
||||
@@ -213,6 +213,8 @@ type Options struct {
|
||||
TLSInsecureSkipVerify bool
|
||||
// TLSCAPath is an optional custom CA bundle.
|
||||
TLSCAPath string
|
||||
// ICEServers contains the server-issued, short-lived Coturn configuration.
|
||||
ICEServers []ICEServer
|
||||
// HasVideo / HasAudio select which tracks to add to the peer connection.
|
||||
HasVideo bool
|
||||
HasAudio bool
|
||||
@@ -224,7 +226,14 @@ type ICECandidate struct {
|
||||
SDPMLineIndex uint16 `msgpack:"sdpMLineIndex"`
|
||||
}
|
||||
|
||||
type ICEServer struct {
|
||||
URLs []string
|
||||
Username string
|
||||
Credential string
|
||||
}
|
||||
|
||||
type P2POfferCallbacks struct {
|
||||
ICEServers []ICEServer
|
||||
OnICE func(c ICECandidate)
|
||||
OnClose func()
|
||||
OnBandwidthEstimate func(bps int)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
@@ -144,8 +145,23 @@ func Start(ctx context.Context, kind Kind, opts Options) (*Publisher, error) {
|
||||
}
|
||||
}
|
||||
|
||||
api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine))
|
||||
pc, err := api.NewPeerConnection(webrtc.Configuration{})
|
||||
interceptorRegistry := &interceptor.Registry{}
|
||||
if err := webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil {
|
||||
return nil, fmt.Errorf("register WebRTC interceptors: %w", err)
|
||||
}
|
||||
api := webrtc.NewAPI(
|
||||
webrtc.WithMediaEngine(mediaEngine),
|
||||
webrtc.WithInterceptorRegistry(interceptorRegistry),
|
||||
)
|
||||
iceServers := make([]webrtc.ICEServer, 0, len(opts.ICEServers))
|
||||
for _, server := range opts.ICEServers {
|
||||
iceServers = append(iceServers, webrtc.ICEServer{
|
||||
URLs: server.URLs,
|
||||
Username: server.Username,
|
||||
Credential: server.Credential,
|
||||
})
|
||||
}
|
||||
pc, err := api.NewPeerConnection(webrtc.Configuration{ICEServers: iceServers})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new peer connection: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user