Merge pull request #43 from mbattista/add-turn-server

allow to add password protected turn server
This commit is contained in:
Miroslav Šedivý 2021-04-04 22:49:51 +02:00 committed by GitHub
commit 4589f758c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 216 additions and 187 deletions

View File

@ -41,6 +41,7 @@ For n.eko room management software visit https://github.com/m1k1o/neko-rooms.
- Added `?usr=<display-name>` that will prefill username. This allows creating auto-join links. - Added `?usr=<display-name>` that will prefill username. This allows creating auto-join links.
- Added `?cast=1` that will hide all control and show only video. - Added `?cast=1` that will hide all control and show only video.
- Shake keyboard icon if someone attempted to control when is nobody hosting. - Shake keyboard icon if someone attempted to control when is nobody hosting.
- Support for password protected `NEKO_ICESERVERS` (by @mbattista).
### Bugs ### Bugs
- Fixed minor gst pipeline bug. - Fixed minor gst pipeline bug.
@ -280,7 +281,16 @@ NEKO_CERT:
NEKO_KEY: NEKO_KEY:
- Path to the SSL-Certificate private key - Path to the SSL-Certificate private key
- e.g. '/certs/key.pem' - e.g. '/certs/key.pem'
NEKO_ICELITE:
- Use the ice lite protocol
- e.g. false
NEKO_ICESERVER:
- Describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer (simple usage for server without authentication)
- e.g. 'stun:stun.l.google.com:19302'
NEKO_ICESERVERS:
- Describes multiple STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer
- e.g. '[{"urls": ["turn:turn.example.com:19302", "stun:stun.example.com:19302"], "username": "name", "credential": "password"}, {"urls": ["stun:stun.example2.com:19302"]}]'
- [More information](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer)
``` ```
# How to contribute? # How to contribute?

View File

