diff --git a/go.mod b/go.mod index 726f76f42e0..527f525fac3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,10 @@ module github.com/pion/webrtc/v4 -go 1.20 +go 1.22.0 + +toolchain go1.22.6 + +replace github.com/pion/rtp => github.com/aginetwork7/rtp v1.0.0-alpha.0 require ( github.com/pion/datachannel v1.5.10 @@ -25,7 +29,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.17.0 // indirect + github.com/onsi/gomega v1.36.2 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/turn/v4 v4.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index c4be8622885..2bf083e229a 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/aginetwork7/rtp v1.0.0-alpha.0 h1:XNJlS5Ccb320r0vKOlVKiYqIEyKLlVBV1KW+VbY/+TI= +github.com/aginetwork7/rtp v1.0.0-alpha.0/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -12,29 +14,29 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= @@ -51,8 +53,6 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.13 h1:8uSUPpjSL4OlwZI8Ygqu7+h2p9NPFB+yAZ461Xn5sNg= -github.com/pion/rtp v1.8.13/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= @@ -87,7 +87,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -101,16 +100,13 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -124,17 +120,14 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/peerconnection.go b/peerconnection.go index cfac67db2b9..17dcbebef7b 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -1042,7 +1042,8 @@ func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { weAnswer := desc.Type == SDPTypeAnswer remoteDesc := pc.RemoteDescription() if weAnswer && remoteDesc != nil { - _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, false) + rejectedMids := []string{} + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, false, &rejectedMids) if err := pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1050,6 +1051,9 @@ func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { pc.ops.Enqueue(func() { pc.startRTP(haveLocalDescription, remoteDesc, currentTransceivers) }) + pc.mu.Lock() + pc.removeRTPTransceiver(rejectedMids) + pc.mu.Unlock() } mediaSection, ok := selectCandidateMediaSection(desc.parsed) @@ -1226,7 +1230,8 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { if isRenegotiation { if weOffer { - _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true) + rejected := []string{} + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true, &rejected) if err = pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1234,6 +1239,9 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { pc.ops.Enqueue(func() { pc.startRTP(true, &desc, currentTransceivers) }) + pc.mu.Lock() + pc.removeRTPTransceiver(rejected) + pc.mu.Unlock() } return nil @@ -1258,7 +1266,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { // Start the networking in a new routine since it will block until // the connection is actually established. if weOffer { - _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true) + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true, nil) if err := pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1333,12 +1341,8 @@ func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPRece } } -//nolint:cyclop -func setRTPTransceiverCurrentDirection( - answer *SessionDescription, - currentTransceivers []*RTPTransceiver, - weOffer bool, -) error { +// rejected is only meaningful when SessionDescription is answer +func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransceivers []*RTPTransceiver, weOffer bool, rejectedMids *[]string) error { currentTransceivers = append([]*RTPTransceiver{}, currentTransceivers...) for _, media := range answer.parsed.MediaDescriptions { midValue := getMidValue(media) @@ -1379,7 +1383,10 @@ func setRTPTransceiverCurrentDirection( if !weOffer && direction == RTPTransceiverDirectionSendonly && transceiver.Sender() == nil { direction = RTPTransceiverDirectionInactive } - + // reject transceiver if it is inactive + if rejectedMids != nil && media.MediaName.Port.Value == 0 && direction == RTPTransceiverDirectionInactive { + *rejectedMids = append(*rejectedMids, midValue) + } transceiver.setCurrentDirection(direction) } @@ -2419,6 +2426,38 @@ func (pc *PeerConnection) addRTPTransceiver(t *RTPTransceiver) { pc.onNegotiationNeeded() } +// removeRTPTransceiver remove inactive +// and fires onNegotiationNeeded; +// caller of this method should hold `pc.mu` lock +func (pc *PeerConnection) removeRTPTransceiver(mids []string) { + if len(mids) == 0 { + return + } + + midSet := make(map[string]struct{}, len(mids)) + for _, mid := range mids { + if mid == "" { + continue + } + midSet[mid] = struct{}{} + } + + n := 0 + for _, transceiver := range pc.rtpTransceivers { + if _, exists := midSet[transceiver.Mid()]; exists { + err := transceiver.Stop() + if err != nil { + pc.log.Errorf("Failed to stop transceiver: %s", err) + } + } else { + pc.rtpTransceivers[n] = transceiver + n++ + } + } + // Resize the slice to remove unwanted transceivers + pc.rtpTransceivers = pc.rtpTransceivers[:n] +} + // CurrentLocalDescription represents the local description that was // successfully negotiated the last time the PeerConnection transitioned // into the stable state plus any local candidates that have been generated @@ -2830,10 +2869,16 @@ func (pc *PeerConnection) generateMatchedSDP( mediaTransceivers := []*RTPTransceiver{transceiver} extensions, _ := rtpExtensionsFromMediaDescription(media) - mediaSections = append( - mediaSections, - mediaSection{id: midValue, transceivers: mediaTransceivers, matchExtensions: extensions, rids: getRids(media)}, - ) + rejected := false + if media.MediaName.Port.Value == 0 { + for _, attr := range media.Attributes { + if attr.Key == sdp.AttrKeyInactive { + rejected = true + break + } + } + } + mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers, matchExtensions: extensions, rids: getRids(media), rejected: rejected}) } } diff --git a/peerconnection_test.go b/peerconnection_test.go index c2584c8ed25..8c91c07f104 100644 --- a/peerconnection_test.go +++ b/peerconnection_test.go @@ -753,6 +753,51 @@ func TestAddTransceiver(t *testing.T) { } // Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected. +func TestRemoveMiddleTransceiverAndRenegotiate(t *testing.T) { + // Initialize the offerPC PeerConnection + offerPC, err := NewPeerConnection(Configuration{}) + assert.NoError(t, err) + defer offerPC.Close() + + // Initialize the answerPC PeerConnection + answerPC, err := NewPeerConnection(Configuration{}) + assert.NoError(t, err) + defer answerPC.Close() + + // Add multiple transceivers to the offerer + videoTransceiver1, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo) + assert.NoError(t, err) + audioTransceiver, err := offerPC.AddTransceiverFromKind(RTPCodecTypeAudio) + assert.NoError(t, err) + videoTransceiver2, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo) + assert.NoError(t, err) + + // Perform initial SDP negotiation + offer, err := offerPC.CreateOffer(nil) + assert.NoError(t, err) + assert.NoError(t, offerPC.SetLocalDescription(offer)) + assert.NoError(t, answerPC.SetRemoteDescription(*offerPC.LocalDescription())) + answer, err := answerPC.CreateAnswer(nil) + assert.NoError(t, err) + assert.NoError(t, answerPC.SetLocalDescription(answer)) + assert.NoError(t, offerPC.SetRemoteDescription(*answerPC.LocalDescription())) + + // Ensure MIDs are assigned + assert.NotEmpty(t, videoTransceiver1.Mid()) + assert.NotEmpty(t, audioTransceiver.Mid()) + assert.NotEmpty(t, videoTransceiver2.Mid()) + + // Remove the middle transceiver (audio) using its MID + offerPC.removeRTPTransceiver([]string{audioTransceiver.Mid()}) + + // Verify the transceiver was removed + remainingTransceivers := offerPC.GetTransceivers() + assert.Len(t, remainingTransceivers, 2) + assert.Equal(t, videoTransceiver1.Mid(), remainingTransceivers[0].Mid()) + assert.Equal(t, videoTransceiver2.Mid(), remainingTransceivers[1].Mid()) +} + +// Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected func TestTransportChain(t *testing.T) { offer, answer, err := newPair() assert.NoError(t, err) diff --git a/sdp.go b/sdp.go index b41d3bfe404..9f056e33a42 100644 --- a/sdp.go +++ b/sdp.go @@ -519,10 +519,12 @@ func addTransceiverSDP( media.WithValueAttribute("rtcp-fb", fmt.Sprintf("%d %s %s", codec.PayloadType, feedback.Type, feedback.Parameter)) } } - if len(codecs) == 0 { + if len(codecs) == 0 || mediaSection.rejected { // If we are sender and we have no codecs throw an error early if transceiver.Sender() != nil { - return false, ErrSenderWithNoCodecs + if !mediaSection.rejected { + return false, ErrSenderWithNoCodecs + } } // Explicitly reject track if we don't have the codec @@ -546,9 +548,14 @@ func addTransceiverSDP( Address: "0.0.0.0", }, }, + Attributes: []sdp.Attribute{ + {Key: RTPTransceiverDirectionInactive.String(), Value: ""}, + {Key: "mid", Value: midValue}, + }, }) return false, nil + } directions := []RTPTransceiverDirection{} @@ -619,6 +626,7 @@ type mediaSection struct { data bool matchExtensions map[string]int rids []*simulcastRid + rejected bool } func bundleMatchFromRemote(matchBundleGroup *string) func(mid string) bool { diff --git a/track_local_static.go b/track_local_static.go index 7512599f1fa..7bb921fdead 100644 --- a/track_local_static.go +++ b/track_local_static.go @@ -213,10 +213,11 @@ func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) { // TrackLocalStaticSample is a TrackLocal that has a pre-set codec and accepts Samples. // If you wish to send a RTP Packet use TrackLocalStaticRTP. type TrackLocalStaticSample struct { - packetizer rtp.Packetizer - sequencer rtp.Sequencer - rtpTrack *TrackLocalStaticRTP - clockRate float64 + packetizer rtp.Packetizer + sequencer rtp.Sequencer + rtpTrack *TrackLocalStaticRTP + clockRate float64 + lastTimestamp uint32 } // NewTrackLocalStaticSample returns a TrackLocalStaticSample. @@ -280,14 +281,17 @@ func (s *TrackLocalStaticSample) Bind(t TrackLocalContext) (RTPCodecParameters, if err != nil { return codec, err } + if s.sequencer == nil { + s.sequencer = rtp.NewRandomSequencer() + } - s.sequencer = rtp.NewRandomSequencer() s.packetizer = rtp.NewPacketizer( rtpOutboundMTU, 0, // Value is handled when writing 0, // Value is handled when writing payloader, s.sequencer, + s.lastTimestamp, codec.ClockRate, ) s.clockRate = float64(codec.RTPCodecCapability.ClockRate) @@ -301,6 +305,22 @@ func (s *TrackLocalStaticSample) Unbind(t TrackLocalContext) error { return s.rtpTrack.Unbind(t) } +func (s *TrackLocalStaticSample) SetSequencer(sequencer rtp.Sequencer) { + s.sequencer = sequencer +} + +func (s *TrackLocalStaticSample) GetSequencer() rtp.Sequencer { + return s.sequencer +} + +func (s *TrackLocalStaticSample) SetTimestamp(ts uint32) { + s.lastTimestamp = ts +} + +func (s *TrackLocalStaticSample) GetTimestamp() uint32 { + return s.lastTimestamp +} + // WriteSample writes a Sample to the TrackLocalStaticSample // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed