add Trickle ICE support.

This commit is contained in:
Miroslav Šedivý 2021-02-02 20:43:33 +01:00
parent dd4c67a6c4
commit cae8201908
9 changed files with 68 additions and 17 deletions

View File

@ -2,6 +2,7 @@ package session
import ( import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/pion/webrtc/v3"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
@ -195,3 +196,11 @@ func (session *SessionCtx) SignalAnswer(sdp string) error {
return session.webrtc_peer.SignalAnswer(sdp) 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)
}

View File

@ -7,9 +7,10 @@ const (
) )
const ( const (
SIGNAL_REQUEST = "signal/request" SIGNAL_REQUEST = "signal/request"
SIGNAL_ANSWER = "signal/answer" SIGNAL_ANSWER = "signal/answer"
SIGNAL_PROVIDE = "signal/provide" SIGNAL_PROVIDE = "signal/provide"
SIGNAL_CANDIDATE = "signal/candidate"
) )
const ( const (

View File

@ -1,6 +1,8 @@
package message package message
import ( import (
"github.com/pion/webrtc/v3"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
) )
@ -45,6 +47,11 @@ type SignalProvide struct {
ICE []string `json:"ice"` ICE []string `json:"ice"`
} }
type SignalCandidate struct {
Event string `json:"event,omitempty"`
*webrtc.ICECandidateInit
}
type SignalAnswer struct { type SignalAnswer struct {
Event string `json:"event,omitempty"` Event string `json:"event,omitempty"`
SDP string `json:"sdp"` SDP string `json:"sdp"`

View File

@ -1,6 +1,10 @@
package types package types
import "net/http" import (
"net/http"
"github.com/pion/webrtc/v3"
)
type MemberProfile struct { type MemberProfile struct {
Secret string `json:"secret,omitempty"` Secret string `json:"secret,omitempty"`
@ -58,6 +62,7 @@ type Session interface {
SetWebRTCPeer(webrtc_peer WebRTCPeer) SetWebRTCPeer(webrtc_peer WebRTCPeer)
SetWebRTCConnected(connected bool) SetWebRTCConnected(connected bool)
SignalAnswer(sdp string) error SignalAnswer(sdp string) error
SignalCandidate(candidate webrtc.ICECandidateInit) error
} }
type SessionManager interface { type SessionManager interface {

View File

@ -4,6 +4,8 @@ import "github.com/pion/webrtc/v3"
type WebRTCPeer interface { type WebRTCPeer interface {
SignalAnswer(sdp string) error SignalAnswer(sdp string) error
SignalCandidate(candidate webrtc.ICECandidateInit) error
Destroy() error Destroy() error
} }

View File

@ -12,6 +12,8 @@ import (
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/codec" "demodesk/neko/internal/types/codec"
"demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message"
"demodesk/neko/internal/config" "demodesk/neko/internal/config"
) )
@ -86,45 +88,57 @@ func (manager *WebRTCManagerCtx) ICEServers() []string {
} }
func (manager *WebRTCManagerCtx) CreatePeer(session types.Session) (*webrtc.SessionDescription, error) { 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() engine, err := manager.mediaEngine()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Custom settings & configuration // Custom settings & configuration
settings := manager.apiSettings(session) settings := manager.apiSettings(logger)
configuration := manager.apiConfiguration() configuration := manager.apiConfiguration()
// Create NewAPI with MediaEngine and SettingEngine // Create NewAPI with MediaEngine and SettingEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(*settings)) api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(*settings))
// Create NewPeerConnection
connection, err := api.NewPeerConnection(*configuration) connection, err := api.NewPeerConnection(*configuration)
if err != nil { if err != nil {
return nil, err 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 { if err := manager.registerTracks(connection); err != nil {
return nil, err return nil, err
} }
// Create Offer
offer, err := connection.CreateOffer(nil) offer, err := connection.CreateOffer(nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: Refactor, send request to client.
gatherComplete := webrtc.GatheringCompletePromise(connection)
if err := connection.SetLocalDescription(offer); err != nil { if err := connection.SetLocalDescription(offer); err != nil {
return nil, err return nil, err
} }
<-gatherComplete
connection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { connection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
switch state { switch state {
case webrtc.PeerConnectionStateConnected: case webrtc.PeerConnectionStateConnected:
@ -145,7 +159,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session) (*webrtc.Sess
} }
if err = manager.handle(message); err != nil { 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 return engine, nil
} }
func (manager *WebRTCManagerCtx) apiSettings(session types.Session) *webrtc.SettingEngine { func (manager *WebRTCManagerCtx) apiSettings(logger zerolog.Logger) *webrtc.SettingEngine {
settings := &webrtc.SettingEngine{ settings := &webrtc.SettingEngine{
LoggerFactory: loggerFactory{ LoggerFactory: loggerFactory{
logger: manager.logger.With().Str("id", session.ID()).Logger(), logger: logger,
}, },
} }

View File

@ -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 { func (webrtc_peer *WebRTCPeerCtx) Destroy() error {
if webrtc_peer.connection == nil || webrtc_peer.connection.ConnectionState() != webrtc.PeerConnectionStateConnected { if webrtc_peer.connection == nil || webrtc_peer.connection.ConnectionState() != webrtc.PeerConnectionStateConnected {
return nil return nil

View File

@ -54,6 +54,11 @@ func (h *MessageHandlerCtx) Message(session types.Session, raw []byte) bool {
err = utils.Unmarshal(payload, raw, func() error { err = utils.Unmarshal(payload, raw, func() error {
return h.signalAnswer(session, payload) 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 // Control Events
case event.CONTROL_RELEASE: case event.CONTROL_RELEASE:

View File

@ -28,3 +28,7 @@ func (h *MessageHandlerCtx) signalRequest(session types.Session) error {
func (h *MessageHandlerCtx) signalAnswer(session types.Session, payload *message.SignalAnswer) error { func (h *MessageHandlerCtx) signalAnswer(session types.Session, payload *message.SignalAnswer) error {
return session.SignalAnswer(payload.SDP) return session.SignalAnswer(payload.SDP)
} }
func (h *MessageHandlerCtx) signalCandidate(session types.Session, payload *message.SignalCandidate) error {
return session.SignalCandidate(*payload.ICECandidateInit)
}