diff --git a/internal/protocols/webrtc/api.go b/internal/protocols/webrtc/api.go deleted file mode 100644 index d1b9b167..00000000 --- a/internal/protocols/webrtc/api.go +++ /dev/null @@ -1,168 +0,0 @@ -package webrtc - -import ( - "github.com/pion/ice/v2" - "github.com/pion/interceptor" - "github.com/pion/webrtc/v3" -) - -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -var videoCodecs = []webrtc.RTPCodecParameters{ - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeAV1, - ClockRate: 90000, - }, - PayloadType: 96, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeVP9, - ClockRate: 90000, - SDPFmtpLine: "profile-id=0", - }, - PayloadType: 97, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeVP9, - ClockRate: 90000, - SDPFmtpLine: "profile-id=1", - }, - PayloadType: 98, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeVP8, - ClockRate: 90000, - }, - PayloadType: 99, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeH264, - ClockRate: 90000, - SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", - }, - PayloadType: 100, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeH264, - ClockRate: 90000, - SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - }, - PayloadType: 101, - }, -} - -var audioCodecs = []webrtc.RTPCodecParameters{ - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeOpus, - ClockRate: 48000, - Channels: 2, - SDPFmtpLine: "minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1", - }, - PayloadType: 111, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeG722, - ClockRate: 8000, - }, - PayloadType: 9, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypePCMU, - ClockRate: 8000, - }, - PayloadType: 0, - }, - { - RTPCodecCapability: webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypePCMA, - ClockRate: 8000, - }, - PayloadType: 8, - }, -} - -// APIConf is the configuration passed to NewAPI(). -type APIConf struct { - ICEUDPMux ice.UDPMux - ICETCPMux ice.TCPMux - LocalRandomUDP bool - IPsFromInterfaces bool - IPsFromInterfacesList []string - AdditionalHosts []string -} - -// NewAPI allocates a webrtc API. -func NewAPI(cnf APIConf) (*webrtc.API, error) { - settingsEngine := webrtc.SettingEngine{} - - settingsEngine.SetInterfaceFilter(func(iface string) bool { - return cnf.IPsFromInterfaces && (len(cnf.IPsFromInterfacesList) == 0 || - stringInSlice(iface, cnf.IPsFromInterfacesList)) - }) - - settingsEngine.SetAdditionalHosts(cnf.AdditionalHosts) - - var networkTypes []webrtc.NetworkType - - // always enable UDP in order to support STUN/TURN - networkTypes = append(networkTypes, webrtc.NetworkTypeUDP4) - - if cnf.ICEUDPMux != nil { - settingsEngine.SetICEUDPMux(cnf.ICEUDPMux) - } - - if cnf.ICETCPMux != nil { - settingsEngine.SetICETCPMux(cnf.ICETCPMux) - networkTypes = append(networkTypes, webrtc.NetworkTypeTCP4) - } - - if cnf.LocalRandomUDP { - settingsEngine.SetICEUDPRandom(true) - } - - settingsEngine.SetNetworkTypes(networkTypes) - - mediaEngine := &webrtc.MediaEngine{} - - for _, codec := range videoCodecs { - err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo) - if err != nil { - return nil, err - } - } - - for _, codec := range audioCodecs { - err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio) - if err != nil { - return nil, err - } - } - - interceptorRegistry := &interceptor.Registry{} - - err := webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry) - if err != nil { - return nil, err - } - - return webrtc.NewAPI( - webrtc.WithSettingEngine(settingsEngine), - webrtc.WithMediaEngine(mediaEngine), - webrtc.WithInterceptorRegistry(interceptorRegistry)), nil -} diff --git a/internal/protocols/webrtc/incoming_track.go b/internal/protocols/webrtc/incoming_track.go index 6a841511..26986340 100644 --- a/internal/protocols/webrtc/incoming_track.go +++ b/internal/protocols/webrtc/incoming_track.go @@ -19,6 +19,88 @@ const ( keyFrameInterval = 2 * time.Second ) +var incomingVideoCodecs = []webrtc.RTPCodecParameters{ + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeAV1, + ClockRate: 90000, + }, + PayloadType: 96, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP9, + ClockRate: 90000, + SDPFmtpLine: "profile-id=0", + }, + PayloadType: 97, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP9, + ClockRate: 90000, + SDPFmtpLine: "profile-id=1", + }, + PayloadType: 98, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + ClockRate: 90000, + }, + PayloadType: 99, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, + ClockRate: 90000, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, + PayloadType: 100, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, + ClockRate: 90000, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", + }, + PayloadType: 101, + }, +} + +var incomingAudioCodecs = []webrtc.RTPCodecParameters{ + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, + ClockRate: 48000, + Channels: 2, + SDPFmtpLine: "minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1", + }, + PayloadType: 111, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeG722, + ClockRate: 8000, + }, + PayloadType: 9, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypePCMU, + ClockRate: 8000, + }, + PayloadType: 0, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypePCMA, + ClockRate: 8000, + }, + PayloadType: 8, + }, +} + // IncomingTrack is an incoming track. type IncomingTrack struct { track *webrtc.TrackRemote diff --git a/internal/protocols/webrtc/outgoing_track.go b/internal/protocols/webrtc/outgoing_track.go index ff53c1a5..f02c66fe 100644 --- a/internal/protocols/webrtc/outgoing_track.go +++ b/internal/protocols/webrtc/outgoing_track.go @@ -8,130 +8,131 @@ import ( "github.com/pion/webrtc/v3" ) -type addTrackFunc func(webrtc.TrackLocal) (*webrtc.RTPSender, error) - // OutgoingTrack is a WebRTC outgoing track type OutgoingTrack struct { + Format format.Format + track *webrtc.TrackLocalStaticRTP } -func newOutgoingTrack(forma format.Format, addTrack addTrackFunc) (*OutgoingTrack, error) { - t := &OutgoingTrack{} - - switch forma := forma.(type) { +func (t *OutgoingTrack) codecParameters() (webrtc.RTPCodecParameters, error) { + switch forma := t.Format.(type) { case *format.AV1: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ MimeType: webrtc.MimeTypeAV1, ClockRate: 90000, }, - "av1", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 96, + }, nil case *format.VP9: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeVP9, - ClockRate: uint32(forma.ClockRate()), + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP9, + ClockRate: 90000, + SDPFmtpLine: "profile-id=1", }, - "vp9", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 98, + }, nil case *format.VP8: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ MimeType: webrtc.MimeTypeVP8, - ClockRate: uint32(forma.ClockRate()), + ClockRate: 90000, }, - "vp8", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 99, + }, nil case *format.H264: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ - MimeType: webrtc.MimeTypeH264, - ClockRate: uint32(forma.ClockRate()), + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, + ClockRate: 90000, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", }, - "h264", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 101, + }, nil case *format.Opus: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ MimeType: webrtc.MimeTypeOpus, - ClockRate: uint32(forma.ClockRate()), + ClockRate: 48000, Channels: 2, }, - "opus", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 111, + }, nil case *format.G722: - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ MimeType: webrtc.MimeTypeG722, - ClockRate: uint32(forma.ClockRate()), + ClockRate: 8000, }, - "g722", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 9, + }, nil case *format.G711: - var mtyp string if forma.MULaw { - mtyp = webrtc.MimeTypePCMU - } else { - mtyp = webrtc.MimeTypePCMA + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypePCMU, + ClockRate: 8000, + }, + PayloadType: 0, + }, nil } - var err error - t.track, err = webrtc.NewTrackLocalStaticRTP( - webrtc.RTPCodecCapability{ - MimeType: mtyp, - ClockRate: uint32(forma.ClockRate()), + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypePCMA, + ClockRate: 8000, }, - "g711", - webrtcStreamID, - ) - if err != nil { - return nil, err - } + PayloadType: 8, + }, nil default: - return nil, fmt.Errorf("unsupported track type: %T", forma) + return webrtc.RTPCodecParameters{}, fmt.Errorf("unsupported track type: %T", forma) + } +} + +func (t *OutgoingTrack) isVideo() bool { + switch t.Format.(type) { + case *format.AV1, + *format.VP9, + *format.VP8, + *format.H264: + return true } - sender, err := addTrack(t.track) + return false +} + +func (t *OutgoingTrack) setup(p *PeerConnection) error { + params, _ := t.codecParameters() //nolint:errcheck + + var trackID string + if t.isVideo() { + trackID = "video" + } else { + trackID = "audio" + } + + var err error + t.track, err = webrtc.NewTrackLocalStaticRTP( + params.RTPCodecCapability, + trackID, + webrtcStreamID, + ) if err != nil { - return nil, err + return err + } + + sender, err := p.wr.AddTrack(t.track) + if err != nil { + return err } // read incoming RTCP packets to make interceptors work @@ -145,7 +146,7 @@ func newOutgoingTrack(forma format.Format, addTrack addTrackFunc) (*OutgoingTrac } }() - return t, nil + return nil } // WriteRTP writes a RTP packet. diff --git a/internal/protocols/webrtc/peer_connection.go b/internal/protocols/webrtc/peer_connection.go index c105c5aa..ca8b3165 100644 --- a/internal/protocols/webrtc/peer_connection.go +++ b/internal/protocols/webrtc/peer_connection.go @@ -7,7 +7,8 @@ import ( "sync" "time" - "github.com/bluenviron/gortsplib/v4/pkg/format" + "github.com/pion/ice/v2" + "github.com/pion/interceptor" "github.com/pion/webrtc/v3" "github.com/bluenviron/mediamtx/internal/logger" @@ -19,6 +20,15 @@ const ( webrtcStreamID = "mediamtx" ) +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + type trackRecvPair struct { track *webrtc.TrackRemote receiver *webrtc.RTPReceiver @@ -26,10 +36,16 @@ type trackRecvPair struct { // PeerConnection is a wrapper around webrtc.PeerConnection. type PeerConnection struct { - ICEServers []webrtc.ICEServer - API *webrtc.API - Publish bool - Log logger.Writer + ICEServers []webrtc.ICEServer + ICEUDPMux ice.UDPMux + ICETCPMux ice.TCPMux + LocalRandomUDP bool + IPsFromInterfaces bool + IPsFromInterfacesList []string + AdditionalHosts []string + Publish bool + OutgoingTracks []*OutgoingTrack + Log logger.Writer wr *webrtc.PeerConnection stateChangeMutex sync.Mutex @@ -46,12 +62,87 @@ type PeerConnection struct { // Start starts the peer connection. func (co *PeerConnection) Start() error { - configuration := webrtc.Configuration{ - ICEServers: co.ICEServers, + settingsEngine := webrtc.SettingEngine{} + + settingsEngine.SetInterfaceFilter(func(iface string) bool { + return co.IPsFromInterfaces && (len(co.IPsFromInterfacesList) == 0 || + stringInSlice(iface, co.IPsFromInterfacesList)) + }) + + settingsEngine.SetAdditionalHosts(co.AdditionalHosts) + + var networkTypes []webrtc.NetworkType + + // always enable UDP in order to support STUN/TURN + networkTypes = append(networkTypes, webrtc.NetworkTypeUDP4) + + if co.ICEUDPMux != nil { + settingsEngine.SetICEUDPMux(co.ICEUDPMux) } - var err error - co.wr, err = co.API.NewPeerConnection(configuration) + if co.ICETCPMux != nil { + settingsEngine.SetICETCPMux(co.ICETCPMux) + networkTypes = append(networkTypes, webrtc.NetworkTypeTCP4) + } + + if co.LocalRandomUDP { + settingsEngine.SetICEUDPRandom(true) + } + + settingsEngine.SetNetworkTypes(networkTypes) + + mediaEngine := &webrtc.MediaEngine{} + + if co.Publish { + for _, tr := range co.OutgoingTracks { + params, err := tr.codecParameters() + if err != nil { + return err + } + + var codecType webrtc.RTPCodecType + if tr.isVideo() { + codecType = webrtc.RTPCodecTypeVideo + } else { + codecType = webrtc.RTPCodecTypeAudio + } + + err = mediaEngine.RegisterCodec(params, codecType) + if err != nil { + return err + } + } + } else { + for _, codec := range incomingVideoCodecs { + err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo) + if err != nil { + return err + } + } + + for _, codec := range incomingAudioCodecs { + err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio) + if err != nil { + return err + } + } + } + + interceptorRegistry := &interceptor.Registry{} + + err := webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry) + if err != nil { + return err + } + + api := webrtc.NewAPI( + webrtc.WithSettingEngine(settingsEngine), + webrtc.WithMediaEngine(mediaEngine), + webrtc.WithInterceptorRegistry(interceptorRegistry)) + + co.wr, err = api.NewPeerConnection(webrtc.Configuration{ + ICEServers: co.ICEServers, + }) if err != nil { return err } @@ -65,7 +156,15 @@ func (co *PeerConnection) Start() error { co.ctx, co.ctxCancel = context.WithCancel(context.Background()) - if !co.Publish { + if co.Publish { + for _, tr := range co.OutgoingTracks { + err = tr.setup(co) + if err != nil { + co.wr.Close() //nolint:errcheck + return err + } + } + } else { _, err = co.wr.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RtpTransceiverInit{ Direction: webrtc.RTPTransceiverDirectionRecvonly, }) @@ -177,6 +276,9 @@ func (co *PeerConnection) CreateFullAnswer( answer, err := co.wr.CreateAnswer(nil) if err != nil { + if err.Error() == "unable to populate media section, RTPSender created with no codecs" { + return nil, fmt.Errorf("track codecs are not supported by remote") + } return nil, err } @@ -268,27 +370,6 @@ func (co *PeerConnection) GatherIncomingTracks( } } -// SetupOutgoingTracks setups outgoing tracks. -func (co *PeerConnection) SetupOutgoingTracks( - videoTrack format.Format, - audioTrack format.Format, -) ([]*OutgoingTrack, error) { - var tracks []*OutgoingTrack - - for _, forma := range []format.Format{videoTrack, audioTrack} { - if forma != nil { - track, err := newOutgoingTrack(forma, co.wr.AddTrack) - if err != nil { - return nil, err - } - - tracks = append(tracks, track) - } - } - - return tracks, nil -} - // Connected returns when connected. func (co *PeerConnection) Connected() <-chan struct{} { return co.connected diff --git a/internal/protocols/webrtc/peer_connection_test.go b/internal/protocols/webrtc/peer_connection_test.go index 1aa747f7..d4d0fa7c 100644 --- a/internal/protocols/webrtc/peer_connection_test.go +++ b/internal/protocols/webrtc/peer_connection_test.go @@ -9,18 +9,13 @@ import ( ) func TestPeerConnectionCloseAfterError(t *testing.T) { - api, err := NewAPI(APIConf{ + pc := &PeerConnection{ LocalRandomUDP: true, IPsFromInterfaces: true, - }) - require.NoError(t, err) - - pc := &PeerConnection{ - API: api, - Publish: false, - Log: test.NilLogger, + Publish: false, + Log: test.NilLogger, } - err = pc.Start() + err := pc.Start() require.NoError(t, err) _, err = pc.CreatePartialOffer() diff --git a/internal/protocols/webrtc/whip_client.go b/internal/protocols/webrtc/whip_client.go index 95f35769..40511200 100644 --- a/internal/protocols/webrtc/whip_client.go +++ b/internal/protocols/webrtc/whip_client.go @@ -38,31 +38,28 @@ func (c *WHIPClient) Publish( return nil, err } - api, err := NewAPI(APIConf{ - LocalRandomUDP: true, - IPsFromInterfaces: true, - }) - if err != nil { - return nil, err + var outgoingTracks []*OutgoingTrack + + if videoTrack != nil { + outgoingTracks = append(outgoingTracks, &OutgoingTrack{Format: videoTrack}) + } + if audioTrack != nil { + outgoingTracks = append(outgoingTracks, &OutgoingTrack{Format: audioTrack}) } c.pc = &PeerConnection{ - ICEServers: iceServers, - API: api, - Publish: true, - Log: c.Log, + ICEServers: iceServers, + LocalRandomUDP: true, + IPsFromInterfaces: true, + Publish: true, + OutgoingTracks: outgoingTracks, + Log: c.Log, } err = c.pc.Start() if err != nil { return nil, err } - tracks, err := c.pc.SetupOutgoingTracks(videoTrack, audioTrack) - if err != nil { - c.pc.Close() - return nil, err - } - offer, err := c.pc.CreatePartialOffer() if err != nil { c.pc.Close() @@ -114,7 +111,7 @@ outer: } } - return tracks, nil + return outgoingTracks, nil } // Read reads tracks. @@ -124,19 +121,12 @@ func (c *WHIPClient) Read(ctx context.Context) ([]*IncomingTrack, error) { return nil, err } - api, err := NewAPI(APIConf{ + c.pc = &PeerConnection{ + ICEServers: iceServers, LocalRandomUDP: true, IPsFromInterfaces: true, - }) - if err != nil { - return nil, err - } - - c.pc = &PeerConnection{ - ICEServers: iceServers, - API: api, - Publish: false, - Log: c.Log, + Publish: false, + Log: c.Log, } err = c.pc.Start() if err != nil { diff --git a/internal/servers/webrtc/server.go b/internal/servers/webrtc/server.go index 5db2f6ed..ba855eb6 100644 --- a/internal/servers/webrtc/server.go +++ b/internal/servers/webrtc/server.go @@ -17,6 +17,7 @@ import ( "time" "github.com/google/uuid" + "github.com/pion/ice/v2" "github.com/pion/logging" pwebrtc "github.com/pion/webrtc/v3" @@ -24,7 +25,6 @@ import ( "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/logger" - "github.com/bluenviron/mediamtx/internal/protocols/webrtc" "github.com/bluenviron/mediamtx/internal/restrictnetwork" "github.com/bluenviron/mediamtx/internal/stream" ) @@ -201,7 +201,8 @@ type Server struct { httpServer *httpServer udpMuxLn net.PacketConn tcpMuxLn net.Listener - api *pwebrtc.API + iceUDPMux ice.UDPMux + iceTCPMux ice.TCPMux sessions map[*session]struct{} sessionsBySecret map[uuid.UUID]*session @@ -252,13 +253,6 @@ func (s *Server) Initialize() error { return err } - apiConf := webrtc.APIConf{ - LocalRandomUDP: false, - IPsFromInterfaces: s.IPsFromInterfaces, - IPsFromInterfacesList: s.IPsFromInterfacesList, - AdditionalHosts: s.AdditionalHosts, - } - if s.LocalUDPAddress != "" { s.udpMuxLn, err = net.ListenPacket(restrictnetwork.Restrict("udp", s.LocalUDPAddress)) if err != nil { @@ -266,7 +260,7 @@ func (s *Server) Initialize() error { ctxCancel() return err } - apiConf.ICEUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, s.udpMuxLn) + s.iceUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, s.udpMuxLn) } if s.LocalTCPAddress != "" { @@ -277,16 +271,7 @@ func (s *Server) Initialize() error { ctxCancel() return err } - apiConf.ICETCPMux = pwebrtc.NewICETCPMux(webrtcNilLogger, s.tcpMuxLn, 8) - } - - s.api, err = webrtc.NewAPI(apiConf) - if err != nil { - s.udpMuxLn.Close() - s.tcpMuxLn.Close() - s.httpServer.close() - ctxCancel() - return err + s.iceTCPMux = pwebrtc.NewICETCPMux(webrtcNilLogger, s.tcpMuxLn, 8) } str := "listener opened on " + s.Address + " (HTTP)" @@ -325,14 +310,18 @@ outer: select { case req := <-s.chNewSession: sx := &session{ - parentCtx: s.ctx, - writeQueueSize: s.WriteQueueSize, - api: s.api, - req: req, - wg: &wg, - externalCmdPool: s.ExternalCmdPool, - pathManager: s.PathManager, - parent: s, + parentCtx: s.ctx, + writeQueueSize: s.WriteQueueSize, + ipsFromInterfaces: s.IPsFromInterfaces, + ipsFromInterfacesList: s.IPsFromInterfacesList, + additionalHosts: s.AdditionalHosts, + iceUDPMux: s.iceUDPMux, + iceTCPMux: s.iceTCPMux, + req: req, + wg: &wg, + externalCmdPool: s.ExternalCmdPool, + pathManager: s.PathManager, + parent: s, } sx.initialize() s.sessions[sx] = struct{}{} diff --git a/internal/servers/webrtc/server_test.go b/internal/servers/webrtc/server_test.go index 12f02d67..197917db 100644 --- a/internal/servers/webrtc/server_test.go +++ b/internal/servers/webrtc/server_test.go @@ -415,7 +415,7 @@ func TestServerRead(t *testing.T) { Header: rtp.Header{ Version: 2, Marker: true, - PayloadType: 100, + PayloadType: 101, SequenceNumber: pkt.SequenceNumber, Timestamp: pkt.Timestamp, SSRC: pkt.SSRC, diff --git a/internal/servers/webrtc/session.go b/internal/servers/webrtc/session.go index 56435b9e..cb98580d 100644 --- a/internal/servers/webrtc/session.go +++ b/internal/servers/webrtc/session.go @@ -18,6 +18,7 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9" "github.com/bluenviron/gortsplib/v4/pkg/rtptime" "github.com/google/uuid" + "github.com/pion/ice/v2" "github.com/pion/sdp/v3" pwebrtc "github.com/pion/webrtc/v3" @@ -281,14 +282,18 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription { } type session struct { - parentCtx context.Context - writeQueueSize int - api *pwebrtc.API - req webRTCNewSessionReq - wg *sync.WaitGroup - externalCmdPool *externalcmd.Pool - pathManager serverPathManager - parent *Server + parentCtx context.Context + writeQueueSize int + ipsFromInterfaces bool + ipsFromInterfacesList []string + additionalHosts []string + iceUDPMux ice.UDPMux + iceTCPMux ice.TCPMux + req webRTCNewSessionReq + wg *sync.WaitGroup + externalCmdPool *externalcmd.Pool + pathManager serverPathManager + parent *Server ctx context.Context ctxCancel func() @@ -403,10 +408,14 @@ func (s *session) runPublish() (int, error) { } pc := &webrtc.PeerConnection{ - ICEServers: iceServers, - API: s.api, - Publish: false, - Log: s, + ICEServers: iceServers, + IPsFromInterfaces: s.ipsFromInterfaces, + IPsFromInterfacesList: s.ipsFromInterfacesList, + AdditionalHosts: s.additionalHosts, + ICEUDPMux: s.iceUDPMux, + ICETCPMux: s.iceTCPMux, + Publish: false, + Log: s, } err = pc.Start() if err != nil { @@ -537,18 +546,6 @@ func (s *session) runRead() (int, error) { return http.StatusInternalServerError, err } - pc := &webrtc.PeerConnection{ - ICEServers: iceServers, - API: s.api, - Publish: false, - Log: s, - } - err = pc.Start() - if err != nil { - return http.StatusBadRequest, err - } - defer pc.Close() - writer := asyncwriter.New(s.writeQueueSize, s) videoTrack, videoSetup := findVideoTrack(stream, writer) @@ -558,10 +555,31 @@ func (s *session) runRead() (int, error) { return http.StatusBadRequest, errNoSupportedCodecs } - tracks, err := pc.SetupOutgoingTracks(videoTrack, audioTrack) + var outgoingTracks []*webrtc.OutgoingTrack + + if videoTrack != nil { + outgoingTracks = append(outgoingTracks, &webrtc.OutgoingTrack{Format: videoTrack}) + } + if audioTrack != nil { + outgoingTracks = append(outgoingTracks, &webrtc.OutgoingTrack{Format: audioTrack}) + } + + pc := &webrtc.PeerConnection{ + ICEServers: iceServers, + IPsFromInterfaces: s.ipsFromInterfaces, + IPsFromInterfacesList: s.ipsFromInterfacesList, + AdditionalHosts: s.additionalHosts, + ICEUDPMux: s.iceUDPMux, + ICETCPMux: s.iceTCPMux, + Publish: true, + OutgoingTracks: outgoingTracks, + Log: s, + } + err = pc.Start() if err != nil { return http.StatusBadRequest, err } + defer pc.Close() offer := whipOffer(s.req.offer) @@ -588,7 +606,7 @@ func (s *session) runRead() (int, error) { n := 0 if videoTrack != nil { - err := videoSetup(tracks[n]) + err := videoSetup(outgoingTracks[n]) if err != nil { return 0, err } @@ -596,7 +614,7 @@ func (s *session) runRead() (int, error) { } if audioTrack != nil { - err := audioSetup(tracks[n]) + err := audioSetup(outgoingTracks[n]) if err != nil { return 0, err } diff --git a/internal/staticsources/webrtc/source_test.go b/internal/staticsources/webrtc/source_test.go index 612cc859..ef809dd3 100644 --- a/internal/staticsources/webrtc/source_test.go +++ b/internal/staticsources/webrtc/source_test.go @@ -27,30 +27,21 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription { } func TestSource(t *testing.T) { - api, err := webrtc.NewAPI(webrtc.APIConf{ + outgoingTracks := []*webrtc.OutgoingTrack{{Format: &format.Opus{ + PayloadTyp: 111, + ChannelCount: 2, + }}} + pc := &webrtc.PeerConnection{ LocalRandomUDP: true, IPsFromInterfaces: true, - }) - require.NoError(t, err) - - pc := &webrtc.PeerConnection{ - API: api, - Publish: true, - Log: test.NilLogger, + Publish: true, + OutgoingTracks: outgoingTracks, + Log: test.NilLogger, } - err = pc.Start() + err := pc.Start() require.NoError(t, err) defer pc.Close() - tracks, err := pc.SetupOutgoingTracks( - nil, - &format.Opus{ - PayloadTyp: 111, - ChannelCount: 2, - }, - ) - require.NoError(t, err) - state := 0 httpServ := &http.Server{ @@ -87,7 +78,7 @@ func TestSource(t *testing.T) { err3 := pc.WaitUntilConnected(context.Background()) require.NoError(t, err3) - err3 = tracks[0].WriteRTP(&rtp.Packet{ + err3 = outgoingTracks[0].WriteRTP(&rtp.Packet{ Header: rtp.Header{ Version: 2, Marker: true,