mirror of
https://github.com/bluenviron/mediamtx.git
synced 2026-01-24 04:19:48 -08:00
change sdp library
This commit is contained in:
parent
f01bb9685f
commit
7dc904a131
6 changed files with 110 additions and 43 deletions
5
go.mod
5
go.mod
|
|
@ -5,10 +5,11 @@ go 1.13
|
|||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||
github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68
|
||||
github.com/aler9/gortsplib v0.0.0-20200712140456-c87fdcdbff66
|
||||
github.com/pion/rtcp v1.2.3
|
||||
github.com/pion/sdp v1.3.0
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
gortc.io/sdp v0.18.2
|
||||
)
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -2,12 +2,14 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
|
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68 h1:apyYugiG/luHl0Xyc2xJtGkL2HbF1umxjCNP2sX9iyw=
|
||||
github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68/go.mod h1:sL64nUkmrTVhlT/GCaxRXyI2Xk7m8XSdw5Uv8xKGPdc=
|
||||
github.com/aler9/gortsplib v0.0.0-20200712140456-c87fdcdbff66 h1:YK6Fjwn5aEnvGjRPodjKmpkQ8VZbA58F5OvAecw0eJM=
|
||||
github.com/aler9/gortsplib v0.0.0-20200712140456-c87fdcdbff66/go.mod h1:YiIgmmv0ELkWUy11Jj2h5AgfqLCpy8sIX/l9MmS8+uw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pion/rtcp v1.2.3 h1:2wrhKnqgSz91Q5nzYTO07mQXztYPtxL8a0XOss4rJqA=
|
||||
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
|
||||
github.com/pion/sdp v1.3.0 h1:21lpgEILHyolpsIrbCBagZaAPj4o057cFjzaFebkVOs=
|
||||
github.com/pion/sdp v1.3.0/go.mod h1:ceA2lTyftydQTuCIbUNoH77aAt6CiQJaRpssA4Gee8I=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
|
@ -23,5 +25,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gortc.io/sdp v0.18.2 h1:w2L1h9rgMZwGQz/bYMxTQ36POQ114ETpYUX1pFfSsLg=
|
||||
gortc.io/sdp v0.18.2/go.mod h1:Oj8tpRIx+Zx6lyrQR9+HHegByfbaz92A9wPSWrQhTI4=
|
||||
|
|
|
|||
8
main.go
8
main.go
|
|
@ -10,8 +10,8 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/pion/sdp"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
"gortc.io/sdp"
|
||||
)
|
||||
|
||||
var Version = "v0.0.0"
|
||||
|
|
@ -155,7 +155,7 @@ func (programEventTerminate) isProgramEvent() {}
|
|||
type publisher interface {
|
||||
publisherIsReady() bool
|
||||
publisherSdpText() []byte
|
||||
publisherSdpParsed() *sdp.Message
|
||||
publisherSdpParsed() *sdp.SessionDescription
|
||||
}
|
||||
|
||||
type program struct {
|
||||
|
|
@ -332,7 +332,7 @@ outer:
|
|||
|
||||
sdpParsed := pub.publisherSdpParsed()
|
||||
|
||||
if len(evt.client.streamTracks) >= len(sdpParsed.Medias) {
|
||||
if len(evt.client.streamTracks) >= len(sdpParsed.MediaDescriptions) {
|
||||
evt.res <- fmt.Errorf("all the tracks have already been setup")
|
||||
continue
|
||||
}
|
||||
|
|
@ -364,7 +364,7 @@ outer:
|
|||
|
||||
sdpParsed := pub.publisherSdpParsed()
|
||||
|
||||
if len(evt.client.streamTracks) != len(sdpParsed.Medias) {
|
||||
if len(evt.client.streamTracks) != len(sdpParsed.MediaDescriptions) {
|
||||
evt.res <- fmt.Errorf("not all tracks have been setup")
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"gortc.io/sdp"
|
||||
"github.com/pion/sdp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -71,8 +71,8 @@ type serverClient struct {
|
|||
authPass string
|
||||
authHelper *gortsplib.AuthServer
|
||||
authFailures int
|
||||
streamSdpText []byte // only if publisher
|
||||
streamSdpParsed *sdp.Message // only if publisher
|
||||
streamSdpText []byte // only if publisher
|
||||
streamSdpParsed *sdp.SessionDescription // only if publisher
|
||||
streamProtocol streamProtocol
|
||||
streamTracks []*track
|
||||
rtcpReceivers []*rtcpReceiver
|
||||
|
|
@ -120,7 +120,7 @@ func (c *serverClient) publisherSdpText() []byte {
|
|||
return c.streamSdpText
|
||||
}
|
||||
|
||||
func (c *serverClient) publisherSdpParsed() *sdp.Message {
|
||||
func (c *serverClient) publisherSdpParsed() *sdp.SessionDescription {
|
||||
return c.streamSdpParsed
|
||||
}
|
||||
|
||||
|
|
@ -631,6 +631,11 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
|
|||
return errClientTerminate
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("path can't be empty"))
|
||||
return errClientTerminate
|
||||
}
|
||||
|
||||
pconf := c.findConfForPath(path)
|
||||
if pconf == nil {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest,
|
||||
|
|
@ -657,15 +662,16 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
|
|||
return errClientTerminate
|
||||
}
|
||||
|
||||
sdpParsed, err := gortsplib.SDPParse(req.Content)
|
||||
sdpParsed := &sdp.SessionDescription{}
|
||||
err = sdpParsed.Unmarshal(string(req.Content))
|
||||
if err != nil {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("invalid SDP: %s", err))
|
||||
return errClientTerminate
|
||||
}
|
||||
sdpParsed, req.Content = gortsplib.SDPFilter(sdpParsed, req.Content)
|
||||
sdpParsed, req.Content = sdpForServer(sdpParsed, req.Content)
|
||||
|
||||
if len(path) == 0 {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("path can't be empty"))
|
||||
if len(sdpParsed.MediaDescriptions) == 0 {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("no tracks defined"))
|
||||
return errClientTerminate
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +868,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
|
|||
return errClientTerminate
|
||||
}
|
||||
|
||||
if len(c.streamTracks) >= len(c.streamSdpParsed.Medias) {
|
||||
if len(c.streamTracks) >= len(c.streamSdpParsed.MediaDescriptions) {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("all the tracks have already been setup"))
|
||||
return errClientTerminate
|
||||
}
|
||||
|
|
@ -914,7 +920,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
|
|||
return errClientTerminate
|
||||
}
|
||||
|
||||
if len(c.streamTracks) >= len(c.streamSdpParsed.Medias) {
|
||||
if len(c.streamTracks) >= len(c.streamSdpParsed.MediaDescriptions) {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("all the tracks have already been setup"))
|
||||
return errClientTerminate
|
||||
}
|
||||
|
|
@ -1014,7 +1020,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
|
|||
return errClientTerminate
|
||||
}
|
||||
|
||||
if len(c.streamTracks) != len(c.streamSdpParsed.Medias) {
|
||||
if len(c.streamTracks) != len(c.streamSdpParsed.MediaDescriptions) {
|
||||
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("not all tracks have been setup"))
|
||||
return errClientTerminate
|
||||
}
|
||||
|
|
|
|||
45
streamer.go
45
streamer.go
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"gortc.io/sdp"
|
||||
"github.com/pion/sdp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -31,9 +31,9 @@ type streamer struct {
|
|||
ur *url.URL
|
||||
proto streamProtocol
|
||||
ready bool
|
||||
clientSdpParsed *sdp.Message
|
||||
clientSdpParsed *sdp.SessionDescription
|
||||
serverSdpText []byte
|
||||
serverSdpParsed *sdp.Message
|
||||
serverSdpParsed *sdp.SessionDescription
|
||||
rtcpReceivers []*rtcpReceiver
|
||||
readBuf *doubleBuffer
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ func (s *streamer) publisherSdpText() []byte {
|
|||
return s.serverSdpText
|
||||
}
|
||||
|
||||
func (s *streamer) publisherSdpParsed() *sdp.Message {
|
||||
func (s *streamer) publisherSdpParsed() *sdp.SessionDescription {
|
||||
return s.serverSdpParsed
|
||||
}
|
||||
|
||||
|
|
@ -217,14 +217,15 @@ func (s *streamer) do() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
clientSdpParsed, err := gortsplib.SDPParse(res.Content)
|
||||
clientSdpParsed := &sdp.SessionDescription{}
|
||||
err = clientSdpParsed.Unmarshal(string(res.Content))
|
||||
if err != nil {
|
||||
s.log("ERR: invalid SDP: %s", err)
|
||||
return true
|
||||
}
|
||||
|
||||
// create a filtered SDP that is used by the server (not by the client)
|
||||
serverSdpParsed, serverSdpText := gortsplib.SDPFilter(clientSdpParsed, res.Content)
|
||||
serverSdpParsed, serverSdpText := sdpForServer(clientSdpParsed, res.Content)
|
||||
|
||||
s.clientSdpParsed = clientSdpParsed
|
||||
s.serverSdpText = serverSdpText
|
||||
|
|
@ -249,7 +250,7 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
|
|||
}
|
||||
}()
|
||||
|
||||
for i, media := range s.clientSdpParsed.Medias {
|
||||
for i, media := range s.clientSdpParsed.MediaDescriptions {
|
||||
var rtpPort int
|
||||
var rtcpPort int
|
||||
var rtpl *streamerUdpListener
|
||||
|
|
@ -282,7 +283,7 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
|
|||
res, err := conn.WriteRequest(&gortsplib.Request{
|
||||
Method: gortsplib.SETUP,
|
||||
Url: func() *url.URL {
|
||||
control := media.Attributes.Value("control")
|
||||
control := sdpFindAttribute(media.Attributes, "control")
|
||||
|
||||
// no control attribute
|
||||
if control == "" {
|
||||
|
|
@ -309,7 +310,7 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
|
|||
ret += "/"
|
||||
}
|
||||
|
||||
control := media.Attributes.Value("control")
|
||||
control := sdpFindAttribute(media.Attributes, "control")
|
||||
if control != "" {
|
||||
ret += control
|
||||
} else {
|
||||
|
|
@ -388,8 +389,8 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.Medias))
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.MediaDescriptions))
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
s.rtcpReceivers[trackId] = newRtcpReceiver()
|
||||
}
|
||||
|
||||
|
|
@ -429,7 +430,7 @@ outer:
|
|||
}
|
||||
|
||||
case <-checkStreamTicker.C:
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
if time.Since(s.rtcpReceivers[trackId].lastFrameTime()) >= s.p.conf.StreamDeadAfter {
|
||||
s.log("ERR: stream is dead")
|
||||
ret = true
|
||||
|
|
@ -438,7 +439,7 @@ outer:
|
|||
}
|
||||
|
||||
case <-receiverReportTicker.C:
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
frame := s.rtcpReceivers[trackId].report()
|
||||
streamerUdpListenerPairs[trackId].rtcpl.writeChan <- &udpAddrBufPair{
|
||||
addr: &net.UDPAddr{
|
||||
|
|
@ -463,7 +464,7 @@ outer:
|
|||
pair.rtcpl.stop()
|
||||
}
|
||||
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
s.rtcpReceivers[trackId].close()
|
||||
}
|
||||
|
||||
|
|
@ -471,13 +472,13 @@ outer:
|
|||
}
|
||||
|
||||
func (s *streamer) runTcp(conn *gortsplib.ConnClient) bool {
|
||||
for i, media := range s.clientSdpParsed.Medias {
|
||||
for i, media := range s.clientSdpParsed.MediaDescriptions {
|
||||
interleaved := fmt.Sprintf("interleaved=%d-%d", (i * 2), (i*2)+1)
|
||||
|
||||
res, err := conn.WriteRequest(&gortsplib.Request{
|
||||
Method: gortsplib.SETUP,
|
||||
Url: func() *url.URL {
|
||||
control := media.Attributes.Value("control")
|
||||
control := sdpFindAttribute(media.Attributes, "control")
|
||||
|
||||
// no control attribute
|
||||
if control == "" {
|
||||
|
|
@ -504,7 +505,7 @@ func (s *streamer) runTcp(conn *gortsplib.ConnClient) bool {
|
|||
ret += "/"
|
||||
}
|
||||
|
||||
control := media.Attributes.Value("control")
|
||||
control := sdpFindAttribute(media.Attributes, "control")
|
||||
if control != "" {
|
||||
ret += control
|
||||
} else {
|
||||
|
|
@ -589,8 +590,8 @@ outer1:
|
|||
}
|
||||
}
|
||||
|
||||
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.Medias))
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.MediaDescriptions))
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
s.rtcpReceivers[trackId] = newRtcpReceiver()
|
||||
}
|
||||
|
||||
|
|
@ -632,7 +633,7 @@ outer2:
|
|||
break outer2
|
||||
|
||||
case <-checkStreamTicker.C:
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
if time.Since(s.rtcpReceivers[trackId].lastFrameTime()) >= s.p.conf.StreamDeadAfter {
|
||||
s.log("ERR: stream is dead")
|
||||
ret = true
|
||||
|
|
@ -641,7 +642,7 @@ outer2:
|
|||
}
|
||||
|
||||
case <-receiverReportTicker.C:
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
frame := s.rtcpReceivers[trackId].report()
|
||||
|
||||
channel := trackFlowTypeToInterleavedChannel(trackId, _TRACK_FLOW_TYPE_RTCP)
|
||||
|
|
@ -659,7 +660,7 @@ outer2:
|
|||
|
||||
s.p.events <- programEventStreamerNotReady{s}
|
||||
|
||||
for trackId := range s.clientSdpParsed.Medias {
|
||||
for trackId := range s.clientSdpParsed.MediaDescriptions {
|
||||
s.rtcpReceivers[trackId].close()
|
||||
}
|
||||
|
||||
|
|
|
|||
59
utils.go
59
utils.go
|
|
@ -4,9 +4,11 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/sdp"
|
||||
)
|
||||
|
||||
func parseIpCidrList(in []string) ([]interface{}, error) {
|
||||
|
|
@ -230,3 +232,60 @@ func (rr *rtcpReceiver) report() []byte {
|
|||
rr.events <- rtcpReceiverEventReport{res}
|
||||
return <-res
|
||||
}
|
||||
|
||||
func sdpFindAttribute(attributes []sdp.Attribute, key string) string {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == key {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func sdpForServer(sin *sdp.SessionDescription, bytsin []byte) (*sdp.SessionDescription, []byte) {
|
||||
sout := &sdp.SessionDescription{
|
||||
SessionName: "Stream",
|
||||
Origin: sdp.Origin{
|
||||
Username: "-",
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
UnicastAddress: "127.0.0.1",
|
||||
},
|
||||
TimeDescriptions: []sdp.TimeDescription{
|
||||
{Timing: sdp.Timing{0, 0}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, min := range sin.MediaDescriptions {
|
||||
mout := &sdp.MediaDescription{
|
||||
MediaName: sdp.MediaName{
|
||||
Media: min.MediaName.Media,
|
||||
Protos: []string{"RTP", "AVP"}, // override protocol
|
||||
Formats: min.MediaName.Formats,
|
||||
},
|
||||
Bandwidth: min.Bandwidth,
|
||||
Attributes: func() []sdp.Attribute {
|
||||
var ret []sdp.Attribute
|
||||
|
||||
for _, attr := range min.Attributes {
|
||||
if attr.Key == "rtpmap" || attr.Key == "fmtp" {
|
||||
ret = append(ret, attr)
|
||||
}
|
||||
}
|
||||
|
||||
// control attribute is mandatory, and is the path that is appended
|
||||
// to the stream path in SETUP
|
||||
ret = append(ret, sdp.Attribute{
|
||||
Key: "control",
|
||||
Value: "trackID=" + strconv.FormatInt(int64(i), 10),
|
||||
})
|
||||
|
||||
return ret
|
||||
}(),
|
||||
}
|
||||
sout.MediaDescriptions = append(sout.MediaDescriptions, mout)
|
||||
}
|
||||
|
||||
bytsout := []byte(sout.Marshal())
|
||||
return sout, bytsout
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue