mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
Stream bucket manager (#12)
* refactor webrtc. * bind track with a single connection. * audio+video codec. * move stream selection to bucket manager. * audio w/o bucket manager. * revert peer changes. * return video IDs. * destroy & recreate all. * add video ID change. * Track -> Recevier.
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/rs/zerolog"
|
||||
@ -35,11 +36,37 @@ const keepAliveInterval = 2 * time.Second
|
||||
const rtcpPLIInterval = 3 * time.Second
|
||||
|
||||
func New(desktop types.DesktopManager, capture types.CaptureManager, config *config.WebRTC) *WebRTCManagerCtx {
|
||||
configuration := webrtc.Configuration{
|
||||
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
|
||||
}
|
||||
|
||||
if !config.ICELite {
|
||||
ICEServers := []webrtc.ICEServer{}
|
||||
for _, server := range config.ICEServers {
|
||||
var credential any
|
||||
if server.Credential != "" {
|
||||
credential = server.Credential
|
||||
} else {
|
||||
credential = false
|
||||
}
|
||||
|
||||
ICEServers = append(ICEServers, webrtc.ICEServer{
|
||||
URLs: server.URLs,
|
||||
Username: server.Username,
|
||||
Credential: credential,
|
||||
})
|
||||
}
|
||||
|
||||
configuration.ICEServers = ICEServers
|
||||
}
|
||||
|
||||
return &WebRTCManagerCtx{
|
||||
logger: log.With().Str("module", "webrtc").Logger(),
|
||||
config: config,
|
||||
metrics: newMetrics(),
|
||||
|
||||
webrtcConfiguration: configuration,
|
||||
|
||||
desktop: desktop,
|
||||
capture: capture,
|
||||
curImage: cursor.NewImage(desktop),
|
||||
@ -58,6 +85,8 @@ type WebRTCManagerCtx struct {
|
||||
curImage *cursor.ImageCtx
|
||||
curPosition *cursor.PositionCtx
|
||||
|
||||
webrtcConfiguration webrtc.Configuration
|
||||
|
||||
tcpMux ice.TCPMux
|
||||
udpMux ice.UDPMux
|
||||
|
||||
@ -121,6 +150,70 @@ func (manager *WebRTCManagerCtx) ICEServers() []types.ICEServer {
|
||||
return manager.config.ICEServers
|
||||
}
|
||||
|
||||
func (manager *WebRTCManagerCtx) newPeerConnection(codecs []codec.RTPCodec, logger zerolog.Logger) (*webrtc.PeerConnection, error) {
|
||||
// create media engine
|
||||
engine := &webrtc.MediaEngine{}
|
||||
for _, codec := range codecs {
|
||||
if err := codec.Register(engine); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// create setting engine
|
||||
settings := webrtc.SettingEngine{
|
||||
LoggerFactory: pionlog.New(logger),
|
||||
}
|
||||
|
||||
settings.SetICETimeouts(disconnectedTimeout, failedTimeout, keepAliveInterval)
|
||||
settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
|
||||
settings.SetLite(manager.config.ICELite)
|
||||
|
||||
var networkType []webrtc.NetworkType
|
||||
|
||||
// udp candidates
|
||||
if manager.udpMux != nil {
|
||||
settings.SetICEUDPMux(manager.udpMux)
|
||||
networkType = append(networkType,
|
||||
webrtc.NetworkTypeUDP4,
|
||||
webrtc.NetworkTypeUDP6,
|
||||
)
|
||||
} else if manager.config.EphemeralMax != 0 {
|
||||
_ = settings.SetEphemeralUDPPortRange(manager.config.EphemeralMin, manager.config.EphemeralMax)
|
||||
networkType = append(networkType,
|
||||
webrtc.NetworkTypeUDP4,
|
||||
webrtc.NetworkTypeUDP6,
|
||||
)
|
||||
}
|
||||
|
||||
// tcp candidates
|
||||
if manager.tcpMux != nil {
|
||||
settings.SetICETCPMux(manager.tcpMux)
|
||||
networkType = append(networkType,
|
||||
webrtc.NetworkTypeTCP4,
|
||||
webrtc.NetworkTypeTCP6,
|
||||
)
|
||||
}
|
||||
|
||||
// enable support for TCP and UDP ICE candidates
|
||||
settings.SetNetworkTypes(networkType)
|
||||
|
||||
// create interceptor registry
|
||||
registry := &interceptor.Registry{}
|
||||
if err := webrtc.RegisterDefaultInterceptors(engine, registry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create new API
|
||||
api := webrtc.NewAPI(
|
||||
webrtc.WithMediaEngine(engine),
|
||||
webrtc.WithSettingEngine(settings),
|
||||
webrtc.WithInterceptorRegistry(registry),
|
||||
)
|
||||
|
||||
// create new peer connection
|
||||
return api.NewPeerConnection(manager.webrtcConfiguration)
|
||||
}
|
||||
|
||||
func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID string) (*webrtc.SessionDescription, error) {
|
||||
id := atomic.AddInt32(&manager.peerId, 1)
|
||||
manager.metrics.NewConnection(session)
|
||||
@ -130,19 +223,16 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
logger.Info().Msg("creating webrtc peer")
|
||||
|
||||
// all audios must have the same codec
|
||||
audioStream := manager.capture.Audio()
|
||||
audio := manager.capture.Audio()
|
||||
audioCodec := audio.Codec()
|
||||
|
||||
// all videos must have the same codec
|
||||
videoStream, ok := manager.capture.Video(videoID)
|
||||
if !ok {
|
||||
return nil, types.ErrWebRTCVideoNotFound
|
||||
}
|
||||
|
||||
manager.metrics.SetVideoID(session, videoID)
|
||||
video := manager.capture.Video()
|
||||
videoCodec := video.Codec()
|
||||
|
||||
connection, err := manager.newPeerConnection([]codec.RTPCodec{
|
||||
audioStream.Codec(),
|
||||
videoStream.Codec(),
|
||||
audioCodec,
|
||||
videoCodec,
|
||||
}, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -166,26 +256,37 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
|
||||
// audio track
|
||||
|
||||
audioTrack, err := manager.newPeerStreamTrack(audioStream, logger)
|
||||
audioTrack, err := NewTrack(logger, audioCodec, connection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := audioTrack.AddToConnection(connection); err != nil {
|
||||
// set stream for audio track
|
||||
err = audioTrack.SetStream(audio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// video track
|
||||
|
||||
videoTrack, err := manager.newPeerStreamTrack(videoStream, logger)
|
||||
videoTrack, err := NewTrack(logger, videoCodec, connection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := videoTrack.AddToConnection(connection); err != nil {
|
||||
// let video stream bucket manager handle stream subscriptions
|
||||
err = video.SetReceiver(videoTrack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set default video id
|
||||
err = videoTrack.SetVideoID(videoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manager.metrics.SetVideoID(session, videoID)
|
||||
|
||||
// data channel
|
||||
|
||||
dataChannel, err := connection.CreateDataChannel("data", nil)
|
||||
@ -198,13 +299,12 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
connection: connection,
|
||||
dataChannel: dataChannel,
|
||||
changeVideo: func(videoID string) error {
|
||||
videoStream, ok := manager.capture.Video(videoID)
|
||||
if !ok {
|
||||
return types.ErrWebRTCVideoNotFound
|
||||
if err := videoTrack.SetVideoID(videoID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manager.metrics.SetVideoID(session, videoID)
|
||||
return videoTrack.SetStream(videoStream)
|
||||
return nil
|
||||
},
|
||||
setPaused: func(isPaused bool) {
|
||||
videoTrack.SetPaused(isPaused)
|
||||
@ -318,7 +418,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
connection.Close()
|
||||
case webrtc.PeerConnectionStateClosed:
|
||||
session.SetWebRTCConnected(peer, false)
|
||||
videoTrack.RemoveStream()
|
||||
video.RemoveReceiver(videoTrack)
|
||||
audioTrack.RemoveStream()
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user