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
|
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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue