webrtc: rename incoming/outgoing into inbound/outbound (#5816)
This commit is contained in:
@@ -1231,7 +1231,7 @@ func TestAPIProtocolKick(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost:8889/mypath/whip")
|
||||
require.NoError(t, err)
|
||||
|
||||
track := &webrtc.OutgoingTrack{
|
||||
track := &webrtc.OutboundTrack{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: pwebrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
@@ -1244,7 +1244,7 @@ func TestAPIProtocolKick(t *testing.T) {
|
||||
URL: u,
|
||||
Log: test.NilLogger,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*webrtc.OutgoingTrack{track},
|
||||
OutboundTracks: []*webrtc.OutboundTrack{track},
|
||||
}
|
||||
|
||||
err = c.Initialize(context.Background())
|
||||
|
||||
@@ -370,7 +370,7 @@ webrtc_sessions_rtcp_packets_sent 0
|
||||
defer tr2.CloseIdleConnections()
|
||||
hc2 := &http.Client{Transport: tr2}
|
||||
|
||||
track := &webrtc.OutgoingTrack{
|
||||
track := &webrtc.OutboundTrack{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: pwebrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
@@ -383,7 +383,7 @@ webrtc_sessions_rtcp_packets_sent 0
|
||||
URL: su,
|
||||
Log: test.NilLogger,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*webrtc.OutgoingTrack{track},
|
||||
OutboundTracks: []*webrtc.OutboundTrack{track},
|
||||
}
|
||||
|
||||
err2 = s.Initialize(context.Background())
|
||||
|
||||
@@ -64,12 +64,12 @@ func timestampToDuration(t int64, clockRate int) time.Duration {
|
||||
func setupVideoTrack(
|
||||
desc *description.Session,
|
||||
r *stream.Reader,
|
||||
) (*OutgoingTrack, error) {
|
||||
) (*OutboundTrack, error) {
|
||||
var av1Format *format.AV1
|
||||
media := desc.FindFormat(&av1Format)
|
||||
|
||||
if av1Format != nil { //nolint:dupl
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
@@ -114,7 +114,7 @@ func setupVideoTrack(
|
||||
media = desc.FindFormat(&vp9Format)
|
||||
|
||||
if vp9Format != nil {
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
@@ -161,7 +161,7 @@ func setupVideoTrack(
|
||||
media = desc.FindFormat(&vp8Format)
|
||||
|
||||
if vp8Format != nil { //nolint:dupl
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
@@ -206,7 +206,7 @@ func setupVideoTrack(
|
||||
media = desc.FindFormat(&h265Format)
|
||||
|
||||
if h265Format != nil { //nolint:dupl
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
@@ -262,7 +262,7 @@ func setupVideoTrack(
|
||||
media = desc.FindFormat(&h264Format)
|
||||
|
||||
if h264Format != nil { //nolint:dupl
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
@@ -320,7 +320,7 @@ func setupVideoTrack(
|
||||
func setupAudioTrack(
|
||||
desc *description.Session,
|
||||
r *stream.Reader,
|
||||
) (*OutgoingTrack, error) {
|
||||
) (*OutboundTrack, error) {
|
||||
var opusFormat *format.Opus
|
||||
media := desc.FindFormat(&opusFormat)
|
||||
|
||||
@@ -354,7 +354,7 @@ func setupAudioTrack(
|
||||
return nil, fmt.Errorf("unsupported channel count: %d", opusFormat.ChannelCount)
|
||||
}
|
||||
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: caps,
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ func setupAudioTrack(
|
||||
media = desc.FindFormat(&g722Format)
|
||||
|
||||
if g722Format != nil {
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeG722,
|
||||
ClockRate: 8000,
|
||||
@@ -470,7 +470,7 @@ func setupAudioTrack(
|
||||
}
|
||||
}
|
||||
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: caps,
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ func setupAudioTrack(
|
||||
return nil, fmt.Errorf("unsupported channel count: %d", lpcmFormat.ChannelCount)
|
||||
}
|
||||
|
||||
track := &OutgoingTrack{
|
||||
track := &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: mimeTypeL16,
|
||||
ClockRate: uint32(lpcmFormat.ClockRate()),
|
||||
@@ -644,12 +644,12 @@ func setupAudioTrack(
|
||||
func setupKLVDataChannel(
|
||||
desc *description.Session,
|
||||
r *stream.Reader,
|
||||
) (*OutgoingDataChannel, error) {
|
||||
) (*OutboundDataChannel, error) {
|
||||
var klvFormat *format.KLV
|
||||
media := desc.FindFormat(&klvFormat)
|
||||
|
||||
if klvFormat != nil {
|
||||
dataChan := &OutgoingDataChannel{
|
||||
dataChan := &OutboundDataChannel{
|
||||
Label: "KLV",
|
||||
}
|
||||
|
||||
@@ -683,7 +683,7 @@ func FromStream(
|
||||
}
|
||||
|
||||
if videoTrack != nil {
|
||||
pc.OutgoingTracks = append(pc.OutgoingTracks, videoTrack)
|
||||
pc.OutboundTracks = append(pc.OutboundTracks, videoTrack)
|
||||
}
|
||||
|
||||
audioTrack, err := setupAudioTrack(desc, r)
|
||||
@@ -692,7 +692,7 @@ func FromStream(
|
||||
}
|
||||
|
||||
if audioTrack != nil {
|
||||
pc.OutgoingTracks = append(pc.OutgoingTracks, audioTrack)
|
||||
pc.OutboundTracks = append(pc.OutboundTracks, audioTrack)
|
||||
}
|
||||
|
||||
klvDataChan, err := setupKLVDataChannel(desc, r)
|
||||
@@ -701,10 +701,10 @@ func FromStream(
|
||||
}
|
||||
|
||||
if klvDataChan != nil {
|
||||
pc.OutgoingDataChannels = append(pc.OutgoingDataChannels, klvDataChan)
|
||||
pc.OutboundDataChannels = append(pc.OutboundDataChannels, klvDataChan)
|
||||
}
|
||||
|
||||
if len(pc.OutgoingTracks) == 0 && len(pc.OutgoingDataChannels) == 0 {
|
||||
if len(pc.OutboundTracks) == 0 && len(pc.OutboundDataChannels) == 0 {
|
||||
return errNoSupportedCodecsFrom
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestFromStream(t *testing.T) {
|
||||
err := FromStream(desc, r, pc)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, ca.webrtcCaps, pc.OutgoingTracks[0].Caps)
|
||||
require.Equal(t, ca.webrtcCaps, pc.OutboundTracks[0].Caps)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -186,10 +186,10 @@ func TestFromStreamResampleOpus(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
|
||||
err = pc1.GatherIncomingTracks(2 * time.Second)
|
||||
err = pc1.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
tracks := pc1.IncomingTracks()
|
||||
tracks := pc1.InboundTracks()
|
||||
|
||||
done := make(chan struct{})
|
||||
n := 0
|
||||
@@ -302,10 +302,10 @@ func TestFromStreamResampleOpusAbsoluteTimestamp(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
|
||||
err = pcReader.GatherIncomingTracks(2 * time.Second)
|
||||
err = pcReader.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
tracks := pcReader.IncomingTracks()
|
||||
tracks := pcReader.InboundTracks()
|
||||
require.Len(t, tracks, 1)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
+12
-12
@@ -18,7 +18,7 @@ const (
|
||||
mimeTypeL16 = "audio/L16"
|
||||
)
|
||||
|
||||
func incomingTrackTWCCExtensionID(params webrtc.RTPParameters) uint8 {
|
||||
func inboundTrackTWCCExtensionID(params webrtc.RTPParameters) uint8 {
|
||||
for _, ext := range params.HeaderExtensions {
|
||||
if ext.URI == twccExtensionURI {
|
||||
return uint8(ext.ID)
|
||||
@@ -243,8 +243,8 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
|
||||
},
|
||||
}
|
||||
|
||||
// IncomingTrack is an incoming track.
|
||||
type IncomingTrack struct {
|
||||
// InboundTrack is an incoming track.
|
||||
type InboundTrack struct {
|
||||
OnPacketRTP func(*rtp.Packet)
|
||||
|
||||
track *webrtc.TrackRemote
|
||||
@@ -258,12 +258,12 @@ type IncomingTrack struct {
|
||||
rtpReceiver *rtpreceiver.Receiver
|
||||
}
|
||||
|
||||
func (t *IncomingTrack) initialize() {
|
||||
func (t *InboundTrack) initialize() {
|
||||
t.OnPacketRTP = func(*rtp.Packet) {}
|
||||
t.twccExtID = incomingTrackTWCCExtensionID(t.receiver.GetParameters())
|
||||
t.twccExtID = inboundTrackTWCCExtensionID(t.receiver.GetParameters())
|
||||
}
|
||||
|
||||
func (t *IncomingTrack) stripTWCCExtension(pkt *rtp.Packet) {
|
||||
func (t *InboundTrack) stripTWCCExtension(pkt *rtp.Packet) {
|
||||
if t.twccExtID == 0 || pkt.GetExtension(t.twccExtID) == nil {
|
||||
return
|
||||
}
|
||||
@@ -280,21 +280,21 @@ func (t *IncomingTrack) stripTWCCExtension(pkt *rtp.Packet) {
|
||||
}
|
||||
|
||||
// Codec returns the track codec.
|
||||
func (t *IncomingTrack) Codec() webrtc.RTPCodecParameters {
|
||||
func (t *InboundTrack) Codec() webrtc.RTPCodecParameters {
|
||||
return t.track.Codec()
|
||||
}
|
||||
|
||||
// ClockRate returns the clock rate. Needed by rtptime.GlobalDecoder
|
||||
func (t *IncomingTrack) ClockRate() int {
|
||||
func (t *InboundTrack) ClockRate() int {
|
||||
return int(t.track.Codec().ClockRate)
|
||||
}
|
||||
|
||||
// PTSEqualsDTS returns whether PTS equals DTS. Needed by rtptime.GlobalDecoder
|
||||
func (*IncomingTrack) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
func (*InboundTrack) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *IncomingTrack) start() {
|
||||
func (t *InboundTrack) start() {
|
||||
t.inboundRTPPacketsLost = &counterdumper.Dumper{
|
||||
OnReport: func(val uint64) {
|
||||
t.log.Log(logger.Warn, "%d RTP %s lost",
|
||||
@@ -393,11 +393,11 @@ func (t *IncomingTrack) start() {
|
||||
}
|
||||
|
||||
// PacketNTP returns the packet NTP.
|
||||
func (t *IncomingTrack) PacketNTP(pkt *rtp.Packet) (time.Time, bool) {
|
||||
func (t *InboundTrack) PacketNTP(pkt *rtp.Packet) (time.Time, bool) {
|
||||
return t.rtpReceiver.PacketNTP(pkt.Timestamp)
|
||||
}
|
||||
|
||||
func (t *IncomingTrack) close() {
|
||||
func (t *InboundTrack) close() {
|
||||
if t.inboundRTPPacketsLost != nil {
|
||||
t.inboundRTPPacketsLost.Stop()
|
||||
}
|
||||
+4
-4
@@ -4,14 +4,14 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// OutgoingDataChannel is an outgoing data channel.
|
||||
type OutgoingDataChannel struct {
|
||||
// OutboundDataChannel is an outgoing data channel.
|
||||
type OutboundDataChannel struct {
|
||||
Label string
|
||||
|
||||
dataChan *webrtc.DataChannel
|
||||
}
|
||||
|
||||
func (c *OutgoingDataChannel) setup(p *PeerConnection) error {
|
||||
func (c *OutboundDataChannel) setup(p *PeerConnection) error {
|
||||
var err error
|
||||
c.dataChan, err = p.wr.CreateDataChannel(c.Label, &webrtc.DataChannelInit{
|
||||
Ordered: new(false),
|
||||
@@ -24,6 +24,6 @@ func (c *OutgoingDataChannel) setup(p *PeerConnection) error {
|
||||
}
|
||||
|
||||
// Write writes data to the channel.
|
||||
func (c *OutgoingDataChannel) Write(data []byte) {
|
||||
func (c *OutboundDataChannel) Write(data []byte) {
|
||||
c.dataChan.Send(data) //nolint:errcheck
|
||||
}
|
||||
+7
-7
@@ -10,8 +10,8 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// OutgoingTrack is an outgoing track.
|
||||
type OutgoingTrack struct {
|
||||
// OutboundTrack is an outgoing track.
|
||||
type OutboundTrack struct {
|
||||
Caps webrtc.RTPCodecCapability
|
||||
|
||||
track *webrtc.TrackLocalStaticRTP
|
||||
@@ -19,11 +19,11 @@ type OutgoingTrack struct {
|
||||
rtcpSender *rtpsender.Sender
|
||||
}
|
||||
|
||||
func (t *OutgoingTrack) isVideo() bool {
|
||||
func (t *OutboundTrack) isVideo() bool {
|
||||
return strings.Split(t.Caps.MimeType, "/")[0] == "video"
|
||||
}
|
||||
|
||||
func (t *OutgoingTrack) setup(p *PeerConnection) error {
|
||||
func (t *OutboundTrack) setup(p *PeerConnection) error {
|
||||
var trackID string
|
||||
if t.isVideo() {
|
||||
trackID = "video"
|
||||
@@ -77,19 +77,19 @@ func (t *OutgoingTrack) setup(p *PeerConnection) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *OutgoingTrack) close() {
|
||||
func (t *OutboundTrack) close() {
|
||||
if t.rtcpSender != nil {
|
||||
t.rtcpSender.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// WriteRTP writes a RTP packet.
|
||||
func (t *OutgoingTrack) WriteRTP(pkt *rtp.Packet) error {
|
||||
func (t *OutboundTrack) WriteRTP(pkt *rtp.Packet) error {
|
||||
return t.WriteRTPWithNTP(pkt, time.Now())
|
||||
}
|
||||
|
||||
// WriteRTPWithNTP writes a RTP packet.
|
||||
func (t *OutgoingTrack) WriteRTPWithNTP(pkt *rtp.Packet, ntp time.Time) error {
|
||||
func (t *OutboundTrack) WriteRTPWithNTP(pkt *rtp.Packet, ntp time.Time) error {
|
||||
// use right SSRC in packet to make rtcpSender work
|
||||
pkt.SSRC = t.ssrc
|
||||
|
||||
@@ -161,19 +161,19 @@ type PeerConnection struct {
|
||||
AdditionalHosts []string
|
||||
STUNGatherTimeout time.Duration
|
||||
Publish bool
|
||||
OutgoingTracks []*OutgoingTrack
|
||||
OutgoingDataChannels []*OutgoingDataChannel
|
||||
OutboundTracks []*OutboundTrack
|
||||
OutboundDataChannels []*OutboundDataChannel
|
||||
Log logger.Writer
|
||||
|
||||
wr *webrtc.PeerConnection
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
readingStarted atomic.Int64
|
||||
incomingTracks []*IncomingTrack
|
||||
inboundTracks []*InboundTrack
|
||||
statsInterceptor *statsInterceptor
|
||||
|
||||
newLocalCandidate chan *webrtc.ICECandidateInit
|
||||
incomingTrack chan trackRecvPair
|
||||
inboundTrack chan trackRecvPair
|
||||
|
||||
stateMutex sync.Mutex
|
||||
state webrtc.PeerConnectionState
|
||||
@@ -232,7 +232,7 @@ func (co *PeerConnection) Start() error {
|
||||
if co.Publish {
|
||||
videoSetupped := false
|
||||
audioSetupped := false
|
||||
for _, tr := range co.OutgoingTracks {
|
||||
for _, tr := range co.OutboundTracks {
|
||||
if tr.isVideo() {
|
||||
videoSetupped = true
|
||||
} else {
|
||||
@@ -243,7 +243,7 @@ func (co *PeerConnection) Start() error {
|
||||
// When audio is not used, a track has to be present anyway,
|
||||
// otherwise video is not displayed on Firefox and Chrome.
|
||||
if !audioSetupped {
|
||||
co.OutgoingTracks = append(co.OutgoingTracks, &OutgoingTrack{
|
||||
co.OutboundTracks = append(co.OutboundTracks, &OutboundTrack{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMU,
|
||||
ClockRate: 8000,
|
||||
@@ -251,7 +251,7 @@ func (co *PeerConnection) Start() error {
|
||||
})
|
||||
}
|
||||
|
||||
for i, tr := range co.OutgoingTracks {
|
||||
for i, tr := range co.OutboundTracks {
|
||||
var codecType webrtc.RTPCodecType
|
||||
if tr.isVideo() {
|
||||
codecType = webrtc.RTPCodecTypeVideo
|
||||
@@ -328,12 +328,12 @@ func (co *PeerConnection) Start() error {
|
||||
co.newLocalCandidate = make(chan *webrtc.ICECandidateInit)
|
||||
co.stateChanged = make(chan struct{})
|
||||
co.gatheringDone = make(chan struct{})
|
||||
co.incomingTrack = make(chan trackRecvPair)
|
||||
co.inboundTrack = make(chan trackRecvPair)
|
||||
co.done = make(chan struct{})
|
||||
co.chStartReading = make(chan struct{})
|
||||
|
||||
if co.Publish {
|
||||
for _, tr := range co.OutgoingTracks {
|
||||
for _, tr := range co.OutboundTracks {
|
||||
err = tr.setup(co)
|
||||
if err != nil {
|
||||
co.wr.GracefulClose() //nolint:errcheck
|
||||
@@ -341,7 +341,7 @@ func (co *PeerConnection) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
for _, dc := range co.OutgoingDataChannels {
|
||||
for _, dc := range co.OutboundDataChannels {
|
||||
err = dc.setup(co)
|
||||
if err != nil {
|
||||
co.wr.GracefulClose() //nolint:errcheck
|
||||
@@ -367,7 +367,7 @@ func (co *PeerConnection) Start() error {
|
||||
|
||||
co.wr.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
|
||||
select {
|
||||
case co.incomingTrack <- trackRecvPair{track, receiver}:
|
||||
case co.inboundTrack <- trackRecvPair{track, receiver}:
|
||||
case <-co.ctx.Done():
|
||||
}
|
||||
})
|
||||
@@ -429,10 +429,10 @@ func (co *PeerConnection) run() {
|
||||
defer close(co.done)
|
||||
|
||||
defer func() {
|
||||
for _, track := range co.incomingTracks {
|
||||
for _, track := range co.inboundTracks {
|
||||
track.close()
|
||||
}
|
||||
for _, track := range co.OutgoingTracks {
|
||||
for _, track := range co.OutboundTracks {
|
||||
track.close()
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ func (co *PeerConnection) run() {
|
||||
for {
|
||||
select {
|
||||
case <-co.chStartReading:
|
||||
for _, track := range co.incomingTracks {
|
||||
for _, track := range co.inboundTracks {
|
||||
track.start()
|
||||
}
|
||||
co.readingStarted.Store(1)
|
||||
@@ -762,8 +762,8 @@ outer:
|
||||
return nil
|
||||
}
|
||||
|
||||
// GatherIncomingTracks gathers incoming tracks.
|
||||
func (co *PeerConnection) GatherIncomingTracks(timeout time.Duration) error {
|
||||
// GatherInboundTracks gathers incoming tracks.
|
||||
func (co *PeerConnection) GatherInboundTracks(timeout time.Duration) error {
|
||||
var sdp sdp.SessionDescription
|
||||
sdp.Unmarshal([]byte(co.wr.RemoteDescription().SDP)) //nolint:errcheck
|
||||
|
||||
@@ -775,13 +775,13 @@ func (co *PeerConnection) GatherIncomingTracks(timeout time.Duration) error {
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if len(co.incomingTracks) != 0 {
|
||||
if len(co.inboundTracks) != 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("deadline exceeded while waiting tracks")
|
||||
|
||||
case pair := <-co.incomingTrack:
|
||||
t := &IncomingTrack{
|
||||
case pair := <-co.inboundTrack:
|
||||
t := &InboundTrack{
|
||||
track: pair.track,
|
||||
receiver: pair.receiver,
|
||||
rid: pair.track.RID(),
|
||||
@@ -789,9 +789,9 @@ func (co *PeerConnection) GatherIncomingTracks(timeout time.Duration) error {
|
||||
log: co.Log,
|
||||
}
|
||||
t.initialize()
|
||||
co.incomingTracks = append(co.incomingTracks, t)
|
||||
co.inboundTracks = append(co.inboundTracks, t)
|
||||
|
||||
if len(co.incomingTracks) >= maxTrackCount {
|
||||
if len(co.inboundTracks) >= maxTrackCount {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -895,9 +895,9 @@ func (co *PeerConnection) GatheringDone() <-chan struct{} {
|
||||
return co.gatheringDone
|
||||
}
|
||||
|
||||
// IncomingTracks returns incoming tracks.
|
||||
func (co *PeerConnection) IncomingTracks() []*IncomingTrack {
|
||||
return co.incomingTracks
|
||||
// InboundTracks returns incoming tracks.
|
||||
func (co *PeerConnection) InboundTracks() []*InboundTrack {
|
||||
return co.inboundTracks
|
||||
}
|
||||
|
||||
// StartReading starts reading incoming tracks.
|
||||
@@ -960,7 +960,7 @@ func (co *PeerConnection) Stats() *Stats {
|
||||
packetsLost := uint64(0)
|
||||
|
||||
if co.readingStarted.Load() == 1 {
|
||||
for _, tr := range co.incomingTracks {
|
||||
for _, tr := range co.inboundTracks {
|
||||
if recvStats := tr.rtpReceiver.Stats(); recvStats != nil {
|
||||
v += recvStats.Jitter
|
||||
n++
|
||||
@@ -970,7 +970,7 @@ func (co *PeerConnection) Stats() *Stats {
|
||||
}
|
||||
}
|
||||
|
||||
for _, tr := range co.OutgoingTracks {
|
||||
for _, tr := range co.OutboundTracks {
|
||||
if sentStats := tr.rtcpSender.Stats(); sentStats != nil {
|
||||
packetsSent += sentStats.Sent
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (nilWriter) Write(p []byte) (int, error) {
|
||||
|
||||
var webrtcNilLogger = logging.NewDefaultLeveledLoggerForScope("", 0, &nilWriter{})
|
||||
|
||||
func gatherCodecs(tracks []*IncomingTrack) []webrtc.RTPCodecParameters {
|
||||
func gatherCodecs(tracks []*InboundTrack) []webrtc.RTPCodecParameters {
|
||||
codecs := make([]webrtc.RTPCodecParameters, len(tracks))
|
||||
for i, track := range tracks {
|
||||
codecs[i] = track.Codec()
|
||||
@@ -235,7 +235,7 @@ func TestPeerConnectionConnectivity(t *testing.T) {
|
||||
ICETCPMux: tcpMux,
|
||||
ICEServers: iceServers,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*OutgoingTrack{{
|
||||
OutboundTracks: []*OutboundTrack{{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
@@ -370,10 +370,10 @@ func TestPeerConnectionRead(t *testing.T) {
|
||||
require.NoError(t, err2)
|
||||
}()
|
||||
|
||||
err = reader.GatherIncomingTracks(2 * time.Second)
|
||||
err = reader.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
codecs := gatherCodecs(reader.IncomingTracks())
|
||||
codecs := gatherCodecs(reader.InboundTracks())
|
||||
|
||||
sort.Slice(codecs, func(i, j int) bool {
|
||||
return codecs[i].PayloadType < codecs[j].PayloadType
|
||||
@@ -556,10 +556,10 @@ func TestPeerConnectionReadSimulcast(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = reader.GatherIncomingTracks(5 * time.Second)
|
||||
err = reader.GatherInboundTracks(5 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
tracks := reader.IncomingTracks()
|
||||
tracks := reader.InboundTracks()
|
||||
codecs := gatherCodecs(tracks)
|
||||
|
||||
require.Equal(t, 3, len(codecs))
|
||||
@@ -651,10 +651,10 @@ func TestPeerConnectionStripIncomingTWCC(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = reader.GatherIncomingTracks(5 * time.Second)
|
||||
err = reader.GatherInboundTracks(5 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
tracks := reader.IncomingTracks()
|
||||
tracks := reader.InboundTracks()
|
||||
require.Len(t, tracks, 1)
|
||||
|
||||
done := make(chan struct{})
|
||||
@@ -685,7 +685,7 @@ func TestPeerConnectionPublishRead(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*OutgoingTrack{
|
||||
OutboundTracks: []*OutboundTrack{
|
||||
{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
@@ -721,7 +721,7 @@ func TestPeerConnectionPublishRead(t *testing.T) {
|
||||
err = pc2.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, track := range pc2.OutgoingTracks {
|
||||
for _, track := range pc2.OutboundTracks {
|
||||
err = track.WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
@@ -736,10 +736,10 @@ func TestPeerConnectionPublishRead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err = pc1.GatherIncomingTracks(2 * time.Second)
|
||||
err = pc1.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
codecs := gatherCodecs(pc1.IncomingTracks())
|
||||
codecs := gatherCodecs(pc1.InboundTracks())
|
||||
|
||||
sort.Slice(codecs, func(i, j int) bool {
|
||||
return codecs[i].PayloadType < codecs[j].PayloadType
|
||||
@@ -784,7 +784,7 @@ func TestPeerConnectionFallbackCodecs(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*OutgoingTrack{{
|
||||
OutboundTracks: []*OutboundTrack{{
|
||||
Caps: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
@@ -860,7 +860,7 @@ func TestPeerConnectionPublishDataChannel(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingDataChannels: []*OutgoingDataChannel{
|
||||
OutboundDataChannels: []*OutboundDataChannel{
|
||||
{
|
||||
Label: "test-channel",
|
||||
},
|
||||
@@ -882,7 +882,7 @@ func TestPeerConnectionPublishDataChannel(t *testing.T) {
|
||||
|
||||
<-dataChanCreated
|
||||
|
||||
pc2.OutgoingDataChannels[0].Write([]byte("test data"))
|
||||
pc2.OutboundDataChannels[0].Write([]byte("test data"))
|
||||
|
||||
<-dataReceived
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func ToStream(
|
||||
timeDecoder := &rtptime.GlobalDecoder{}
|
||||
timeDecoder.Initialize()
|
||||
|
||||
for _, track := range pc.incomingTracks {
|
||||
for _, track := range pc.inboundTracks {
|
||||
var typ description.MediaType
|
||||
var forma format.Format
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ func TestToStream(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*OutgoingTrack{{
|
||||
OutboundTracks: []*OutboundTrack{{
|
||||
Caps: ca.webrtcCaps,
|
||||
}},
|
||||
Log: test.NilLogger,
|
||||
@@ -385,7 +385,7 @@ func TestToStream(t *testing.T) {
|
||||
err = pc2.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pc1.OutgoingTracks[0].WriteRTP(&rtp.Packet{
|
||||
err = pc1.OutboundTracks[0].WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: true,
|
||||
@@ -398,7 +398,7 @@ func TestToStream(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pc2.GatherIncomingTracks(2 * time.Second)
|
||||
err = pc2.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
var subStream *stream.SubStream
|
||||
|
||||
@@ -77,7 +77,7 @@ func offerAndCandidateToSDPFragment(
|
||||
type Client struct {
|
||||
URL *url.URL
|
||||
Publish bool
|
||||
OutgoingTracks []*webrtc.OutgoingTrack
|
||||
OutboundTracks []*webrtc.OutboundTrack
|
||||
HTTPClient *http.Client
|
||||
BearerToken string
|
||||
UDPReadBufferSize uint
|
||||
@@ -114,7 +114,7 @@ func (c *Client) Initialize(ctx context.Context) error {
|
||||
IPsFromInterfaces: true,
|
||||
Publish: c.Publish,
|
||||
STUNGatherTimeout: c.STUNGatherTimeout,
|
||||
OutgoingTracks: c.OutgoingTracks,
|
||||
OutboundTracks: c.OutboundTracks,
|
||||
Log: c.Log,
|
||||
}
|
||||
err = c.pc.Start()
|
||||
@@ -199,7 +199,7 @@ func (c *Client) initializeInner(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if !c.Publish {
|
||||
err = c.pc.GatherIncomingTracks(c.TrackGatherTimeout)
|
||||
err = c.pc.GatherInboundTracks(c.TrackGatherTimeout)
|
||||
if err != nil {
|
||||
c.deleteSession(context.Background()) //nolint:errcheck
|
||||
return err
|
||||
@@ -247,9 +247,9 @@ func (c *Client) PeerConnection() *webrtc.PeerConnection {
|
||||
return c.pc
|
||||
}
|
||||
|
||||
// IncomingTracks returns incoming tracks.
|
||||
func (c *Client) IncomingTracks() []*webrtc.IncomingTrack {
|
||||
return c.pc.IncomingTracks()
|
||||
// InboundTracks returns incoming tracks.
|
||||
func (c *Client) InboundTracks() []*webrtc.InboundTrack {
|
||||
return c.pc.InboundTracks()
|
||||
}
|
||||
|
||||
// StartReading starts reading all incoming tracks.
|
||||
|
||||
@@ -24,7 +24,7 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func gatherCodecs(tracks []*webrtc.IncomingTrack) []pwebrtc.RTPCodecParameters {
|
||||
func gatherCodecs(tracks []*webrtc.InboundTrack) []pwebrtc.RTPCodecParameters {
|
||||
codecs := make([]pwebrtc.RTPCodecParameters, len(tracks))
|
||||
for i, track := range tracks {
|
||||
codecs[i] = track.Codec()
|
||||
@@ -38,11 +38,11 @@ func TestClientRead(t *testing.T) {
|
||||
"video+audio",
|
||||
} {
|
||||
t.Run(ca, func(t *testing.T) {
|
||||
var outgoingTracks []*webrtc.OutgoingTrack
|
||||
var outboundTracks []*webrtc.OutboundTrack
|
||||
|
||||
switch ca {
|
||||
case "audio":
|
||||
outgoingTracks = []*webrtc.OutgoingTrack{{
|
||||
outboundTracks = []*webrtc.OutboundTrack{{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "audio/opus",
|
||||
ClockRate: 48000,
|
||||
@@ -51,7 +51,7 @@ func TestClientRead(t *testing.T) {
|
||||
}}
|
||||
|
||||
case "video+audio":
|
||||
outgoingTracks = []*webrtc.OutgoingTrack{
|
||||
outboundTracks = []*webrtc.OutboundTrack{
|
||||
{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "video/H264",
|
||||
@@ -72,7 +72,7 @@ func TestClientRead(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingTracks: outgoingTracks,
|
||||
OutboundTracks: outboundTracks,
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
err := pc.Start()
|
||||
@@ -116,7 +116,7 @@ func TestClientRead(t *testing.T) {
|
||||
err3 := pc.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
for _, track := range outgoingTracks {
|
||||
for _, track := range outboundTracks {
|
||||
err3 = track.WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
@@ -168,7 +168,7 @@ func TestClientRead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer cl.Close() //nolint:errcheck
|
||||
|
||||
codecs := gatherCodecs(cl.IncomingTracks())
|
||||
codecs := gatherCodecs(cl.InboundTracks())
|
||||
|
||||
switch ca {
|
||||
case "audio":
|
||||
@@ -215,12 +215,12 @@ func TestClientRead(t *testing.T) {
|
||||
}, codecs)
|
||||
}
|
||||
|
||||
recv := make([]chan struct{}, len(outgoingTracks))
|
||||
for i := range outgoingTracks {
|
||||
recv := make([]chan struct{}, len(outboundTracks))
|
||||
for i := range outboundTracks {
|
||||
recv[i] = make(chan struct{})
|
||||
}
|
||||
|
||||
for i, track := range cl.IncomingTracks() {
|
||||
for i, track := range cl.InboundTracks() {
|
||||
ci := i
|
||||
track.OnPacketRTP = func(_ *rtp.Packet) {
|
||||
close(recv[ci])
|
||||
@@ -286,10 +286,10 @@ func TestClientPublish(t *testing.T) {
|
||||
err3 := pc.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
err3 = pc.GatherIncomingTracks(2 * time.Second)
|
||||
err3 = pc.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
codecs := gatherCodecs(pc.IncomingTracks())
|
||||
codecs := gatherCodecs(pc.InboundTracks())
|
||||
|
||||
switch ca {
|
||||
case "audio":
|
||||
@@ -333,7 +333,7 @@ func TestClientPublish(t *testing.T) {
|
||||
}, codecs)
|
||||
}
|
||||
|
||||
for i, track := range pc.IncomingTracks() {
|
||||
for i, track := range pc.InboundTracks() {
|
||||
ci := i
|
||||
track.OnPacketRTP = func(_ *rtp.Packet) {
|
||||
close(recv[ci])
|
||||
@@ -370,11 +370,11 @@ func TestClientPublish(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost:9005/my/resource")
|
||||
require.NoError(t, err)
|
||||
|
||||
var outgoingTracks []*webrtc.OutgoingTrack
|
||||
var outboundTracks []*webrtc.OutboundTrack
|
||||
|
||||
switch ca {
|
||||
case "audio":
|
||||
outgoingTracks = []*webrtc.OutgoingTrack{{
|
||||
outboundTracks = []*webrtc.OutboundTrack{{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "audio/opus",
|
||||
ClockRate: 48000,
|
||||
@@ -383,7 +383,7 @@ func TestClientPublish(t *testing.T) {
|
||||
}}
|
||||
|
||||
case "video+audio":
|
||||
outgoingTracks = []*webrtc.OutgoingTrack{
|
||||
outboundTracks = []*webrtc.OutboundTrack{
|
||||
{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "video/H264",
|
||||
@@ -400,15 +400,15 @@ func TestClientPublish(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
recv = make([]chan struct{}, len(outgoingTracks))
|
||||
for i := range outgoingTracks {
|
||||
recv = make([]chan struct{}, len(outboundTracks))
|
||||
for i := range outboundTracks {
|
||||
recv[i] = make(chan struct{})
|
||||
}
|
||||
|
||||
cl := &Client{
|
||||
URL: u,
|
||||
Publish: true,
|
||||
OutgoingTracks: outgoingTracks,
|
||||
OutboundTracks: outboundTracks,
|
||||
HTTPClient: &http.Client{},
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func TestClientPublish(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer cl.Close() //nolint:errcheck
|
||||
|
||||
for _, track := range cl.OutgoingTracks {
|
||||
for _, track := range cl.OutboundTracks {
|
||||
err = track.WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
@@ -481,7 +481,7 @@ func TestClientBearerToken(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost:9005/my/resource")
|
||||
require.NoError(t, err)
|
||||
|
||||
outgoingTracks := []*webrtc.OutgoingTrack{{
|
||||
outboundTracks := []*webrtc.OutboundTrack{{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "audio/opus",
|
||||
ClockRate: 48000,
|
||||
@@ -495,7 +495,7 @@ func TestClientBearerToken(t *testing.T) {
|
||||
BearerToken: "my_secret_token",
|
||||
Log: test.NilLogger,
|
||||
Publish: true,
|
||||
OutgoingTracks: outgoingTracks,
|
||||
OutboundTracks: outboundTracks,
|
||||
}
|
||||
err = cl.Initialize(context.Background())
|
||||
require.NoError(t, err)
|
||||
@@ -549,10 +549,10 @@ func TestClientNoTrickleICE(t *testing.T) {
|
||||
err3 := pc.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
err3 = pc.GatherIncomingTracks(2 * time.Second)
|
||||
err3 = pc.GatherInboundTracks(2 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
pc.IncomingTracks()[0].OnPacketRTP = func(_ *rtp.Packet) {
|
||||
pc.InboundTracks()[0].OnPacketRTP = func(_ *rtp.Packet) {
|
||||
close(recv)
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ func TestClientNoTrickleICE(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost:9005/my/resource")
|
||||
require.NoError(t, err)
|
||||
|
||||
outgoingTracks := []*webrtc.OutgoingTrack{{
|
||||
outboundTracks := []*webrtc.OutboundTrack{{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "audio/opus",
|
||||
ClockRate: 48000,
|
||||
@@ -594,7 +594,7 @@ func TestClientNoTrickleICE(t *testing.T) {
|
||||
cl := &Client{
|
||||
URL: u,
|
||||
Publish: true,
|
||||
OutgoingTracks: outgoingTracks,
|
||||
OutboundTracks: outboundTracks,
|
||||
HTTPClient: &http.Client{},
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
@@ -602,7 +602,7 @@ func TestClientNoTrickleICE(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer cl.Close() //nolint:errcheck
|
||||
|
||||
err = outgoingTracks[0].WriteRTP(&rtp.Packet{
|
||||
err = outboundTracks[0].WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: true,
|
||||
|
||||
@@ -89,9 +89,7 @@ type httpServer struct {
|
||||
func (s *httpServer) initialize() error {
|
||||
router := gin.New()
|
||||
router.SetTrustedProxies(s.trustedProxies.ToTrustedProxies()) //nolint:errcheck
|
||||
|
||||
router.Use(s.middlewarePreflightRequests)
|
||||
|
||||
router.Use(s.onRequest)
|
||||
|
||||
var proto string
|
||||
@@ -205,16 +203,22 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool)
|
||||
return
|
||||
}
|
||||
|
||||
res := s.parent.newSession(webRTCNewSessionReq{
|
||||
res := s.parent.newSession(newSessionReq{
|
||||
pathName: pathName,
|
||||
remoteAddr: httpp.RemoteAddr(ctx),
|
||||
offer: offer,
|
||||
publish: publish,
|
||||
offer: offer,
|
||||
httpRequest: ctx.Request,
|
||||
})
|
||||
if res.err != nil {
|
||||
s.writeErrorNoLog(ctx, res.errStatusCode, res.err)
|
||||
return
|
||||
}
|
||||
|
||||
res2 := res.sx.initialRequest(initialRequestReq{})
|
||||
if res2.err != nil {
|
||||
var terr *auth.Error
|
||||
if errors.As(res.err, &terr) {
|
||||
if errors.As(res2.err, &terr) {
|
||||
if terr.AskCredentials {
|
||||
ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`)
|
||||
s.writeErrorNoLog(ctx, http.StatusUnauthorized, fmt.Errorf("authentication error"))
|
||||
@@ -230,7 +234,7 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool)
|
||||
return
|
||||
}
|
||||
|
||||
s.writeErrorNoLog(ctx, res.errStatusCode, res.err)
|
||||
s.writeErrorNoLog(ctx, res2.errStatusCode, res2.err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -252,12 +256,12 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool)
|
||||
ctx.Writer.Header()["Link"] = whip.LinkHeaderMarshal(servers)
|
||||
ctx.Header("Location", sessionLocation(publish, pathName, ctx.Request.URL.RawQuery, res.sx.secret))
|
||||
ctx.Writer.WriteHeader(http.StatusCreated)
|
||||
ctx.Writer.Write(res.answer)
|
||||
ctx.Writer.Write(res2.answer)
|
||||
|
||||
res.sx.Log(logger.Debug, "SDP answer:\n"+string(res.answer))
|
||||
res.sx.Log(logger.Debug, "SDP answer:\n"+string(res2.answer))
|
||||
}
|
||||
|
||||
func (s *httpServer) onWHIPPatch(ctx *gin.Context, pathName string, rawSecret string) {
|
||||
func (s *httpServer) onWHIPPatch(ctx *gin.Context, rawSecret string) {
|
||||
secret, err := uuid.Parse(rawSecret)
|
||||
if err != nil {
|
||||
s.writeErrorNoLog(ctx, http.StatusBadRequest, fmt.Errorf("invalid secret"))
|
||||
@@ -282,8 +286,7 @@ func (s *httpServer) onWHIPPatch(ctx *gin.Context, pathName string, rawSecret st
|
||||
return
|
||||
}
|
||||
|
||||
res := s.parent.addSessionCandidates(webRTCAddSessionCandidatesReq{
|
||||
pathName: pathName,
|
||||
res := s.parent.addSessionCandidates(addSessionCandidatesReq{
|
||||
secret: secret,
|
||||
fragment: &frag,
|
||||
})
|
||||
@@ -323,16 +326,15 @@ func (s *httpServer) onWHIPPatch(ctx *gin.Context, pathName string, rawSecret st
|
||||
})
|
||||
}
|
||||
|
||||
func (s *httpServer) onWHIPDelete(ctx *gin.Context, pathName string, rawSecret string) {
|
||||
func (s *httpServer) onWHIPDelete(ctx *gin.Context, rawSecret string) {
|
||||
secret, err := uuid.Parse(rawSecret)
|
||||
if err != nil {
|
||||
s.writeErrorNoLog(ctx, http.StatusBadRequest, fmt.Errorf("invalid secret"))
|
||||
return
|
||||
}
|
||||
|
||||
err = s.parent.deleteSession(webRTCDeleteSessionReq{
|
||||
pathName: pathName,
|
||||
secret: secret,
|
||||
err = s.parent.deleteSession(deleteSessionReq{
|
||||
secret: secret,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrSessionNotFound) {
|
||||
@@ -379,22 +381,6 @@ func (s *httpServer) middlewarePreflightRequests(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
func (s *httpServer) onRequest(ctx *gin.Context) {
|
||||
if strings.HasSuffix(ctx.Request.URL.Path, "/publisher.js") {
|
||||
ctx.Header("Cache-Control", "max-age=3600")
|
||||
ctx.Header("Content-Type", "application/javascript")
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Write(publisherJS)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasSuffix(ctx.Request.URL.Path, "/reader.js") {
|
||||
ctx.Header("Cache-Control", "max-age=3600")
|
||||
ctx.Header("Content-Type", "application/javascript")
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Write(readerJS)
|
||||
return
|
||||
}
|
||||
|
||||
// WHIP/WHEP, outside session
|
||||
if m := reWHIPWHEPNoID.FindStringSubmatch(ctx.Request.URL.Path); m != nil {
|
||||
switch ctx.Request.Method {
|
||||
@@ -417,10 +403,10 @@ func (s *httpServer) onRequest(ctx *gin.Context) {
|
||||
if m := reWHIPWHEPWithID.FindStringSubmatch(ctx.Request.URL.Path); m != nil {
|
||||
switch ctx.Request.Method {
|
||||
case http.MethodPatch:
|
||||
s.onWHIPPatch(ctx, m[1], m[3])
|
||||
s.onWHIPPatch(ctx, m[3])
|
||||
|
||||
case http.MethodDelete:
|
||||
s.onWHIPDelete(ctx, m[1], m[3])
|
||||
s.onWHIPDelete(ctx, m[3])
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -428,6 +414,18 @@ func (s *httpServer) onRequest(ctx *gin.Context) {
|
||||
// static resources
|
||||
if ctx.Request.Method == http.MethodGet {
|
||||
switch {
|
||||
case strings.HasSuffix(ctx.Request.URL.Path, "/publisher.js"):
|
||||
ctx.Header("Cache-Control", "max-age=3600")
|
||||
ctx.Header("Content-Type", "application/javascript")
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Write(publisherJS)
|
||||
|
||||
case strings.HasSuffix(ctx.Request.URL.Path, "/reader.js"):
|
||||
ctx.Header("Cache-Control", "max-age=3600")
|
||||
ctx.Header("Content-Type", "application/javascript")
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Write(readerJS)
|
||||
|
||||
case ctx.Request.URL.Path == "/favicon.ico":
|
||||
|
||||
case len(ctx.Request.URL.Path) >= 2:
|
||||
@@ -443,6 +441,5 @@ func (s *httpServer) onRequest(ctx *gin.Context) {
|
||||
s.onPage(ctx, ctx.Request.URL.Path[1:len(ctx.Request.URL.Path)-1], false)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,6 @@
|
||||
const controls = document.getElementById("controls");
|
||||
const message = document.getElementById("message");
|
||||
const publishButton = document.getElementById("publish-button");
|
||||
let publisher = null;
|
||||
|
||||
const videoForm = {
|
||||
device: document.getElementById("video-device"),
|
||||
@@ -177,6 +176,8 @@
|
||||
voice: document.getElementById("audio-voice"),
|
||||
};
|
||||
|
||||
let publisher = null;
|
||||
|
||||
const setMessage = (str) => {
|
||||
message.innerText = str;
|
||||
};
|
||||
@@ -209,6 +210,8 @@
|
||||
const videoId = videoForm.device.value;
|
||||
const audioId = audioForm.device.value;
|
||||
|
||||
let mediaPromise;
|
||||
|
||||
if (videoId !== "screen") {
|
||||
let videoOpts = false;
|
||||
|
||||
@@ -236,31 +239,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({
|
||||
video: videoOpts,
|
||||
audio: audioOpts,
|
||||
})
|
||||
.then((stream) => onStream(stream))
|
||||
.catch((err) => {
|
||||
setMessage(err.toString());
|
||||
});
|
||||
mediaPromise = navigator.mediaDevices.getUserMedia({
|
||||
video: videoOpts,
|
||||
audio: audioOpts,
|
||||
});
|
||||
} else {
|
||||
navigator.mediaDevices
|
||||
.getDisplayMedia({
|
||||
video: {
|
||||
width: { ideal: videoForm.width.value },
|
||||
height: { ideal: videoForm.height.value },
|
||||
frameRate: { ideal: videoForm.framerate.value },
|
||||
cursor: "always",
|
||||
},
|
||||
audio: true,
|
||||
})
|
||||
.then((stream) => onStream(stream))
|
||||
.catch((err) => {
|
||||
setMessage(err.toString());
|
||||
});
|
||||
mediaPromise = navigator.mediaDevices.getDisplayMedia({
|
||||
video: {
|
||||
width: { ideal: videoForm.width.value },
|
||||
height: { ideal: videoForm.height.value },
|
||||
frameRate: { ideal: videoForm.framerate.value },
|
||||
cursor: "always",
|
||||
},
|
||||
audio: true,
|
||||
});
|
||||
}
|
||||
|
||||
mediaPromise
|
||||
.then((stream) => onStream(stream))
|
||||
.catch((err) => {
|
||||
setMessage(err.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const selectHasOption = (select, option) => {
|
||||
@@ -350,29 +349,18 @@
|
||||
});
|
||||
};
|
||||
|
||||
const populateOptions = () => {
|
||||
const loadDevices = () => {
|
||||
setMessage("loading devices");
|
||||
|
||||
navigator.mediaDevices
|
||||
return navigator.mediaDevices
|
||||
.getUserMedia({ video: true, audio: true })
|
||||
.then((tempStream) => {
|
||||
return Promise.all([populateDevices(), populateCodecs()]).then(
|
||||
() => {
|
||||
// free the webcam to prevent 'NotReadableError' on Android
|
||||
tempStream.getTracks().forEach((track) => track.stop());
|
||||
|
||||
setMessage("");
|
||||
|
||||
loadValuesFromQuery();
|
||||
setupEventListeners();
|
||||
|
||||
video.style.display = "none";
|
||||
controls.style.display = "flex";
|
||||
},
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
setMessage(err.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -439,8 +427,18 @@
|
||||
return;
|
||||
}
|
||||
|
||||
publishButton.addEventListener("click", onPublish);
|
||||
populateOptions();
|
||||
loadDevices()
|
||||
.then(() => {
|
||||
loadValuesFromQuery();
|
||||
setupEventListeners();
|
||||
video.style.display = "none";
|
||||
controls.style.display = "flex";
|
||||
publishButton.addEventListener("click", onPublish);
|
||||
setMessage("");
|
||||
})
|
||||
.catch((err) => {
|
||||
setMessage(err.toString());
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
|
||||
@@ -28,35 +28,29 @@
|
||||
|
||||
/** WebRTC/WHIP publisher. */
|
||||
class MediaMTXWebRTCPublisher {
|
||||
#retryPause;
|
||||
static #RETRY_PAUSE = 2000;
|
||||
|
||||
#conf;
|
||||
#state;
|
||||
#restartTimeout;
|
||||
#pc;
|
||||
#offerData;
|
||||
#sessionUrl;
|
||||
#queuedCandidates;
|
||||
#state = "running";
|
||||
#restartTimeout = null;
|
||||
#pc = null;
|
||||
#offerData = null;
|
||||
#sessionUrl = null;
|
||||
#queuedCandidates = [];
|
||||
|
||||
/**
|
||||
* Create a MediaMTXWebRTCPublisher.
|
||||
* @param {Conf} conf - configuration.
|
||||
*/
|
||||
constructor(conf) {
|
||||
this.#retryPause = 2000;
|
||||
this.#conf = conf;
|
||||
this.#state = "running";
|
||||
this.#restartTimeout = null;
|
||||
this.#pc = null;
|
||||
this.#offerData = null;
|
||||
this.#sessionUrl = null;
|
||||
this.#queuedCandidates = [];
|
||||
this.#start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the publisher and all its resources.
|
||||
*/
|
||||
close = () => {
|
||||
close() {
|
||||
this.#state = "closed";
|
||||
|
||||
if (this.#pc !== null) {
|
||||
@@ -66,7 +60,7 @@ class MediaMTXWebRTCPublisher {
|
||||
if (this.#restartTimeout !== null) {
|
||||
clearTimeout(this.#restartTimeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static #unquoteCredential(v) {
|
||||
return JSON.parse(`"${v}"`);
|
||||
@@ -123,17 +117,16 @@ class MediaMTXWebRTCPublisher {
|
||||
candidatesByMedia[mid].push(candidate);
|
||||
}
|
||||
|
||||
let frag =
|
||||
"a=ice-ufrag:" + od.iceUfrag + "\r\n" + "a=ice-pwd:" + od.icePwd + "\r\n";
|
||||
let frag = `a=ice-ufrag:${od.iceUfrag}\r\n` + `a=ice-pwd:${od.icePwd}\r\n`;
|
||||
|
||||
let mid = 0;
|
||||
|
||||
for (const media of od.medias) {
|
||||
if (candidatesByMedia[mid] !== undefined) {
|
||||
frag += "m=" + media + "\r\n" + "a=mid:" + mid + "\r\n";
|
||||
frag += `m=${media}\r\n` + `a=mid:${mid}\r\n`;
|
||||
|
||||
for (const candidate of candidatesByMedia[mid]) {
|
||||
frag += "a=" + candidate.candidate + "\r\n";
|
||||
frag += `a=${candidate.candidate}\r\n`;
|
||||
}
|
||||
}
|
||||
mid++;
|
||||
@@ -273,6 +266,12 @@ class MediaMTXWebRTCPublisher {
|
||||
return sections.join("m=");
|
||||
}
|
||||
|
||||
#restart() {
|
||||
this.#restartTimeout = null;
|
||||
this.#state = "running";
|
||||
this.#start();
|
||||
}
|
||||
|
||||
#start() {
|
||||
this.#requestICEServers()
|
||||
.then((iceServers) => this.#setupPeerConnection(iceServers))
|
||||
@@ -303,11 +302,10 @@ class MediaMTXWebRTCPublisher {
|
||||
this.#queuedCandidates = [];
|
||||
this.#state = "restarting";
|
||||
|
||||
this.#restartTimeout = window.setTimeout(() => {
|
||||
this.#restartTimeout = null;
|
||||
this.#state = "running";
|
||||
this.#start();
|
||||
}, this.#retryPause);
|
||||
this.#restartTimeout = window.setTimeout(
|
||||
() => this.#restart(),
|
||||
MediaMTXWebRTCPublisher.#RETRY_PAUSE,
|
||||
);
|
||||
|
||||
if (this.#conf.onError !== undefined) {
|
||||
this.#conf.onError(`${err}, retrying in some seconds`);
|
||||
@@ -329,9 +327,7 @@ class MediaMTXWebRTCPublisher {
|
||||
#requestICEServers() {
|
||||
return fetch(this.#conf.url, {
|
||||
method: "OPTIONS",
|
||||
headers: {
|
||||
...this.#authHeader(),
|
||||
},
|
||||
headers: this.#authHeader(),
|
||||
}).then((res) =>
|
||||
MediaMTXWebRTCPublisher.#linkToIceServers(res.headers.get("Link")),
|
||||
);
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<script>
|
||||
const video = document.getElementById("video");
|
||||
const message = document.getElementById("message");
|
||||
|
||||
let defaultControls = false;
|
||||
let reader = null;
|
||||
|
||||
|
||||
@@ -29,14 +29,15 @@
|
||||
|
||||
/** WebRTC/WHEP reader. */
|
||||
class MediaMTXWebRTCReader {
|
||||
#retryPause;
|
||||
static #RETRY_PAUSE = 2000;
|
||||
|
||||
#conf;
|
||||
#state;
|
||||
#restartTimeout;
|
||||
#pc;
|
||||
#offerData;
|
||||
#sessionUrl;
|
||||
#queuedCandidates;
|
||||
#state = "getting_codecs";
|
||||
#restartTimeout = null;
|
||||
#pc = null;
|
||||
#offerData = null;
|
||||
#sessionUrl = null;
|
||||
#queuedCandidates = [];
|
||||
#nonAdvertisedCodecs;
|
||||
|
||||
/**
|
||||
@@ -44,14 +45,7 @@ class MediaMTXWebRTCReader {
|
||||
* @param {Conf} conf - configuration.
|
||||
*/
|
||||
constructor(conf) {
|
||||
this.#retryPause = 2000;
|
||||
this.#conf = conf;
|
||||
this.#state = "getting_codecs";
|
||||
this.#restartTimeout = null;
|
||||
this.#pc = null;
|
||||
this.#offerData = null;
|
||||
this.#sessionUrl = null;
|
||||
this.#queuedCandidates = [];
|
||||
this.#getNonAdvertisedCodecs();
|
||||
}
|
||||
|
||||
@@ -432,11 +426,10 @@ class MediaMTXWebRTCReader {
|
||||
this.#queuedCandidates = [];
|
||||
this.#state = "restarting";
|
||||
|
||||
this.#restartTimeout = window.setTimeout(() => {
|
||||
this.#restartTimeout = null;
|
||||
this.#state = "running";
|
||||
this.#start();
|
||||
}, this.#retryPause);
|
||||
this.#restartTimeout = window.setTimeout(
|
||||
() => this.#restart(),
|
||||
MediaMTXWebRTCReader.#RETRY_PAUSE,
|
||||
);
|
||||
|
||||
if (this.#conf.onError !== undefined) {
|
||||
this.#conf.onError(`${err}, retrying in some seconds`);
|
||||
@@ -450,6 +443,12 @@ class MediaMTXWebRTCReader {
|
||||
}
|
||||
}
|
||||
|
||||
#restart() {
|
||||
this.#restartTimeout = null;
|
||||
this.#state = "running";
|
||||
this.#start();
|
||||
}
|
||||
|
||||
#getNonAdvertisedCodecs() {
|
||||
Promise.all(
|
||||
[
|
||||
@@ -504,9 +503,7 @@ class MediaMTXWebRTCReader {
|
||||
#requestICEServers() {
|
||||
return fetch(this.#conf.url, {
|
||||
method: "OPTIONS",
|
||||
headers: {
|
||||
...this.#authHeader(),
|
||||
},
|
||||
headers: this.#authHeader(),
|
||||
}).then((res) =>
|
||||
MediaMTXWebRTCReader.#linkToIceServers(res.headers.get("Link")),
|
||||
);
|
||||
|
||||
@@ -133,43 +133,40 @@ type serverAPISessionsKickReq struct {
|
||||
res chan serverAPISessionsKickRes
|
||||
}
|
||||
|
||||
type webRTCNewSessionRes struct {
|
||||
type newSessionRes struct {
|
||||
sx *session
|
||||
answer []byte
|
||||
errStatusCode int
|
||||
err error
|
||||
}
|
||||
|
||||
type webRTCNewSessionReq struct {
|
||||
type newSessionReq struct {
|
||||
pathName string
|
||||
remoteAddr string
|
||||
offer []byte
|
||||
publish bool
|
||||
httpRequest *http.Request
|
||||
res chan webRTCNewSessionRes
|
||||
res chan newSessionRes
|
||||
}
|
||||
|
||||
type webRTCAddSessionCandidatesRes struct {
|
||||
type addSessionCandidatesRes struct {
|
||||
sx *session
|
||||
answer *whip.SDPFragment
|
||||
err error
|
||||
}
|
||||
|
||||
type webRTCAddSessionCandidatesReq struct {
|
||||
pathName string
|
||||
type addSessionCandidatesReq struct {
|
||||
secret uuid.UUID
|
||||
fragment *whip.SDPFragment
|
||||
res chan webRTCAddSessionCandidatesRes
|
||||
res chan addSessionCandidatesRes
|
||||
}
|
||||
|
||||
type webRTCDeleteSessionRes struct {
|
||||
type deleteSessionRes struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type webRTCDeleteSessionReq struct {
|
||||
pathName string
|
||||
secret uuid.UUID
|
||||
res chan webRTCDeleteSessionRes
|
||||
type deleteSessionReq struct {
|
||||
secret uuid.UUID
|
||||
res chan deleteSessionRes
|
||||
}
|
||||
|
||||
type serverMetrics interface {
|
||||
@@ -223,10 +220,10 @@ type Server struct {
|
||||
sessionsBySecret map[uuid.UUID]*session
|
||||
|
||||
// in
|
||||
chNewSession chan webRTCNewSessionReq
|
||||
chNewSession chan newSessionReq
|
||||
chCloseSession chan *session
|
||||
chAddSessionCandidates chan webRTCAddSessionCandidatesReq
|
||||
chDeleteSession chan webRTCDeleteSessionReq
|
||||
chAddSessionCandidates chan addSessionCandidatesReq
|
||||
chDeleteSession chan deleteSessionReq
|
||||
chAPISessionsList chan serverAPISessionsListReq
|
||||
chAPISessionsGet chan serverAPISessionsGetReq
|
||||
chAPIConnsKick chan serverAPISessionsKickReq
|
||||
@@ -243,10 +240,10 @@ func (s *Server) Initialize() error {
|
||||
s.ctxCancel = ctxCancel
|
||||
s.sessions = make(map[*session]struct{})
|
||||
s.sessionsBySecret = make(map[uuid.UUID]*session)
|
||||
s.chNewSession = make(chan webRTCNewSessionReq)
|
||||
s.chNewSession = make(chan newSessionReq)
|
||||
s.chCloseSession = make(chan *session)
|
||||
s.chAddSessionCandidates = make(chan webRTCAddSessionCandidatesReq)
|
||||
s.chDeleteSession = make(chan webRTCDeleteSessionReq)
|
||||
s.chAddSessionCandidates = make(chan addSessionCandidatesReq)
|
||||
s.chDeleteSession = make(chan deleteSessionReq)
|
||||
s.chAPISessionsList = make(chan serverAPISessionsListReq)
|
||||
s.chAPISessionsGet = make(chan serverAPISessionsGetReq)
|
||||
s.chAPIConnsKick = make(chan serverAPISessionsKickReq)
|
||||
@@ -309,7 +306,7 @@ func (s *Server) Initialize() error {
|
||||
}
|
||||
}
|
||||
|
||||
str := "listener opened on " + s.Address
|
||||
str := "started with listeners on " + s.Address
|
||||
if !s.Encryption {
|
||||
str += " (TCP/HTTP)"
|
||||
} else {
|
||||
@@ -339,7 +336,7 @@ func (s *Server) Log(level logger.Level, format string, args ...any) {
|
||||
|
||||
// Close closes the server.
|
||||
func (s *Server) Close() {
|
||||
s.Log(logger.Info, "listener is closing")
|
||||
s.Log(logger.Info, "closing")
|
||||
|
||||
if !interfaceIsEmpty(s.Metrics) {
|
||||
s.Metrics.SetWebRTCServer(nil)
|
||||
@@ -369,7 +366,11 @@ outer:
|
||||
stunGatherTimeout: s.STUNGatherTimeout,
|
||||
handshakeTimeout: s.HandshakeTimeout,
|
||||
trackGatherTimeout: s.TrackGatherTimeout,
|
||||
req: req,
|
||||
pathName: req.pathName,
|
||||
remoteAddr: req.remoteAddr,
|
||||
offer: req.offer,
|
||||
publish: req.publish,
|
||||
httpRequest: req.httpRequest,
|
||||
wg: &wg,
|
||||
externalCmdPool: s.ExternalCmdPool,
|
||||
pathManager: s.PathManager,
|
||||
@@ -378,7 +379,7 @@ outer:
|
||||
sx.initialize()
|
||||
s.sessions[sx] = struct{}{}
|
||||
s.sessionsBySecret[sx.secret] = sx
|
||||
req.res <- webRTCNewSessionRes{sx: sx}
|
||||
req.res <- newSessionRes{sx: sx}
|
||||
|
||||
case sx := <-s.chCloseSession:
|
||||
delete(s.sessions, sx)
|
||||
@@ -386,17 +387,17 @@ outer:
|
||||
|
||||
case req := <-s.chAddSessionCandidates:
|
||||
sx, ok := s.sessionsBySecret[req.secret]
|
||||
if !ok || sx.req.pathName != req.pathName {
|
||||
req.res <- webRTCAddSessionCandidatesRes{err: ErrSessionNotFound}
|
||||
if !ok {
|
||||
req.res <- addSessionCandidatesRes{err: ErrSessionNotFound}
|
||||
continue
|
||||
}
|
||||
|
||||
req.res <- webRTCAddSessionCandidatesRes{sx: sx}
|
||||
req.res <- addSessionCandidatesRes{sx: sx}
|
||||
|
||||
case req := <-s.chDeleteSession:
|
||||
sx, ok := s.sessionsBySecret[req.secret]
|
||||
if !ok || sx.req.pathName != req.pathName {
|
||||
req.res <- webRTCDeleteSessionRes{err: ErrSessionNotFound}
|
||||
if !ok {
|
||||
req.res <- deleteSessionRes{err: ErrSessionNotFound}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -404,7 +405,7 @@ outer:
|
||||
delete(s.sessionsBySecret, sx.secret)
|
||||
sx.Close()
|
||||
|
||||
req.res <- webRTCDeleteSessionRes{}
|
||||
req.res <- deleteSessionRes{}
|
||||
|
||||
case req := <-s.chAPISessionsList:
|
||||
data := &defs.APIWebRTCSessionList{
|
||||
@@ -505,17 +506,15 @@ func (s *Server) generateICEServers(clientConfig bool) ([]pwebrtc.ICEServer, err
|
||||
}
|
||||
|
||||
// newSession is called by webRTCHTTPServer.
|
||||
func (s *Server) newSession(req webRTCNewSessionReq) webRTCNewSessionRes {
|
||||
req.res = make(chan webRTCNewSessionRes)
|
||||
func (s *Server) newSession(req newSessionReq) newSessionRes {
|
||||
req.res = make(chan newSessionRes)
|
||||
|
||||
select {
|
||||
case s.chNewSession <- req:
|
||||
res := <-req.res
|
||||
|
||||
return res.sx.new(req)
|
||||
return <-req.res
|
||||
|
||||
case <-s.ctx.Done():
|
||||
return webRTCNewSessionRes{
|
||||
return newSessionRes{
|
||||
errStatusCode: http.StatusInternalServerError,
|
||||
err: fmt.Errorf("terminated"),
|
||||
}
|
||||
@@ -532,9 +531,9 @@ func (s *Server) closeSession(sx *session) {
|
||||
|
||||
// addSessionCandidates is called by webRTCHTTPServer.
|
||||
func (s *Server) addSessionCandidates(
|
||||
req webRTCAddSessionCandidatesReq,
|
||||
) webRTCAddSessionCandidatesRes {
|
||||
req.res = make(chan webRTCAddSessionCandidatesRes)
|
||||
req addSessionCandidatesReq,
|
||||
) addSessionCandidatesRes {
|
||||
req.res = make(chan addSessionCandidatesRes)
|
||||
select {
|
||||
case s.chAddSessionCandidates <- req:
|
||||
res1 := <-req.res
|
||||
@@ -545,13 +544,13 @@ func (s *Server) addSessionCandidates(
|
||||
return res1.sx.addCandidates(req)
|
||||
|
||||
case <-s.ctx.Done():
|
||||
return webRTCAddSessionCandidatesRes{err: fmt.Errorf("terminated")}
|
||||
return addSessionCandidatesRes{err: fmt.Errorf("terminated")}
|
||||
}
|
||||
}
|
||||
|
||||
// deleteSession is called by webRTCHTTPServer.
|
||||
func (s *Server) deleteSession(req webRTCDeleteSessionReq) error {
|
||||
req.res = make(chan webRTCDeleteSessionRes)
|
||||
func (s *Server) deleteSession(req deleteSessionReq) error {
|
||||
req.res = make(chan deleteSessionRes)
|
||||
select {
|
||||
case s.chDeleteSession <- req:
|
||||
res := <-req.res
|
||||
|
||||
@@ -422,7 +422,7 @@ func TestServerPublish(t *testing.T) {
|
||||
su, err := url.Parse("http://myuser:mypass@localhost:8886/teststream/whip?param=value")
|
||||
require.NoError(t, err)
|
||||
|
||||
track := &webrtc.OutgoingTrack{
|
||||
track := &webrtc.OutboundTrack{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: pwebrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
@@ -434,7 +434,7 @@ func TestServerPublish(t *testing.T) {
|
||||
HTTPClient: hc,
|
||||
URL: su,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*webrtc.OutgoingTrack{track},
|
||||
OutboundTracks: []*webrtc.OutboundTrack{track},
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
|
||||
@@ -737,7 +737,7 @@ func TestServerRead(t *testing.T) {
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
wc.IncomingTracks()[0].OnPacketRTP = func(pkt *rtp.Packet) {
|
||||
wc.InboundTracks()[0].OnPacketRTP = func(pkt *rtp.Packet) {
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
@@ -990,7 +990,7 @@ func TestServerICERestart(t *testing.T) {
|
||||
su, err := url.Parse("http://localhost:8886/teststream/whip")
|
||||
require.NoError(t, err)
|
||||
|
||||
track := &webrtc.OutgoingTrack{
|
||||
track := &webrtc.OutboundTrack{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: pwebrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
@@ -1002,7 +1002,7 @@ func TestServerICERestart(t *testing.T) {
|
||||
HTTPClient: hc,
|
||||
URL: su,
|
||||
Publish: true,
|
||||
OutgoingTracks: []*webrtc.OutgoingTrack{track},
|
||||
OutboundTracks: []*webrtc.OutboundTrack{track},
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,16 @@ func fullAnswerToSDPFragment(answerSDP string) (*whip.SDPFragment, error) {
|
||||
return frag, nil
|
||||
}
|
||||
|
||||
type initialRequestRes struct {
|
||||
answer []byte
|
||||
err error
|
||||
errStatusCode int
|
||||
}
|
||||
|
||||
type initialRequestReq struct {
|
||||
res chan initialRequestRes
|
||||
}
|
||||
|
||||
type sessionParent interface {
|
||||
closeSession(sx *session)
|
||||
generateICEServers(clientConfig bool) ([]pwebrtc.ICEServer, error)
|
||||
@@ -235,7 +245,11 @@ type session struct {
|
||||
stunGatherTimeout conf.Duration
|
||||
handshakeTimeout conf.Duration
|
||||
trackGatherTimeout conf.Duration
|
||||
req webRTCNewSessionReq
|
||||
pathName string
|
||||
remoteAddr string
|
||||
offer []byte
|
||||
publish bool
|
||||
httpRequest *http.Request
|
||||
wg *sync.WaitGroup
|
||||
externalCmdPool *externalcmd.Pool
|
||||
pathManager serverPathManager
|
||||
@@ -251,22 +265,19 @@ type session struct {
|
||||
pc *webrtc.PeerConnection
|
||||
user string
|
||||
|
||||
chNew chan webRTCNewSessionReq
|
||||
chAddCandidates chan webRTCAddSessionCandidatesReq
|
||||
chInitialRequest chan initialRequestReq
|
||||
chAddCandidates chan addSessionCandidatesReq
|
||||
}
|
||||
|
||||
func (s *session) initialize() {
|
||||
ctx, ctxCancel := context.WithCancel(s.parentCtx)
|
||||
|
||||
s.ctx = ctx
|
||||
s.ctxCancel = ctxCancel
|
||||
s.ctx, s.ctxCancel = context.WithCancel(s.parentCtx)
|
||||
s.created = time.Now()
|
||||
s.uuid = uuid.New()
|
||||
s.secret = uuid.New()
|
||||
s.chNew = make(chan webRTCNewSessionReq)
|
||||
s.chAddCandidates = make(chan webRTCAddSessionCandidatesReq)
|
||||
s.chInitialRequest = make(chan initialRequestReq)
|
||||
s.chAddCandidates = make(chan addSessionCandidatesReq)
|
||||
|
||||
s.Log(logger.Info, "created by %s", s.req.remoteAddr)
|
||||
s.Log(logger.Info, "created by %s", s.remoteAddr)
|
||||
|
||||
s.wg.Add(1)
|
||||
|
||||
@@ -296,16 +307,17 @@ func (s *session) run() {
|
||||
}
|
||||
|
||||
func (s *session) runInner() error {
|
||||
var req initialRequestReq
|
||||
select {
|
||||
case <-s.chNew:
|
||||
case req = <-s.chInitialRequest:
|
||||
case <-s.ctx.Done():
|
||||
return fmt.Errorf("terminated")
|
||||
}
|
||||
|
||||
errStatusCode, err := s.runInner2()
|
||||
errStatusCode, err := s.runInner2(&req)
|
||||
|
||||
if errStatusCode != 0 {
|
||||
s.req.res <- webRTCNewSessionRes{
|
||||
req.res <- initialRequestRes{
|
||||
errStatusCode: errStatusCode,
|
||||
err: err,
|
||||
}
|
||||
@@ -314,24 +326,24 @@ func (s *session) runInner() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *session) runInner2() (int, error) {
|
||||
if s.req.publish {
|
||||
return s.runPublish()
|
||||
func (s *session) runInner2(req *initialRequestReq) (int, error) {
|
||||
if s.publish {
|
||||
return s.runPublish(req)
|
||||
}
|
||||
return s.runRead()
|
||||
return s.runRead(req)
|
||||
}
|
||||
|
||||
func (s *session) runPublish() (int, error) {
|
||||
ip, _, _ := net.SplitHostPort(s.req.remoteAddr)
|
||||
func (s *session) runPublish(req *initialRequestReq) (int, error) {
|
||||
ip, _, _ := net.SplitHostPort(s.remoteAddr)
|
||||
|
||||
res1, err := s.pathManager.FindPathConf(defs.PathFindPathConfReq{
|
||||
AccessRequest: defs.PathAccessRequest{
|
||||
Name: s.req.pathName,
|
||||
Query: s.req.httpRequest.URL.RawQuery,
|
||||
Name: s.pathName,
|
||||
Query: s.httpRequest.URL.RawQuery,
|
||||
Publish: true,
|
||||
Proto: auth.ProtocolWebRTC,
|
||||
ID: &s.uuid,
|
||||
Credentials: httpp.Credentials(s.req.httpRequest),
|
||||
Credentials: httpp.Credentials(s.httpRequest),
|
||||
IP: net.ParseIP(ip),
|
||||
},
|
||||
})
|
||||
@@ -380,7 +392,7 @@ func (s *session) runPublish() (int, error) {
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
offer := whipOffer(s.req.offer)
|
||||
offer := whipOffer(s.offer)
|
||||
|
||||
var sdp sdp.SessionDescription
|
||||
err = sdp.Unmarshal([]byte(offer.SDP))
|
||||
@@ -403,9 +415,11 @@ func (s *session) runPublish() (int, error) {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
s.writeAnswer(answer)
|
||||
req.res <- initialRequestRes{
|
||||
answer: []byte(answer.SDP),
|
||||
}
|
||||
|
||||
go s.readRemoteCandidates(pc)
|
||||
go s.readRemoteCandidates(s.offer, pc)
|
||||
|
||||
err = pc.WaitUntilConnected(time.Duration(s.handshakeTimeout))
|
||||
if err != nil {
|
||||
@@ -416,7 +430,7 @@ func (s *session) runPublish() (int, error) {
|
||||
s.pc = pc
|
||||
s.mutex.Unlock()
|
||||
|
||||
err = pc.GatherIncomingTracks(time.Duration(s.trackGatherTimeout))
|
||||
err = pc.GatherInboundTracks(time.Duration(s.trackGatherTimeout))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -435,8 +449,8 @@ func (s *session) runPublish() (int, error) {
|
||||
ReplaceNTP: !res1.Conf.UseAbsoluteTimestamp,
|
||||
ConfToCompare: res1.Conf,
|
||||
AccessRequest: defs.PathAccessRequest{
|
||||
Name: s.req.pathName,
|
||||
Query: s.req.httpRequest.URL.RawQuery,
|
||||
Name: s.pathName,
|
||||
Query: s.httpRequest.URL.RawQuery,
|
||||
Publish: true,
|
||||
SkipAuth: true,
|
||||
},
|
||||
@@ -460,17 +474,17 @@ func (s *session) runPublish() (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *session) runRead() (int, error) {
|
||||
ip, _, _ := net.SplitHostPort(s.req.remoteAddr)
|
||||
func (s *session) runRead(req *initialRequestReq) (int, error) {
|
||||
ip, _, _ := net.SplitHostPort(s.remoteAddr)
|
||||
|
||||
res, err := s.pathManager.AddReader(defs.PathAddReaderReq{
|
||||
Author: s,
|
||||
AccessRequest: defs.PathAccessRequest{
|
||||
Name: s.req.pathName,
|
||||
Query: s.req.httpRequest.URL.RawQuery,
|
||||
Name: s.pathName,
|
||||
Query: s.httpRequest.URL.RawQuery,
|
||||
Proto: auth.ProtocolWebRTC,
|
||||
ID: &s.uuid,
|
||||
Credentials: httpp.Credentials(s.req.httpRequest),
|
||||
Credentials: httpp.Credentials(s.httpRequest),
|
||||
IP: net.ParseIP(ip),
|
||||
},
|
||||
})
|
||||
@@ -534,16 +548,18 @@ func (s *session) runRead() (int, error) {
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
offer := whipOffer(s.req.offer)
|
||||
offer := whipOffer(s.offer)
|
||||
|
||||
answer, err := pc.CreateFullAnswer(offer, false)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
s.writeAnswer(answer)
|
||||
req.res <- initialRequestRes{
|
||||
answer: []byte(answer.SDP),
|
||||
}
|
||||
|
||||
go s.readRemoteCandidates(pc)
|
||||
go s.readRemoteCandidates(s.offer, pc)
|
||||
|
||||
err = pc.WaitUntilConnected(time.Duration(s.handshakeTimeout))
|
||||
if err != nil {
|
||||
@@ -563,7 +579,7 @@ func (s *session) runRead() (int, error) {
|
||||
Conf: res.Path.SafeConf(),
|
||||
ExternalCmdEnv: res.Path.ExternalCmdEnv(),
|
||||
Reader: *s.APIReaderDescribe(),
|
||||
Query: s.req.httpRequest.URL.RawQuery,
|
||||
Query: s.httpRequest.URL.RawQuery,
|
||||
})
|
||||
defer onUnreadHook()
|
||||
|
||||
@@ -586,15 +602,8 @@ func (s *session) runRead() (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *session) writeAnswer(answer *pwebrtc.SessionDescription) {
|
||||
s.req.res <- webRTCNewSessionRes{
|
||||
sx: s,
|
||||
answer: []byte(answer.SDP),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *session) readRemoteCandidates(pc *webrtc.PeerConnection) {
|
||||
remoteUfrag := parseOfferUfrag(s.req.offer)
|
||||
func (s *session) readRemoteCandidates(offer []byte, pc *webrtc.PeerConnection) {
|
||||
remoteUfrag := parseOfferUfrag(offer)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -604,18 +613,18 @@ func (s *session) readRemoteCandidates(pc *webrtc.PeerConnection) {
|
||||
|
||||
candidates, err := sdpFragmentToCandidates(req.fragment)
|
||||
if err != nil {
|
||||
req.res <- webRTCAddSessionCandidatesRes{err: err}
|
||||
req.res <- addSessionCandidatesRes{err: err}
|
||||
continue
|
||||
}
|
||||
|
||||
// ICE restart: client sent new credentials
|
||||
var answer *pwebrtc.SessionDescription
|
||||
if ufrag != "" && ufrag != remoteUfrag {
|
||||
sdp := replaceICECredentials(s.req.offer, ufrag, pwd)
|
||||
sdp := replaceICECredentials(offer, ufrag, pwd)
|
||||
|
||||
answer, err = pc.CreateFullAnswer(whipOffer(sdp), true)
|
||||
if err != nil {
|
||||
req.res <- webRTCAddSessionCandidatesRes{err: err}
|
||||
req.res <- addSessionCandidatesRes{err: err}
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -628,7 +637,7 @@ func (s *session) readRemoteCandidates(pc *webrtc.PeerConnection) {
|
||||
}
|
||||
}
|
||||
if addErr != nil {
|
||||
req.res <- webRTCAddSessionCandidatesRes{err: addErr}
|
||||
req.res <- addSessionCandidatesRes{err: addErr}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -636,14 +645,14 @@ func (s *session) readRemoteCandidates(pc *webrtc.PeerConnection) {
|
||||
var frag *whip.SDPFragment
|
||||
frag, err = fullAnswerToSDPFragment(answer.SDP)
|
||||
if err != nil {
|
||||
req.res <- webRTCAddSessionCandidatesRes{err: err}
|
||||
req.res <- addSessionCandidatesRes{err: err}
|
||||
continue
|
||||
}
|
||||
|
||||
remoteUfrag = ufrag
|
||||
req.res <- webRTCAddSessionCandidatesRes{answer: frag}
|
||||
req.res <- addSessionCandidatesRes{answer: frag}
|
||||
} else {
|
||||
req.res <- webRTCAddSessionCandidatesRes{}
|
||||
req.res <- addSessionCandidatesRes{}
|
||||
}
|
||||
|
||||
case <-s.ctx.Done():
|
||||
@@ -652,27 +661,28 @@ func (s *session) readRemoteCandidates(pc *webrtc.PeerConnection) {
|
||||
}
|
||||
}
|
||||
|
||||
// new is called by webRTCHTTPServer through Server.
|
||||
func (s *session) new(req webRTCNewSessionReq) webRTCNewSessionRes {
|
||||
func (s *session) initialRequest(req initialRequestReq) initialRequestRes {
|
||||
req.res = make(chan initialRequestRes)
|
||||
|
||||
select {
|
||||
case s.chNew <- req:
|
||||
case s.chInitialRequest <- req:
|
||||
return <-req.res
|
||||
|
||||
case <-s.ctx.Done():
|
||||
return webRTCNewSessionRes{err: fmt.Errorf("terminated"), errStatusCode: http.StatusInternalServerError}
|
||||
return initialRequestRes{err: fmt.Errorf("terminated"), errStatusCode: http.StatusInternalServerError}
|
||||
}
|
||||
}
|
||||
|
||||
// addCandidates is called by webRTCHTTPServer through Server.
|
||||
func (s *session) addCandidates(
|
||||
req webRTCAddSessionCandidatesReq,
|
||||
) webRTCAddSessionCandidatesRes {
|
||||
req addSessionCandidatesReq,
|
||||
) addSessionCandidatesRes {
|
||||
select {
|
||||
case s.chAddCandidates <- req:
|
||||
return <-req.res
|
||||
|
||||
case <-s.ctx.Done():
|
||||
return webRTCAddSessionCandidatesRes{err: fmt.Errorf("terminated")}
|
||||
return addSessionCandidatesRes{err: fmt.Errorf("terminated")}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,18 +741,18 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
|
||||
return &defs.APIWebRTCSession{
|
||||
ID: s.uuid,
|
||||
Created: s.created,
|
||||
RemoteAddr: s.req.remoteAddr,
|
||||
RemoteAddr: s.remoteAddr,
|
||||
PeerConnectionEstablished: peerConnectionEstablished,
|
||||
LocalCandidate: localCandidate,
|
||||
RemoteCandidate: remoteCandidate,
|
||||
State: func() defs.APIWebRTCSessionState {
|
||||
if s.req.publish {
|
||||
if s.publish {
|
||||
return defs.APIWebRTCSessionStatePublish
|
||||
}
|
||||
return defs.APIWebRTCSessionStateRead
|
||||
}(),
|
||||
Path: s.req.pathName,
|
||||
Query: s.req.httpRequest.URL.RawQuery,
|
||||
Path: s.pathName,
|
||||
Query: s.httpRequest.URL.RawQuery,
|
||||
User: s.user,
|
||||
InboundBytes: bytesReceived,
|
||||
InboundRTPPackets: rtpPacketsReceived,
|
||||
|
||||
@@ -26,7 +26,7 @@ func whipOffer(body []byte) *pwebrtc.SessionDescription {
|
||||
}
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
outgoingTracks := []*webrtc.OutgoingTrack{{
|
||||
outboundTracks := []*webrtc.OutboundTrack{{
|
||||
Caps: pwebrtc.RTPCodecCapability{
|
||||
MimeType: "audio/opus",
|
||||
ClockRate: 48000,
|
||||
@@ -39,7 +39,7 @@ func TestSource(t *testing.T) {
|
||||
LocalRandomUDP: true,
|
||||
IPsFromInterfaces: true,
|
||||
Publish: true,
|
||||
OutgoingTracks: outgoingTracks,
|
||||
OutboundTracks: outboundTracks,
|
||||
Log: test.NilLogger,
|
||||
}
|
||||
err := pc.Start()
|
||||
@@ -81,7 +81,7 @@ func TestSource(t *testing.T) {
|
||||
err3 := pc.WaitUntilConnected(10 * time.Second)
|
||||
require.NoError(t, err3)
|
||||
|
||||
err3 = outgoingTracks[0].WriteRTP(&rtp.Packet{
|
||||
err3 = outboundTracks[0].WriteRTP(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: true,
|
||||
|
||||
Reference in New Issue
Block a user