1
0
Fork 0
forked from External/mediamtx

webrtc: in answer, include codecs that are actually in use (#3374)

This commit is contained in:
Alessandro Ros 2024-05-19 19:41:42 +02:00 committed by GitHub
parent 0ace308672
commit 407702380a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 376 additions and 397 deletions

View file

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

View file

@ -19,6 +19,88 @@ const (
keyFrameInterval = 2 * time.Second 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. // IncomingTrack is an incoming track.
type IncomingTrack struct { type IncomingTrack struct {
track *webrtc.TrackRemote track *webrtc.TrackRemote

View file

@ -8,130 +8,131 @@ import (
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
type addTrackFunc func(webrtc.TrackLocal) (*webrtc.RTPSender, error)
// OutgoingTrack is a WebRTC outgoing track // OutgoingTrack is a WebRTC outgoing track
type OutgoingTrack struct { type OutgoingTrack struct {
Format format.Format
track *webrtc.TrackLocalStaticRTP track *webrtc.TrackLocalStaticRTP
} }
func newOutgoingTrack(forma format.Format, addTrack addTrackFunc) (*OutgoingTrack, error) { func (t *OutgoingTrack) codecParameters() (webrtc.RTPCodecParameters, error) {
t := &OutgoingTrack{} switch forma := t.Format.(type) {
switch forma := forma.(type) {
case *format.AV1: case *format.AV1:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeAV1, MimeType: webrtc.MimeTypeAV1,
ClockRate: 90000, ClockRate: 90000,
}, },
"av1", PayloadType: 96,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.VP9: case *format.VP9:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP9, MimeType: webrtc.MimeTypeVP9,
ClockRate: uint32(forma.ClockRate()), ClockRate: 90000,
SDPFmtpLine: "profile-id=1",
}, },
"vp9", PayloadType: 98,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.VP8: case *format.VP8:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP8, MimeType: webrtc.MimeTypeVP8,
ClockRate: uint32(forma.ClockRate()), ClockRate: 90000,
}, },
"vp8", PayloadType: 99,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.H264: case *format.H264:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeH264, MimeType: webrtc.MimeTypeH264,
ClockRate: uint32(forma.ClockRate()), ClockRate: 90000,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
}, },
"h264", PayloadType: 101,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.Opus: case *format.Opus:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeOpus, MimeType: webrtc.MimeTypeOpus,
ClockRate: uint32(forma.ClockRate()), ClockRate: 48000,
Channels: 2, Channels: 2,
}, },
"opus", PayloadType: 111,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.G722: case *format.G722:
var err error return webrtc.RTPCodecParameters{
t.track, err = webrtc.NewTrackLocalStaticRTP( RTPCodecCapability: webrtc.RTPCodecCapability{
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeG722, MimeType: webrtc.MimeTypeG722,
ClockRate: uint32(forma.ClockRate()), ClockRate: 8000,
}, },
"g722", PayloadType: 9,
webrtcStreamID, }, nil
)
if err != nil {
return nil, err
}
case *format.G711: case *format.G711:
var mtyp string
if forma.MULaw { if forma.MULaw {
mtyp = webrtc.MimeTypePCMU return webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypePCMU,
ClockRate: 8000,
},
PayloadType: 0,
}, nil
}
return webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypePCMA,
ClockRate: 8000,
},
PayloadType: 8,
}, nil
default:
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
}
return false
}
func (t *OutgoingTrack) setup(p *PeerConnection) error {
params, _ := t.codecParameters() //nolint:errcheck
var trackID string
if t.isVideo() {
trackID = "video"
} else { } else {
mtyp = webrtc.MimeTypePCMA trackID = "audio"
} }
var err error var err error
t.track, err = webrtc.NewTrackLocalStaticRTP( t.track, err = webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{ params.RTPCodecCapability,
MimeType: mtyp, trackID,
ClockRate: uint32(forma.ClockRate()),
},
"g711",
webrtcStreamID, webrtcStreamID,
) )
if err != nil { if err != nil {
return nil, err return err
} }
default: sender, err := p.wr.AddTrack(t.track)
return nil, fmt.Errorf("unsupported track type: %T", forma)
}
sender, err := addTrack(t.track)
if err != nil { if err != nil {
return nil, err return err
} }
// read incoming RTCP packets to make interceptors work // 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. // WriteRTP writes a RTP packet.

