diff --git a/constants.go b/constants.go index 94ba78494a6..79c2fa38074 100644 --- a/constants.go +++ b/constants.go @@ -27,6 +27,8 @@ const ( sdpAttributeRid = "rid" + sdpAttributeSimulcast = "simulcast" + rtpOutboundMTU = 1200 rtpPayloadTypeBitmask = 0x7F diff --git a/sdp.go b/sdp.go index 44c42ae40a3..1a9b29566cd 100644 --- a/sdp.go +++ b/sdp.go @@ -232,12 +232,30 @@ func trackDetailsToRTPReceiveParameters(t *trackDetails) RTPReceiveParameters { return RTPReceiveParameters{Encodings: encodings} } -func getRids(media *sdp.MediaDescription) map[string]string { - rids := map[string]string{} +func getRids(media *sdp.MediaDescription) map[string]*simulcastRid { + rids := map[string]*simulcastRid{} + var simulcastAttr string for _, attr := range media.Attributes { if attr.Key == sdpAttributeRid { split := strings.Split(attr.Value, " ") - rids[split[0]] = attr.Value + rids[split[0]] = &simulcastRid{attrValue: attr.Value} + } else if attr.Key == sdpAttributeSimulcast { + simulcastAttr = attr.Value + } + } + // process paused stream like "a=simulcast:send 1;~2;~3" + if simulcastAttr != "" { + if space := strings.Index(simulcastAttr, " "); space > 0 { + simulcastAttr = simulcastAttr[space+1:] + } + ridStates := strings.Split(simulcastAttr, ";") + for _, ridState := range ridStates { + if ridState[:1] == "~" { + rid := ridState[1:] + if r, ok := rids[rid]; ok { + r.paused = true + } + } } } return rids @@ -379,7 +397,7 @@ func addSenderSDP( sendRids = append(sendRids, encoding.RID) } // Simulcast - media.WithValueAttribute("simulcast", "send "+strings.Join(sendRids, ";")) + media.WithValueAttribute(sdpAttributeSimulcast, "send "+strings.Join(sendRids, ";")) } if !isPlanB { @@ -475,10 +493,13 @@ func addTransceiverSDP( for rid := range mediaSection.ridMap { media.WithValueAttribute(sdpAttributeRid, rid+" recv") + if mediaSection.ridMap[rid].paused { + rid = "~" + rid + } recvRids = append(recvRids, rid) } // Simulcast - media.WithValueAttribute("simulcast", "recv "+strings.Join(recvRids, ";")) + media.WithValueAttribute(sdpAttributeSimulcast, "recv "+strings.Join(recvRids, ";")) } addSenderSDP(mediaSection, isPlanB, media) @@ -500,11 +521,16 @@ func addTransceiverSDP( return true, nil } +type simulcastRid struct { + attrValue string + paused bool +} + type mediaSection struct { id string transceivers []*RTPTransceiver data bool - ridMap map[string]string + ridMap map[string]*simulcastRid } func bundleMatchFromRemote(matchBundleGroup *string) func(mid string) bool { diff --git a/sdp_test.go b/sdp_test.go index a0d99b140f7..4696ce87279 100644 --- a/sdp_test.go +++ b/sdp_test.go @@ -381,8 +381,14 @@ func TestPopulateSDP(t *testing.T) { tr := &RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs} tr.setDirection(RTPTransceiverDirectionRecvonly) - ridMap := map[string]string{ - "ridkey": "some", + ridMap := map[string]*simulcastRid{ + "ridkey": { + attrValue: "some", + }, + "ridPaused": { + attrValue: "some2", + paused: true, + }, } mediaSections := []mediaSection{{id: "video", transceivers: []*RTPTransceiver{tr}, ridMap: ridMap}} @@ -392,21 +398,20 @@ func TestPopulateSDP(t *testing.T) { assert.Nil(t, err) // Test contains rid map keys - var found bool + var ridFound int for _, desc := range offerSdp.MediaDescriptions { if desc.MediaName.Media != "video" { continue } - for _, a := range desc.Attributes { - if a.Key == sdpAttributeRid { - if strings.Contains(a.Value, "ridkey") { - found = true - break - } - } + ridInSDP := getRids(desc) + if ridKey, ok := ridInSDP["ridkey"]; ok && !ridKey.paused { + ridFound++ + } + if ridPaused, ok := ridInSDP["ridPaused"]; ok && ridPaused.paused { + ridFound++ } } - assert.Equal(t, true, found, "Rid key should be present") + assert.Equal(t, 2, ridFound, "All rid keys should be present") }) t.Run("SetCodecPreferences", func(t *testing.T) { se := SettingEngine{}