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:
Miroslav Šedivý
2022-10-17 13:39:31 +02:00
committed by GitHub
parent 095f9fe8ee
commit 5ad5daa6bb
11 changed files with 438 additions and 356 deletions

View File

@ -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()
}