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
)
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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