ability to use ICE or ICELite

This commit is contained in:
Craig 2020-04-05 07:07:45 +00:00
parent 853dd14386
commit 04033b664b
9 changed files with 112 additions and 69 deletions

View File

@ -62,8 +62,8 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this._ws = new WebSocket(`${url}ws?password=${password}`) this._ws = new WebSocket(`${url}ws?password=${password}`)
this.emit('debug', `connecting to ${this._ws.url}`) this.emit('debug', `connecting to ${this._ws.url}`)
this._ws.onmessage = this.onMessage.bind(this) this._ws.onmessage = this.onMessage.bind(this)
this._ws.onerror = (event) => this.onError.bind(this) this._ws.onerror = event => this.onError.bind(this)
this._ws.onclose = (event) => this.onDisconnected.bind(this, new Error('websocket closed')) this._ws.onclose = event => this.onDisconnected.bind(this, new Error('websocket closed'))
this._timeout = setTimeout(this.onTimeout.bind(this), 15000) this._timeout = setTimeout(this.onTimeout.bind(this), 15000)
} catch (err) { } catch (err) {
this.onDisconnected(err) this.onDisconnected(err)
@ -156,7 +156,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) { public createPeer(sdp: string, lite: boolean, servers: string[]) {
this.emit('debug', `creating peer`) this.emit('debug', `creating peer`)
if (!this.socketOpen) { if (!this.socketOpen) {
this.emit( this.emit(
@ -173,16 +173,21 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
} }
this._peer = new RTCPeerConnection() this._peer = new RTCPeerConnection()
if (lite !== true) {
this._peer = new RTCPeerConnection({
iceServers: [{ urls: servers }],
})
}
this._peer.onconnectionstatechange = (event) => { this._peer.onconnectionstatechange = event => {
this.emit('debug', `peer connection state changed`, this._peer ? this._peer.connectionState : undefined) this.emit('debug', `peer connection state changed`, this._peer ? this._peer.connectionState : undefined)
} }
this._peer.onsignalingstatechange = (event) => { this._peer.onsignalingstatechange = event => {
this.emit('debug', `peer signaling state changed`, this._peer ? this._peer.signalingState : undefined) this.emit('debug', `peer signaling state changed`, this._peer ? this._peer.signalingState : undefined)
} }
this._peer.oniceconnectionstatechange = (event) => { this._peer.oniceconnectionstatechange = event => {
this._state = this._peer!.iceConnectionState this._state = this._peer!.iceConnectionState
this.emit('debug', `peer ice connection state changed: ${this._peer!.iceConnectionState}`) this.emit('debug', `peer ice connection state changed: ${this._peer!.iceConnectionState}`)
@ -217,7 +222,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this._peer.setRemoteDescription({ type: 'offer', sdp }) this._peer.setRemoteDescription({ type: 'offer', sdp })
this._peer this._peer
.createAnswer() .createAnswer()
.then((d) => { .then(d => {
this._peer!.setLocalDescription(d) this._peer!.setLocalDescription(d)
this._ws!.send( this._ws!.send(
JSON.stringify({ JSON.stringify({
@ -227,7 +232,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
}), }),
) )
}) })
.catch((err) => this.emit('error', err)) .catch(err => this.emit('error', err))
} }
private onMessage(e: MessageEvent) { private onMessage(e: MessageEvent) {
@ -236,9 +241,9 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this.emit('debug', `received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload) this.emit('debug', `received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload)
if (event === EVENT.SIGNAL.PROVIDE) { if (event === EVENT.SIGNAL.PROVIDE) {
const { sdp, id } = payload as SignalProvidePayload const { sdp, lite, ice, id } = payload as SignalProvidePayload
this._id = id this._id = id
this.createPeer(sdp) this.createPeer(sdp, lite, ice)
return return
} }

View File

@ -61,6 +61,8 @@ export interface SignalProvideMessage extends WebSocketMessage, SignalProvidePay
} }
export interface SignalProvidePayload { export interface SignalProvidePayload {
id: string id: string
lite: boolean
ice: string[]
sdp: string sdp: string
} }

View File

@ -16,6 +16,8 @@ type WebRTC struct {
AudioCodec string AudioCodec string
AudioParams string AudioParams string
Display string Display string
ICELite bool
ICEServers []string
VideoCodec string VideoCodec string
VideoParams string VideoParams string
EphemeralMin uint16 EphemeralMin uint16
@ -99,6 +101,16 @@ func (WebRTC) Init(cmd *cobra.Command) error {
return err return err
} }
cmd.PersistentFlags().Bool("icelite", false, "")
if err := viper.BindPFlag("icelite", cmd.PersistentFlags().Lookup("icelite")); err != nil {
return err
}
cmd.PersistentFlags().StringSlice("iceserver", []string{"stun:stun.l.google.com:19302"}, "")
if err := viper.BindPFlag("iceserver", cmd.PersistentFlags().Lookup("iceserver")); err != nil {
return err
}
return nil return nil
} }
@ -123,6 +135,9 @@ func (s *WebRTC) Set() {
audioCodec = webrtc.PCMA audioCodec = webrtc.PCMA
} }
s.ICELite = viper.GetBool("icelite")
s.ICEServers = viper.GetStringSlice("iceserver")
s.Device = viper.GetString("device") s.Device = viper.GetString("device")
s.AudioCodec = audioCodec s.AudioCodec = audioCodec
s.AudioParams = viper.GetString("aparams") s.AudioParams = viper.GetString("aparams")

View File

@ -17,6 +17,8 @@ type SignalProvide struct {
Event string `json:"event"` Event string `json:"event"`
ID string `json:"id"` ID string `json:"id"`
SDP string `json:"sdp"` SDP string `json:"sdp"`
Lite bool `json:"lite"`
ICE []string `json:"ice"`
} }
type SignalAnswer struct { type SignalAnswer struct {

View File

@ -8,7 +8,7 @@ type Sample struct {
type WebRTCManager interface { type WebRTCManager interface {
Start() Start()
Shutdown() error Shutdown() error
CreatePeer(id string, session Session) (string, error) CreatePeer(id string, session Session) (string, bool, []string, error)
ChangeScreenSize(width int, height int, rate int) error ChangeScreenSize(width int, height int, rate int) error
} }

View File

@ -8,8 +8,12 @@ import (
type Peer struct { type Peer struct {
id string id string
api *webrtc.API
engine *webrtc.MediaEngine
manager *WebRTCManager manager *WebRTCManager
settings *webrtc.SettingEngine
connection *webrtc.PeerConnection connection *webrtc.PeerConnection
configuration *webrtc.Configuration
mu sync.Mutex mu sync.Mutex
} }

View File

@ -8,7 +8,7 @@ import (
"n.eko.moe/neko/internal/gst" "n.eko.moe/neko/internal/gst"
) )
func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pipelineSrc string) (*gst.Pipeline, *webrtc.Track, error) { func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pipelineSrc string) (*gst.Pipeline, *webrtc.Track, *webrtc.RTPCodec, error) {
pipeline, err := gst.CreatePipeline( pipeline, err := gst.CreatePipeline(
codecName, codecName,
pipelineDevice, pipelineDevice,
@ -16,7 +16,7 @@ func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pip
) )
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
var codec *webrtc.RTPCodec var codec *webrtc.RTPCodec
@ -36,14 +36,13 @@ func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pip
case webrtc.PCMA: case webrtc.PCMA:
codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000) codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000)
default: default:
return nil, nil, fmt.Errorf("unknown codec %s", codecName) return nil, nil, nil, fmt.Errorf("unknown codec %s", codecName)
} }
m.engine.RegisterCodec(codec)
track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec) track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
return pipeline, track, nil return pipeline, track, codec, nil
} }