View file

@ -7,7 +7,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/pion/ice/v2"
"github.com/pion/interceptor"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
@ -19,6 +20,15 @@ const (
webrtcStreamID = "mediamtx" webrtcStreamID = "mediamtx"
) )
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
type trackRecvPair struct { type trackRecvPair struct {
track *webrtc.TrackRemote track *webrtc.TrackRemote
receiver *webrtc.RTPReceiver receiver *webrtc.RTPReceiver
@ -27,8 +37,14 @@ type trackRecvPair struct {
// PeerConnection is a wrapper around webrtc.PeerConnection. // PeerConnection is a wrapper around webrtc.PeerConnection.
type PeerConnection struct { type PeerConnection struct {
ICEServers []webrtc.ICEServer ICEServers []webrtc.ICEServer
API *webrtc.API ICEUDPMux ice.UDPMux
ICETCPMux ice.TCPMux
LocalRandomUDP bool
IPsFromInterfaces bool
IPsFromInterfacesList []string
AdditionalHosts []string
Publish bool Publish bool
OutgoingTracks []*OutgoingTrack
Log logger.Writer Log logger.Writer
wr *webrtc.PeerConnection wr *webrtc.PeerConnection
@ -46,12 +62,87 @@ type PeerConnection struct {
// Start starts the peer connection. // Start starts the peer connection.
func (co *PeerConnection) Start() error { func (co *PeerConnection) Start() error {
configuration := webrtc.Configuration{ settingsEngine := webrtc.SettingEngine{}
ICEServers: co.ICEServers,
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 if co.ICETCPMux != nil {
co.wr, err = co.API.NewPeerConnection(configuration) 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 { if err != nil {
return err return err
} }
@ -65,7 +156,15 @@ func (co *PeerConnection) Start() error {
co.ctx, co.ctxCancel = context.WithCancel(context.Background()) 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{ _, err = co.wr.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RtpTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionRecvonly, Direction: webrtc.RTPTransceiverDirectionRecvonly,
}) })
@ -177,6 +276,9 @@ func (co *PeerConnection) CreateFullAnswer(
answer, err := co.wr.CreateAnswer(nil) answer, err := co.wr.CreateAnswer(nil)
if err != 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 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. // Connected returns when connected.
func (co *PeerConnection) Connected() <-chan struct{} { func (co *PeerConnection) Connected() <-chan struct{} {
return co.connected return co.connected

View file

@ -9,18 +9,13 @@ import (
) )
func TestPeerConnectionCloseAfterError(t *testing.T) { func TestPeerConnectionCloseAfterError(t *testing.T) {
api, err := NewAPI(APIConf{ pc := &PeerConnection{
LocalRandomUDP: true, LocalRandomUDP: true,
IPsFromInterfaces: true, IPsFromInterfaces: true,
})
require.NoError(t, err)
pc := &PeerConnection{
API: api,
Publish: false, Publish: false,
Log: test.NilLogger, Log: test.NilLogger,
} }
err = pc.Start() err := pc.Start()
require.NoError(t, err) require.NoError(t, err)
_, err = pc.CreatePartialOffer() _, err = pc.CreatePartialOffer()

View file

@ -38,18 +38,21 @@ func (c *WHIPClient) Publish(
return nil, err return nil, err
} }
api, err := NewAPI(APIConf{ var outgoingTracks []*OutgoingTrack
LocalRandomUDP: true,
IPsFromInterfaces: true, if videoTrack != nil {
}) outgoingTracks = append(outgoingTracks, &OutgoingTrack{Format: videoTrack})
if err != nil { }
return nil, err if audioTrack != nil {
outgoingTracks = append(outgoingTracks, &OutgoingTrack{Format: audioTrack})
} }
c.pc = &PeerConnection{ c.pc = &PeerConnection{
ICEServers: iceServers, ICEServers: iceServers,
API: api, LocalRandomUDP: true,
IPsFromInterfaces: true,
Publish: true, Publish: true,
OutgoingTracks: outgoingTracks,
Log: c.Log, Log: c.Log,
} }
err = c.pc.Start() err = c.pc.Start()
@ -57,12 +60,6 @@ func (c *WHIPClient) Publish(
return nil, err return nil, err
} }
tracks, err := c.pc.SetupOutgoingTracks(videoTrack, audioTrack)
if err != nil {
c.pc.Close()
return nil, err
}
offer, err := c.pc.CreatePartialOffer() offer, err := c.pc.CreatePartialOffer()
if err != nil { if err != nil {
c.pc.Close() c.pc.Close()
@ -114,7 +111,7 @@ outer:
} }
} }
return tracks, nil return outgoingTracks, nil
} }
// Read reads tracks. // Read reads tracks.
@ -124,17 +121,10 @@ func (c *WHIPClient) Read(ctx context.Context) ([]*IncomingTrack, error) {
return nil, err return nil, err
} }
api, err := NewAPI(APIConf{
LocalRandomUDP: true,
IPsFromInterfaces: true,
})
if err != nil {
return nil, err
}
c.pc = &PeerConnection{ c.pc = &PeerConnection{
ICEServers: iceServers, ICEServers: iceServers,
API: api, LocalRandomUDP: true,
IPsFromInterfaces: true,
Publish: false, Publish: false,
Log: c.Log, Log: c.Log,
} }

View file

@ -17,6 +17,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pion/ice/v2"
"github.com/pion/logging" "github.com/pion/logging"
pwebrtc "github.com/pion/webrtc/v3" pwebrtc "github.com/pion/webrtc/v3"
@ -24,7 +25,6 @@ import (
"github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/defs"
"github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/externalcmd"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/webrtc"
"github.com/bluenviron/mediamtx/internal/restrictnetwork" "github.com/bluenviron/mediamtx/internal/restrictnetwork"
"github.com/bluenviron/mediamtx/internal/stream" "github.com/bluenviron/mediamtx/internal/stream"
) )
@ -201,7 +201,8 @@ type Server struct {
httpServer *httpServer httpServer *httpServer
udpMuxLn net.PacketConn udpMuxLn net.PacketConn
tcpMuxLn net.Listener tcpMuxLn net.Listener
api *pwebrtc.API iceUDPMux ice.UDPMux
iceTCPMux ice.TCPMux
sessions map[*session]struct{} sessions map[*session]struct{}
sessionsBySecret map[uuid.UUID]*session sessionsBySecret map[uuid.UUID]*session
@ -252,13 +253,6 @@ func (s *Server) Initialize() error {
return err return err
} }
apiConf := webrtc.APIConf{
LocalRandomUDP: false,
IPsFromInterfaces: s.IPsFromInterfaces,
IPsFromInterfacesList: s.IPsFromInterfacesList,
AdditionalHosts: s.AdditionalHosts,
}
if s.LocalUDPAddress != "" { if s.LocalUDPAddress != "" {
s.udpMuxLn, err = net.ListenPacket(restrictnetwork.Restrict("udp", s.LocalUDPAddress)) s.udpMuxLn, err = net.ListenPacket(restrictnetwork.Restrict("udp", s.LocalUDPAddress))
if err != nil { if err != nil {
@ -266,7 +260,7 @@ func (s *Server) Initialize() error {
ctxCancel() ctxCancel()
return err return err
} }
apiConf.ICEUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, s.udpMuxLn) s.iceUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, s.udpMuxLn)
} }
if s.LocalTCPAddress != "" { if s.LocalTCPAddress != "" {
@ -277,16 +271,7 @@ func (s *Server) Initialize() error {
ctxCancel() ctxCancel()
return err return err
} }
apiConf.ICETCPMux = pwebrtc.NewICETCPMux(webrtcNilLogger, s.tcpMuxLn, 8) s.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
} }
str := "listener opened on " + s.Address + " (HTTP)" str := "listener opened on " + s.Address + " (HTTP)"
@ -327,7 +312,11 @@ outer:
sx := &session{ sx := &session{
parentCtx: s.ctx, parentCtx: s.ctx,
writeQueueSize: s.WriteQueueSize, writeQueueSize: s.WriteQueueSize,
api: s.api, ipsFromInterfaces: s.IPsFromInterfaces,
ipsFromInterfacesList: s.IPsFromInterfacesList,
additionalHosts: s.AdditionalHosts,
iceUDPMux: s.iceUDPMux,
iceTCPMux: s.iceTCPMux,
req: req, req: req,
wg: &wg, wg: &wg,
externalCmdPool: s.ExternalCmdPool, externalCmdPool: s.ExternalCmdPool,

View file

@ -415,7 +415,7 @@ func TestServerRead(t *testing.T) {
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
Marker: true, Marker: true,
PayloadType: 100, PayloadType: 101,
SequenceNumber: pkt.SequenceNumber, SequenceNumber: pkt.SequenceNumber,
Timestamp: pkt.Timestamp, Timestamp: pkt.Timestamp,
SSRC: pkt.SSRC, SSRC: pkt.SSRC,

View file

@ -18,6 +18,7 @@ import (
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9" "github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime" "github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pion/ice/v2"
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
pwebrtc "github.com/pion/webrtc/v3" pwebrtc "github.com/pion/webrtc/v3"
@ -283,7 +284,11 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription {
type session struct { type session struct {
parentCtx context.Context parentCtx context.Context
writeQueueSize int writeQueueSize int
api *pwebrtc.API ipsFromInterfaces bool
ipsFromInterfacesList []string
additionalHosts []string
iceUDPMux ice.UDPMux
iceTCPMux ice.TCPMux
req webRTCNewSessionReq req webRTCNewSessionReq
wg *sync.WaitGroup wg *sync.WaitGroup
externalCmdPool *externalcmd.Pool externalCmdPool *externalcmd.Pool
@ -404,7 +409,11 @@ func (s *session) runPublish() (int, error) {
pc := &webrtc.PeerConnection{ pc := &webrtc.PeerConnection{
ICEServers: iceServers, ICEServers: iceServers,
API: s.api, IPsFromInterfaces: s.ipsFromInterfaces,
IPsFromInterfacesList: s.ipsFromInterfacesList,
AdditionalHosts: s.additionalHosts,
ICEUDPMux: s.iceUDPMux,
ICETCPMux: s.iceTCPMux,
Publish: false, Publish: false,
Log: s, Log: s,
} }
@ -537,18 +546,6 @@ func (s *session) runRead() (int, error) {
return http.StatusInternalServerError, err 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) writer := asyncwriter.New(s.writeQueueSize, s)
videoTrack, videoSetup := findVideoTrack(stream, writer) videoTrack, videoSetup := findVideoTrack(stream, writer)
@ -558,10 +555,31 @@ func (s *session) runRead() (int, error) {
return http.StatusBadRequest, errNoSupportedCodecs 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 { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err
} }
defer pc.Close()
offer := whipOffer(s.req.offer) offer := whipOffer(s.req.offer)
@ -588,7 +606,7 @@ func (s *session) runRead() (int, error) {
n := 0 n := 0
if videoTrack != nil { if videoTrack != nil {
err := videoSetup(tracks[n]) err := videoSetup(outgoingTracks[n])
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -596,7 +614,7 @@ func (s *session) runRead() (int, error) {
} }
if audioTrack != nil { if audioTrack != nil {
err := audioSetup(tracks[n]) err := audioSetup(outgoingTracks[n])
if err != nil { if err != nil {
return 0, err return 0, err
} }

View file

@ -27,29 +27,20 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription {
} }
func TestSource(t *testing.T) { func TestSource(t *testing.T) {
api, err := webrtc.NewAPI(webrtc.APIConf{ outgoingTracks := []*webrtc.OutgoingTrack{{Format: &format.Opus{
LocalRandomUDP: true,
IPsFromInterfaces: true,
})
require.NoError(t, err)
pc := &webrtc.PeerConnection{
API: api,
Publish: true,
Log: test.NilLogger,
}
err = pc.Start()
require.NoError(t, err)
defer pc.Close()
tracks, err := pc.SetupOutgoingTracks(
nil,
&format.Opus{
PayloadTyp: 111, PayloadTyp: 111,
ChannelCount: 2, ChannelCount: 2,
}, }}}
) pc := &webrtc.PeerConnection{
LocalRandomUDP: true,
IPsFromInterfaces: true,
Publish: true,
OutgoingTracks: outgoingTracks,
Log: test.NilLogger,
}
err := pc.Start()
require.NoError(t, err) require.NoError(t, err)
defer pc.Close()
state := 0 state := 0
@ -87,7 +78,7 @@ func TestSource(t *testing.T) {
err3 := pc.WaitUntilConnected(context.Background()) err3 := pc.WaitUntilConnected(context.Background())
require.NoError(t, err3) require.NoError(t, err3)
err3 = tracks[0].WriteRTP(&rtp.Packet{ err3 = outgoingTracks[0].WriteRTP(&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
Marker: true, Marker: true,