Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish, proxy, record and playback video and audio streams.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

364 lines
7.3 KiB

package core
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph264"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9"
"github.com/pion/webrtc/v3"
"github.com/bluenviron/mediamtx/internal/asyncwriter"
"github.com/bluenviron/mediamtx/internal/stream"
"github.com/bluenviron/mediamtx/internal/unit"
)
type webRTCOutgoingTrack struct {
sender *webrtc.RTPSender
media *description.Media
format format.Format
track *webrtc.TrackLocalStaticRTP
cb func(unit.Unit) error
}
func newWebRTCOutgoingTrackVideo(desc *description.Session) (*webRTCOutgoingTrack, error) {
var av1Format *format.AV1
videoMedia := desc.FindFormat(&av1Format)
if videoMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeAV1,
ClockRate: 90000,
},
"av1",
webrtcStreamID,
)
if err != nil {
return nil, err
}
encoder := &rtpav1.Encoder{
PayloadType: 105,
PayloadMaxSize: webrtcPayloadMaxSize,
}
err = encoder.Init()
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: videoMedia,
format: av1Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
tunit := u.(*unit.AV1)
if tunit.TU == nil {
return nil
}
packets, err := encoder.Encode(tunit.TU)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
var vp9Format *format.VP9
videoMedia = desc.FindFormat(&vp9Format)
if videoMedia != nil { //nolint:dupl
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP9,
ClockRate: uint32(vp9Format.ClockRate()),
},
"vp9",
webrtcStreamID,
)
if err != nil {
return nil, err
}
encoder := &rtpvp9.Encoder{
PayloadType: 96,
PayloadMaxSize: webrtcPayloadMaxSize,
}
err = encoder.Init()
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: videoMedia,
format: vp9Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
return nil
}
packets, err := encoder.Encode(tunit.Frame)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
var vp8Format *format.VP8
videoMedia = desc.FindFormat(&vp8Format)
if videoMedia != nil { //nolint:dupl
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP8,
ClockRate: uint32(vp8Format.ClockRate()),
},
"vp8",
webrtcStreamID,
)
if err != nil {
return nil, err
}
encoder := &rtpvp8.Encoder{
PayloadType: 96,
PayloadMaxSize: webrtcPayloadMaxSize,
}
err = encoder.Init()
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: videoMedia,
format: vp8Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
tunit := u.(*unit.VP8)
if tunit.Frame == nil {
return nil
}
packets, err := encoder.Encode(tunit.Frame)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
var h264Format *format.H264
videoMedia = desc.FindFormat(&h264Format)
if videoMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeH264,
ClockRate: uint32(h264Format.ClockRate()),
},
"h264",
webrtcStreamID,
)
if err != nil {
return nil, err
}
encoder := &rtph264.Encoder{
PayloadType: 96,
PayloadMaxSize: webrtcPayloadMaxSize,
}
err = encoder.Init()
if err != nil {
return nil, err
}
firstReceived := false
var lastPTS time.Duration
return &webRTCOutgoingTrack{
media: videoMedia,
format: h264Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
return fmt.Errorf("WebRTC doesn't support H264 streams with B-frames")
}
lastPTS = tunit.PTS
packets, err := encoder.Encode(tunit.AU)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
return nil, nil
}
func newWebRTCOutgoingTrackAudio(desc *description.Session) (*webRTCOutgoingTrack, error) {
var opusFormat *format.Opus
audioMedia := desc.FindFormat(&opusFormat)
if audioMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeOpus,
ClockRate: uint32(opusFormat.ClockRate()),
Channels: 2,
},
"opus",
webrtcStreamID,
)
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: audioMedia,
format: opusFormat,
track: webRTCTrak,
cb: func(u unit.Unit) error {
for _, pkt := range u.GetRTPPackets() {
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
var g722Format *format.G722
audioMedia = desc.FindFormat(&g722Format)
if audioMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeG722,
ClockRate: uint32(g722Format.ClockRate()),
},
"g722",
webrtcStreamID,
)
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: audioMedia,
format: g722Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
for _, pkt := range u.GetRTPPackets() {
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
var g711Format *format.G711
audioMedia = desc.FindFormat(&g711Format)
if audioMedia != nil {
var mtyp string
if g711Format.MULaw {
mtyp = webrtc.MimeTypePCMU
} else {
mtyp = webrtc.MimeTypePCMA
}
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: mtyp,
ClockRate: uint32(g711Format.ClockRate()),
},
"g711",
webrtcStreamID,
)
if err != nil {
return nil, err
}
return &webRTCOutgoingTrack{
media: audioMedia,
format: g711Format,
track: webRTCTrak,
cb: func(u unit.Unit) error {
for _, pkt := range u.GetRTPPackets() {
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
return nil
},
}, nil
}
return nil, nil
}
func (t *webRTCOutgoingTrack) start(
stream *stream.Stream,
writer *asyncwriter.Writer,
) {
// read incoming RTCP packets to make interceptors work
go func() {
buf := make([]byte, 1500)
for {
_, _, err := t.sender.Read(buf)
if err != nil {
return
}
}
}()
stream.AddReader(writer, t.media, t.format, t.cb)
}