mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-09-27 03:56:15 +08:00
319 lines
6.9 KiB
Go
319 lines
6.9 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
|
"github.com/bluenviron/gortsplib/v4/pkg/rtpreorderer"
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/webrtc/v3"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/logger"
|
|
)
|
|
|
|
const (
|
|
keyFrameInterval = 2 * time.Second
|
|
mimeTypeMultiopus = "audio/multiopus"
|
|
mimeTypeL16 = "audio/L16"
|
|
)
|
|
|
|
var incomingVideoCodecs = []webrtc.RTPCodecParameters{
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeAV1,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "profile=1",
|
|
},
|
|
PayloadType: 96,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeAV1,
|
|
ClockRate: 90000,
|
|
},
|
|
PayloadType: 97,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeVP9,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "profile-id=3",
|
|
},
|
|
PayloadType: 98,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeVP9,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "profile-id=2",
|
|
},
|
|
PayloadType: 99,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeVP9,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "profile-id=1",
|
|
},
|
|
PayloadType: 100,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeVP9,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "profile-id=0",
|
|
},
|
|
PayloadType: 101,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeVP8,
|
|
ClockRate: 90000,
|
|
},
|
|
PayloadType: 102,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH265,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-id=93;profile-id=2;tier-flag=0;tx-mode=SRST",
|
|
},
|
|
PayloadType: 103,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH265,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-id=93;profile-id=1;tier-flag=0;tx-mode=SRST",
|
|
},
|
|
PayloadType: 104,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
|
},
|
|
PayloadType: 105,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
|
},
|
|
PayloadType: 106,
|
|
},
|
|
}
|
|
|
|
var incomingAudioCodecs = []webrtc.RTPCodecParameters{
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 3,
|
|
SDPFmtpLine: "channel_mapping=0,2,1;num_streams=2;coupled_streams=1",
|
|
},
|
|
PayloadType: 112,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 4,
|
|
SDPFmtpLine: "channel_mapping=0,1,2,3;num_streams=2;coupled_streams=2",
|
|
},
|
|
PayloadType: 113,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 5,
|
|
SDPFmtpLine: "channel_mapping=0,4,1,2,3;num_streams=3;coupled_streams=2",
|
|
},
|
|
PayloadType: 114,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 6,
|
|
SDPFmtpLine: "channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2",
|
|
},
|
|
PayloadType: 115,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 7,
|
|
SDPFmtpLine: "channel_mapping=0,4,1,2,3,5,6;num_streams=4;coupled_streams=4",
|
|
},
|
|
PayloadType: 116,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeMultiopus,
|
|
ClockRate: 48000,
|
|
Channels: 8,
|
|
SDPFmtpLine: "channel_mapping=0,6,1,4,5,2,3,7;num_streams=5;coupled_streams=4",
|
|
},
|
|
PayloadType: 117,
|
|
},
|
|
{
|
|
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,
|
|
Channels: 2,
|
|
},
|
|
PayloadType: 118,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypePCMA,
|
|
ClockRate: 8000,
|
|
Channels: 2,
|
|
},
|
|
PayloadType: 119,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypePCMU,
|
|
ClockRate: 8000,
|
|
},
|
|
PayloadType: 0,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypePCMA,
|
|
ClockRate: 8000,
|
|
},
|
|
PayloadType: 8,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeL16,
|
|
ClockRate: 8000,
|
|
Channels: 2,
|
|
},
|
|
PayloadType: 120,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeL16,
|
|
ClockRate: 16000,
|
|
Channels: 2,
|
|
},
|
|
PayloadType: 121,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: mimeTypeL16,
|
|
ClockRate: 48000,
|
|
Channels: 2,
|
|
},
|
|
PayloadType: 122,
|
|
},
|
|
}
|
|
|
|
// IncomingTrack is an incoming track.
|
|
type IncomingTrack struct {
|
|
OnPacketRTP func(*rtp.Packet)
|
|
|
|
track *webrtc.TrackRemote
|
|
receiver *webrtc.RTPReceiver
|
|
writeRTCP func([]rtcp.Packet) error
|
|
log logger.Writer
|
|
}
|
|
|
|
func (t *IncomingTrack) initialize() {
|
|
t.OnPacketRTP = func(*rtp.Packet) {}
|
|
}
|
|
|
|
// ClockRate returns the clock rate. Needed by rtptime.GlobalDecoder
|
|
func (t *IncomingTrack) ClockRate() int {
|
|
return int(t.track.Codec().ClockRate)
|
|
}
|
|
|
|
// PTSEqualsDTS returns whether PTS equals DTS. Needed by rtptime.GlobalDecoder
|
|
func (*IncomingTrack) PTSEqualsDTS(*rtp.Packet) bool {
|
|
return true
|
|
}
|
|
|
|
func (t *IncomingTrack) start() {
|
|
// read incoming RTCP packets to make interceptors work
|
|
go func() {
|
|
buf := make([]byte, 1500)
|
|
for {
|
|
_, _, err := t.receiver.Read(buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
// send period key frame requests
|
|
if t.track.Kind() == webrtc.RTPCodecTypeVideo {
|
|
go func() {
|
|
keyframeTicker := time.NewTicker(keyFrameInterval)
|
|
defer keyframeTicker.Stop()
|
|
|
|
for range keyframeTicker.C {
|
|
err := t.writeRTCP([]rtcp.Packet{
|
|
&rtcp.PictureLossIndication{
|
|
MediaSSRC: uint32(t.track.SSRC()),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// read incoming RTP packets
|
|
go func() {
|
|
reorderer := rtpreorderer.New()
|
|
|
|
for {
|
|
pkt, _, err := t.track.ReadRTP()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
packets, lost := reorderer.Process(pkt)
|
|
if lost != 0 {
|
|
t.log.Log(logger.Warn, (liberrors.ErrClientRTPPacketsLost{Lost: lost}).Error())
|
|
// do not return
|
|
}
|
|
|
|
for _, pkt := range packets {
|
|
// sometimes Chrome sends empty RTP packets. ignore them.
|
|
if len(pkt.Payload) == 0 {
|
|
continue
|
|
}
|
|
|
|
t.OnPacketRTP(pkt)
|
|
}
|
|
}
|
|
}()
|
|
}
|