Files
rtsp-simple-server/internal/protocols/webrtc/outgoing_track.go
2025-09-16 13:10:34 +02:00

115 lines
2.5 KiB
Go

package webrtc
import (
"strings"
"sync/atomic"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/rtpsender"
"github.com/pion/rtcp"
"github.com/pion/rtp"
"github.com/pion/webrtc/v4"
)
var multichannelOpusSDP = map[int]string{
3: "channel_mapping=0,2,1;num_streams=2;coupled_streams=1",
4: "channel_mapping=0,1,2,3;num_streams=2;coupled_streams=2",
5: "channel_mapping=0,4,1,2,3;num_streams=3;coupled_streams=2",
6: "channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2",
7: "channel_mapping=0,4,1,2,3,5,6;num_streams=4;coupled_streams=4",
8: "channel_mapping=0,6,1,4,5,2,3,7;num_streams=5;coupled_streams=4",
}
// OutgoingTrack is a WebRTC outgoing track
type OutgoingTrack struct {
Caps webrtc.RTPCodecCapability
track *webrtc.TrackLocalStaticRTP
ssrc uint32
rtcpSender *rtpsender.Sender
rtpPacketsSent *uint64
}
func (t *OutgoingTrack) isVideo() bool {
return strings.Split(t.Caps.MimeType, "/")[0] == "video"
}
func (t *OutgoingTrack) setup(p *PeerConnection) error {
var trackID string
if t.isVideo() {
trackID = "video"
} else {
trackID = "audio"
}
var err error
t.track, err = webrtc.NewTrackLocalStaticRTP(
t.Caps,
trackID,
webrtcStreamID,
)
if err != nil {
return err
}
sender, err := p.wr.AddTrack(t.track)
if err != nil {
return err
}
t.ssrc = uint32(sender.GetParameters().Encodings[0].SSRC)
t.rtcpSender = &rtpsender.Sender{
ClockRate: int(t.track.Codec().ClockRate),
Period: 1 * time.Second,
TimeNow: time.Now,
WritePacketRTCP: func(pkt rtcp.Packet) {
p.wr.WriteRTCP([]rtcp.Packet{pkt}) //nolint:errcheck
},
}
t.rtcpSender.Initialize()
t.rtpPacketsSent = p.rtpPacketsSent
// incoming RTCP packets must always be read to make interceptors work
go func() {
buf := make([]byte, 1500)
for {
n, _, err2 := sender.Read(buf)
if err2 != nil {
return
}
_, err2 = rtcp.Unmarshal(buf[:n])
if err2 != nil {
panic(err2)
}
}
}()
return nil
}
func (t *OutgoingTrack) close() {
if t.rtcpSender != nil {
t.rtcpSender.Close()
}
}
// WriteRTP writes a RTP packet.
func (t *OutgoingTrack) 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 {
// use right SSRC in packet to make rtcpSender work
pkt.SSRC = t.ssrc
t.rtcpSender.ProcessPacket(pkt, ntp, true)
atomic.AddUint64(t.rtpPacketsSent, 1)
return t.track.WriteRTP(pkt)
}