View File

@ -18,53 +18,27 @@ import (
) )
func New(sessions types.SessionManager, config *config.WebRTC) *WebRTCManager { func New(sessions types.SessionManager, config *config.WebRTC) *WebRTCManager {
logger := log.With().Str("module", "webrtc").Logger()
settings := webrtc.SettingEngine{
LoggerFactory: loggerFactory{
logger: logger,
},
}
settings.SetLite(true)
settings.SetEphemeralUDPPortRange(config.EphemeralMin, config.EphemeralMax)
settings.SetNAT1To1IPs(config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
// Create MediaEngine based off sdp
engine := webrtc.MediaEngine{}
engine.RegisterDefaultCodecs()
// Create API with MediaEngine and SettingEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings))
return &WebRTCManager{ return &WebRTCManager{
logger: logger, logger: log.With().Str("module", "webrtc").Logger(),
settings: settings,
cleanup: time.NewTicker(1 * time.Second), cleanup: time.NewTicker(1 * time.Second),
shutdown: make(chan bool), shutdown: make(chan bool),
sessions: sessions, sessions: sessions,
engine: engine,
config: config, config: config,
api: api,
configuration: &webrtc.Configuration{
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
},
} }
} }
type WebRTCManager struct { type WebRTCManager struct {
logger zerolog.Logger logger zerolog.Logger
settings webrtc.SettingEngine
engine webrtc.MediaEngine
api *webrtc.API
videoTrack *webrtc.Track videoTrack *webrtc.Track
audioTrack *webrtc.Track audioTrack *webrtc.Track
videoPipeline *gst.Pipeline videoPipeline *gst.Pipeline
audioPipeline *gst.Pipeline audioPipeline *gst.Pipeline
videoCodec *webrtc.RTPCodec
audioCodec *webrtc.RTPCodec
sessions types.SessionManager sessions types.SessionManager
cleanup *time.Ticker cleanup *time.Ticker
config *config.WebRTC config *config.WebRTC
shutdown chan bool shutdown chan bool
configuration *webrtc.Configuration
} }
func (m *WebRTCManager) Start() { func (m *WebRTCManager) Start() {
@ -79,12 +53,12 @@ func (m *WebRTCManager) Start() {
} }
var err error var err error
m.videoPipeline, m.videoTrack, err = m.createTrack(m.config.VideoCodec, m.config.Display, m.config.VideoParams) m.videoPipeline, m.videoTrack, m.videoCodec, err = m.createTrack(m.config.VideoCodec, m.config.Display, m.config.VideoParams)
if err != nil { if err != nil {
m.logger.Panic().Err(err).Msg("unable to start webrtc manager") m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
} }
m.audioPipeline, m.audioTrack, err = m.createTrack(m.config.AudioCodec, m.config.Device, m.config.AudioParams) m.audioPipeline, m.audioTrack, m.audioCodec, err = m.createTrack(m.config.AudioCodec, m.config.Device, m.config.AudioParams)
if err != nil { if err != nil {
m.logger.Panic().Err(err).Msg("unable to start webrtc manager") m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
} }
@ -133,10 +107,12 @@ func (m *WebRTCManager) Start() {
Str("video_codec", m.config.VideoCodec). Str("video_codec", m.config.VideoCodec).
Str("audio_device", m.config.Device). Str("audio_device", m.config.Device).
Str("audio_codec", m.config.AudioCodec). Str("audio_codec", m.config.AudioCodec).
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", m.config.EphemeralMin, m.config.EphemeralMax)).
Str("nat_ips", strings.Join(m.config.NAT1To1IPs, ",")).
Str("audio_pipeline_src", m.audioPipeline.Src). Str("audio_pipeline_src", m.audioPipeline.Src).
Str("video_pipeline_src", m.videoPipeline.Src). Str("video_pipeline_src", m.videoPipeline.Src).
Str("ice_lite", fmt.Sprintf("%t", m.config.ICELite)).
Str("ice_servers", strings.Join(m.config.ICEServers, ",")).
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", m.config.EphemeralMin, m.config.EphemeralMax)).
Str("nat_ips", strings.Join(m.config.NAT1To1IPs, ",")).
Msgf("webrtc streaming") Msgf("webrtc streaming")
} }
@ -149,28 +125,62 @@ func (m *WebRTCManager) Shutdown() error {
return nil return nil
} }
func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, error) { func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []string, error) {
configuration := &webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: m.config.ICEServers,
},
},
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
}
settings := webrtc.SettingEngine{
LoggerFactory: loggerFactory{
logger: m.logger,
},
}
if m.config.ICELite {
configuration = &webrtc.Configuration{
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
}
settings.SetLite(true)
}
settings.SetEphemeralUDPPortRange(m.config.EphemeralMin, m.config.EphemeralMax)
settings.SetNAT1To1IPs(m.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
// Create MediaEngine based off sdp
engine := webrtc.MediaEngine{}
// engine.RegisterDefaultCodecs()
engine.RegisterCodec(m.audioCodec)
engine.RegisterCodec(m.videoCodec)
// Create API with MediaEngine and SettingEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings))
// Create new peer connection // Create new peer connection
connection, err := m.api.NewPeerConnection(*m.configuration) connection, err := api.NewPeerConnection(*configuration)
if err != nil { if err != nil {
return "", err return "", m.config.ICELite, m.config.ICEServers, err
} }
if _, err = connection.AddTransceiverFromTrack(m.videoTrack, webrtc.RtpTransceiverInit{ if _, err = connection.AddTransceiverFromTrack(m.videoTrack, webrtc.RtpTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendonly, Direction: webrtc.RTPTransceiverDirectionSendonly,
}); err != nil { }); err != nil {
return "", err return "", m.config.ICELite, m.config.ICEServers, err
} }
if _, err = connection.AddTransceiverFromTrack(m.audioTrack, webrtc.RtpTransceiverInit{ if _, err = connection.AddTransceiverFromTrack(m.audioTrack, webrtc.RtpTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendonly, Direction: webrtc.RTPTransceiverDirectionSendonly,
}); err != nil { }); err != nil {
return "", err return "", m.config.ICELite, m.config.ICEServers, err
} }
description, err := connection.CreateOffer(nil) description, err := connection.CreateOffer(nil)
if err != nil { if err != nil {
return "", err return "", m.config.ICELite, m.config.ICEServers, err
} }
connection.OnDataChannel(func(d *webrtc.DataChannel) { connection.OnDataChannel(func(d *webrtc.DataChannel) {
@ -201,13 +211,17 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, er
if err := session.SetPeer(&Peer{ if err := session.SetPeer(&Peer{
id: id, id: id,
api: api,
engine: &engine,
manager: m, manager: m,
settings: &settings,
connection: connection, connection: connection,
configuration: configuration,
}); err != nil { }); err != nil {
return "", err return "", m.config.ICELite, m.config.ICEServers, err
} }
return description.SDP, nil return description.SDP, m.config.ICELite, m.config.ICEServers, nil
} }
func (m *WebRTCManager) ChangeScreenSize(width int, height int, rate int) error { func (m *WebRTCManager) ChangeScreenSize(width int, height int, rate int) error {

View File

@ -7,7 +7,7 @@ import (
) )
func (h *MessageHandler) signalProvide(id string, session types.Session) error { func (h *MessageHandler) signalProvide(id string, session types.Session) error {
sdp, err := h.webrtc.CreatePeer(id, session) sdp, lite, ice, err := h.webrtc.CreatePeer(id, session)
if err != nil { if err != nil {
return err return err
} }
@ -16,6 +16,8 @@ func (h *MessageHandler) signalProvide(id string, session types.Session) error {
Event: event.SIGNAL_PROVIDE, Event: event.SIGNAL_PROVIDE,
ID: id, ID: id,
SDP: sdp, SDP: sdp,
Lite: lite,
ICE: ice,
}); err != nil { }); err != nil {
return err return err
} }