forked from External/mediamtx
webrtc: in answer, include codecs that are actually in use (#3374)
This commit is contained in:
parent
0ace308672
commit
407702380a
10 changed files with 376 additions and 397 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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{}{}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue