diff --git a/internal/session/session.go b/internal/session/session.go index 60642a0c..1e607a1a 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -2,6 +2,7 @@ package session import ( "github.com/rs/zerolog" + "github.com/pion/webrtc/v3" "demodesk/neko/internal/types" "demodesk/neko/internal/types/event" @@ -195,3 +196,11 @@ func (session *SessionCtx) SignalAnswer(sdp string) error { return session.webrtc_peer.SignalAnswer(sdp) } + +func (session *SessionCtx) SignalCandidate(candidate webrtc.ICECandidateInit) error { + if session.webrtc_peer == nil { + return nil + } + + return session.webrtc_peer.SignalCandidate(candidate) +} diff --git a/internal/types/event/events.go b/internal/types/event/events.go index 5541571f..1f6cc26e 100644 --- a/internal/types/event/events.go +++ b/internal/types/event/events.go @@ -7,9 +7,10 @@ const ( ) const ( - SIGNAL_REQUEST = "signal/request" - SIGNAL_ANSWER = "signal/answer" - SIGNAL_PROVIDE = "signal/provide" + SIGNAL_REQUEST = "signal/request" + SIGNAL_ANSWER = "signal/answer" + SIGNAL_PROVIDE = "signal/provide" + SIGNAL_CANDIDATE = "signal/candidate" ) const ( diff --git a/internal/types/message/messages.go b/internal/types/message/messages.go index a0476739..fff6313b 100644 --- a/internal/types/message/messages.go +++ b/internal/types/message/messages.go @@ -1,6 +1,8 @@ package message import ( + "github.com/pion/webrtc/v3" + "demodesk/neko/internal/types" ) @@ -45,6 +47,11 @@ type SignalProvide struct { ICE []string `json:"ice"` } +type SignalCandidate struct { + Event string `json:"event,omitempty"` + *webrtc.ICECandidateInit +} + type SignalAnswer struct { Event string `json:"event,omitempty"` SDP string `json:"sdp"` diff --git a/internal/types/session.go b/internal/types/session.go index ae6494b9..9c74d905 100644 --- a/internal/types/session.go +++ b/internal/types/session.go @@ -1,6 +1,10 @@ package types -import "net/http" +import ( + "net/http" + + "github.com/pion/webrtc/v3" +) type MemberProfile struct { Secret string `json:"secret,omitempty"` @@ -58,6 +62,7 @@ type Session interface { SetWebRTCPeer(webrtc_peer WebRTCPeer) SetWebRTCConnected(connected bool) SignalAnswer(sdp string) error + SignalCandidate(candidate webrtc.ICECandidateInit) error } type SessionManager interface { diff --git a/internal/types/webrtc.go b/internal/types/webrtc.go index 8052f335..9c0bca3f 100644 --- a/internal/types/webrtc.go +++ b/internal/types/webrtc.go @@ -4,6 +4,8 @@ import "github.com/pion/webrtc/v3" type WebRTCPeer interface { SignalAnswer(sdp string) error + SignalCandidate(candidate webrtc.ICECandidateInit) error + Destroy() error } diff --git a/internal/webrtc/manager.go b/internal/webrtc/manager.go index a37c4f28..bb9b822c 100644 --- a/internal/webrtc/manager.go +++ b/internal/webrtc/manager.go @@ -12,6 +12,8 @@ import ( "demodesk/neko/internal/types" "demodesk/neko/internal/types/codec" + "demodesk/neko/internal/types/event" + "demodesk/neko/internal/types/message" "demodesk/neko/internal/config" ) @@ -86,45 +88,57 @@ func (manager *WebRTCManagerCtx) ICEServers() []string { } func (manager *WebRTCManagerCtx) CreatePeer(session types.Session) (*webrtc.SessionDescription, error) { - // Create MediaEngine + logger := manager.logger.With().Str("id", session.ID()).Logger() + engine, err := manager.mediaEngine() if err != nil { return nil, err } // Custom settings & configuration - settings := manager.apiSettings(session) + settings := manager.apiSettings(logger) configuration := manager.apiConfiguration() // Create NewAPI with MediaEngine and SettingEngine api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(*settings)) - // Create NewPeerConnection connection, err := api.NewPeerConnection(*configuration) if err != nil { return nil, err } - // Register video, audio & data tracks + // Asynchronously send local ICE Candidates + connection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { + logger.Debug().Msg("all local ice candidates sent") + return + } + + ICECandidateInit := candidate.ToJSON() + err := session.Send( + message.SignalCandidate{ + Event: event.SIGNAL_CANDIDATE, + ICECandidateInit: &ICECandidateInit, + }) + + if err != nil { + logger.Warn().Err(err).Msg("sending ice candidate failed") + } + }) + if err := manager.registerTracks(connection); err != nil { return nil, err } - // Create Offer offer, err := connection.CreateOffer(nil) if err != nil { return nil, err } - // TODO: Refactor, send request to client. - gatherComplete := webrtc.GatheringCompletePromise(connection) - if err := connection.SetLocalDescription(offer); err != nil { return nil, err } - <-gatherComplete - connection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { switch state { case webrtc.PeerConnectionStateConnected: @@ -145,7 +159,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session) (*webrtc.Sess } if err = manager.handle(message); err != nil { - manager.logger.Warn().Err(err).Msg("data handle failed") + logger.Warn().Err(err).Msg("data handle failed") } }) }) @@ -176,10 +190,10 @@ func (manager *WebRTCManagerCtx) mediaEngine() (*webrtc.MediaEngine, error) { return engine, nil } -func (manager *WebRTCManagerCtx) apiSettings(session types.Session) *webrtc.SettingEngine { +func (manager *WebRTCManagerCtx) apiSettings(logger zerolog.Logger) *webrtc.SettingEngine { settings := &webrtc.SettingEngine{ LoggerFactory: loggerFactory{ - logger: manager.logger.With().Str("id", session.ID()).Logger(), + logger: logger, }, } diff --git a/internal/webrtc/peer.go b/internal/webrtc/peer.go index 7cc9f0e1..e6e8e05f 100644 --- a/internal/webrtc/peer.go +++ b/internal/webrtc/peer.go @@ -17,6 +17,10 @@ func (webrtc_peer *WebRTCPeerCtx) SignalAnswer(sdp string) error { }) } +func (webrtc_peer *WebRTCPeerCtx) SignalCandidate(candidate webrtc.ICECandidateInit) error { + return webrtc_peer.connection.AddICECandidate(candidate) +} + func (webrtc_peer *WebRTCPeerCtx) Destroy() error { if webrtc_peer.connection == nil || webrtc_peer.connection.ConnectionState() != webrtc.PeerConnectionStateConnected { return nil diff --git a/internal/websocket/handler/handler.go b/internal/websocket/handler/handler.go index 1601a42b..2a1ef34a 100644 --- a/internal/websocket/handler/handler.go +++ b/internal/websocket/handler/handler.go @@ -54,6 +54,11 @@ func (h *MessageHandlerCtx) Message(session types.Session, raw []byte) bool { err = utils.Unmarshal(payload, raw, func() error { return h.signalAnswer(session, payload) }) + case event.SIGNAL_CANDIDATE: + payload := &message.SignalCandidate{} + err = utils.Unmarshal(payload, raw, func() error { + return h.signalCandidate(session, payload) + }) // Control Events case event.CONTROL_RELEASE: diff --git a/internal/websocket/handler/signal.go b/internal/websocket/handler/signal.go index 22ebd577..d7cfc62d 100644 --- a/internal/websocket/handler/signal.go +++ b/internal/websocket/handler/signal.go @@ -28,3 +28,7 @@ func (h *MessageHandlerCtx) signalRequest(session types.Session) error { func (h *MessageHandlerCtx) signalAnswer(session types.Session, payload *message.SignalAnswer) error { return session.SignalAnswer(payload.SDP) } + +func (h *MessageHandlerCtx) signalCandidate(session types.Session, payload *message.SignalCandidate) error { + return session.SignalCandidate(*payload.ICECandidateInit) +}