@ -183,7 +183,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this._ws!.send(JSON.stringify({ event, ...payload })) this._ws!.send(JSON.stringify({ event, ...payload }))
} }
public createPeer(sdp: string, lite: boolean, servers: string[]) { public createPeer(sdp: string, lite: boolean, servers: RTCIceServer[]) {
this.emit('debug', `creating peer`) this.emit('debug', `creating peer`)
if (!this.socketOpen) { if (!this.socketOpen) {
this.emit( this.emit(
@ -202,7 +202,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this._peer = new RTCPeerConnection() this._peer = new RTCPeerConnection()
if (lite !== true) { if (lite !== true) {
this._peer = new RTCPeerConnection({ this._peer = new RTCPeerConnection({
iceServers: [{ urls: servers }], iceServers: servers,
}) })
} }

View File

@ -67,7 +67,7 @@ export interface SignalProvideMessage extends WebSocketMessage, SignalProvidePay
export interface SignalProvidePayload { export interface SignalProvidePayload {
id: string id: string
lite: boolean lite: boolean
ice: string[] ice: RTCIceServer[]
sdp: string sdp: string
} }

View File

@ -127,7 +127,7 @@ func (session *Session) SignalCandidate(data string) error {
return session.socket.Send(&message.SignalCandidate{ return session.socket.Send(&message.SignalCandidate{
Event: event.SIGNAL_CANDIDATE, Event: event.SIGNAL_CANDIDATE,
Data: data, Data: data,
}); })
} }
func (session *Session) destroy() error { func (session *Session) destroy() error {

View File

@ -1,17 +1,20 @@
package config package config
import ( import (
"encoding/json"
"strconv" "strconv"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"n.eko.moe/neko/internal/utils" "n.eko.moe/neko/internal/utils"
"github.com/pion/webrtc/v3"
) )
type WebRTC struct { type WebRTC struct {
ICELite bool ICELite bool
ICEServers []string ICEServers []webrtc.ICEServer
EphemeralMin uint16 EphemeralMin uint16
EphemeralMax uint16 EphemeralMax uint16
NAT1To1IPs []string NAT1To1IPs []string
@ -38,13 +41,31 @@ func (WebRTC) Init(cmd *cobra.Command) error {
return err return err
} }
cmd.PersistentFlags().String("iceservers", "", "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer")
if err := viper.BindPFlag("iceservers", cmd.PersistentFlags().Lookup("iceservers")); err != nil {
return err
}
return nil return nil
} }
func (s *WebRTC) Set() { func (s *WebRTC) Set() {
s.ICELite = viper.GetBool("icelite")
s.ICEServers = viper.GetStringSlice("iceserver")
s.NAT1To1IPs = viper.GetStringSlice("nat1to1") s.NAT1To1IPs = viper.GetStringSlice("nat1to1")
s.ICELite = viper.GetBool("icelite")
s.ICEServers = []webrtc.ICEServer{}
iceServersJson := viper.GetString("iceservers")
if iceServersJson != "" {
err := json.Unmarshal([]byte(iceServersJson), &s.ICEServers)
if err != nil {
panic(err)
}
}
iceServerSlice := viper.GetStringSlice("iceserver")
if len(iceServerSlice) > 0 {
s.ICEServers = append(s.ICEServers, webrtc.ICEServer{URLs: iceServerSlice})
}
if len(s.NAT1To1IPs) == 0 { if len(s.NAT1To1IPs) == 0 {
ip, err := utils.GetIP() ip, err := utils.GetIP()

View File

@ -2,6 +2,8 @@ package message
import ( import (
"n.eko.moe/neko/internal/types" "n.eko.moe/neko/internal/types"
"github.com/pion/webrtc/v3"
) )
type Message struct { type Message struct {
@ -18,7 +20,7 @@ type SignalProvide struct {
ID string `json:"id"` ID string `json:"id"`
SDP string `json:"sdp"` SDP string `json:"sdp"`
Lite bool `json:"lite"` Lite bool `json:"lite"`
ICE []string `json:"ice"` ICE []webrtc.ICEServer `json:"ice"`
} }
type SignalAnswer struct { type SignalAnswer struct {

View File

@ -2,6 +2,8 @@ package types
import ( import (
"time" "time"
"github.com/pion/webrtc/v3"
) )
type Sample struct { type Sample struct {
@ -13,7 +15,7 @@ type Sample struct {
type WebRTCManager interface { type WebRTCManager interface {
Start() Start()
Shutdown() error Shutdown() error
CreatePeer(id string, session Session) (string, bool, []string, error) CreatePeer(id string, session Session) (string, bool, []webrtc.ICEServer, error)
} }
type Peer interface { type Peer interface {

View File

@ -63,7 +63,7 @@ func (manager *WebRTCManager) Start() {
manager.logger.Info(). manager.logger.Info().
Str("ice_lite", fmt.Sprintf("%t", manager.config.ICELite)). Str("ice_lite", fmt.Sprintf("%t", manager.config.ICELite)).
Str("ice_servers", strings.Join(manager.config.ICEServers, ",")). Str("ice_servers", fmt.Sprintf("%+v", manager.config.ICEServers)).
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", manager.config.EphemeralMin, manager.config.EphemeralMax)). Str("ephemeral_port_range", fmt.Sprintf("%d-%d", manager.config.EphemeralMin, manager.config.EphemeralMax)).
Str("nat_ips", strings.Join(manager.config.NAT1To1IPs, ",")). Str("nat_ips", strings.Join(manager.config.NAT1To1IPs, ",")).
Msgf("webrtc starting") Msgf("webrtc starting")
@ -74,13 +74,9 @@ func (manager *WebRTCManager) Shutdown() error {
return nil return nil
} }
func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []string, error) { func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []webrtc.ICEServer, error) {
configuration := &webrtc.Configuration{ configuration := &webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: manager.config.ICEServers,
{
URLs: manager.config.ICEServers,
},
},
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
} }
@ -99,7 +95,7 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri
settings.SetEphemeralUDPPortRange(manager.config.EphemeralMin, manager.config.EphemeralMax) settings.SetEphemeralUDPPortRange(manager.config.EphemeralMin, manager.config.EphemeralMax)
settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost) settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
settings.SetICETimeouts(6 * time.Second, 6 * time.Second, 3 * time.Second) settings.SetICETimeouts(6*time.Second, 6*time.Second, 3*time.Second)
settings.SetSRTPReplayProtectionWindow(512) settings.SetSRTPReplayProtectionWindow(512)
// Create MediaEngine based off sdp // Create MediaEngine based off sdp
@ -146,12 +142,12 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri
Msg("connection state has changed") Msg("connection state has changed")
}) })
rtpVideo, err := connection.AddTrack(manager.videoTrack); rtpVideo, err := connection.AddTrack(manager.videoTrack)
if err != nil { if err != nil {
return "", manager.config.ICELite, manager.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
rtpAudio, err := connection.AddTrack(manager.audioTrack); rtpAudio, err := connection.AddTrack(manager.audioTrack)
if err != nil { if err != nil {
return "", manager.config.ICELite, manager.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
@ -234,7 +230,6 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri
} }
}() }()
return description.SDP, manager.config.ICELite, manager.config.ICEServers, nil return description.SDP, manager.config.ICELite, manager.config.ICEServers, nil
} }

View File

@ -90,7 +90,6 @@ func (h *MessageHandler) Message(id string, raw []byte) error {
return h.controlKeyboard(id, session, payload) return h.controlKeyboard(id, session, payload)
}), "%s failed", header.Event) }), "%s failed", header.Event)
// Chat Events // Chat Events
case event.CHAT_MESSAGE: case event.CHAT_MESSAGE:
payload := &message.ChatReceive{} payload := &message.ChatReceive{}

View File

@ -10,10 +10,10 @@ import "C"
import ( import (
"fmt" "fmt"
"regexp"
"sync" "sync"
"time" "time"
"unsafe" "unsafe"
"regexp"
"n.eko.moe/neko/internal/types" "n.eko.moe/neko/internal/types"
) )