Skip to content

Commit 6ee528d

Browse files
jbrady42Sean-Der
authored andcommitted
Add Simulcast support
Resolves #1016
1 parent 570ddd0 commit 6ee528d

File tree

10 files changed

+406
-136
lines changed

10 files changed

+406
-136
lines changed

constants.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@ const (
1010
// Equal to UDP MTU
1111
receiveMTU = 1460
1212

13+
// simulcastProbeCount is the amount of RTP Packets
14+
// that handleUndeclaredSSRC will read and try to dispatch from
15+
// mid and rid values
16+
simulcastProbeCount = 10
17+
1318
mediaSectionApplication = "application"
1419
)

examples/simulcast/main.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"fmt"
55
"io"
6-
"log"
76
"math/rand"
87
"net/url"
98
"time"
@@ -103,17 +102,13 @@ func main() {
103102
panic(err)
104103
}
105104

106-
// fmt.Printf("offer: %s\n", offer.SDP)
107-
// Set the remote SessionDescription
108-
err = peerConnection.SetRemoteDescription(offer)
109-
if err != nil {
105+
if err = peerConnection.SetRemoteDescription(offer); err != nil {
110106
panic(err)
111107
}
112108

113109
// Set a handler for when a new remote track starts
114110
peerConnection.OnTrack(func(track *webrtc.Track, receiver *webrtc.RTPReceiver) {
115-
fmt.Printf("Track has started\n")
116-
log.Println("Track has started", track)
111+
fmt.Println("Track has started")
117112

118113
// Start reading from all the streams and sending them to the related output track
119114
rid := track.RID()
@@ -132,10 +127,9 @@ func main() {
132127
}
133128
}()
134129
for {
135-
var readErr error
136130
// Read RTP packets being sent to Pion
137131
packet, readErr := track.ReadRTP()
138-
if err != nil {
132+
if readErr != nil {
139133
panic(readErr)
140134
}
141135

@@ -157,8 +151,6 @@ func main() {
157151
panic(err)
158152
}
159153

160-
fmt.Printf("answer: %s\n", answer.SDP)
161-
162154
// Sets the LocalDescription, and starts our UDP listeners
163155
err = peerConnection.SetLocalDescription(answer)
164156
if err != nil {

peerconnection.go

Lines changed: 144 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"crypto/elliptic"
99
"crypto/rand"
1010
"fmt"
11+
"io"
1112
"strconv"
1213
"strings"
1314
"sync"
@@ -877,24 +878,35 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
877878
}
878879

879880
func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPReceiver) {
880-
err := receiver.Receive(RTPReceiveParameters{
881-
Encodings: RTPDecodingParameters{
882-
RTPCodingParameters{SSRC: incoming.ssrc},
883-
}})
884-
if err != nil {
881+
encodings := []RTPDecodingParameters{}
882+
if incoming.ssrc != 0 {
883+
encodings = append(encodings, RTPDecodingParameters{RTPCodingParameters{SSRC: incoming.ssrc}})
884+
}
885+
for _, rid := range incoming.rids {
886+
encodings = append(encodings, RTPDecodingParameters{RTPCodingParameters{RID: rid}})
887+
}
888+
889+
if err := receiver.Receive(RTPReceiveParameters{Encodings: encodings}); err != nil {
885890
pc.log.Warnf("RTPReceiver Receive failed %s", err)
886891
return
887892
}
888893

889894
// set track id and label early so they can be set as new track information
890895
// is received from the SDP.
891-
receiver.Track().mu.Lock()
892-
receiver.Track().id = incoming.id
893-
receiver.Track().label = incoming.label
894-
receiver.Track().mu.Unlock()
896+
for i := range receiver.tracks {
897+
receiver.tracks[i].track.mu.Lock()
898+
receiver.tracks[i].track.id = incoming.id
899+
receiver.tracks[i].track.label = incoming.label
900+
receiver.tracks[i].track.mu.Unlock()
901+
}
902+
903+
// We can't block and wait for a single SSRC
904+
if incoming.ssrc == 0 {
905+
return
906+
}
895907

896908
go func() {
897-
if err = receiver.Track().determinePayloadType(); err != nil {
909+
if err := receiver.Track().determinePayloadType(); err != nil {
898910
pc.log.Warnf("Could not determine PayloadType for SSRC %d", receiver.Track().SSRC())
899911
return
900912
}
@@ -922,7 +934,7 @@ func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPRece
922934
}
923935

924936
// startRTPReceivers opens knows inbound SRTP streams from the RemoteDescription
925-
func (pc *PeerConnection) startRTPReceivers(incomingTracks map[uint32]trackDetails, currentTransceivers []*RTPTransceiver) {
937+
func (pc *PeerConnection) startRTPReceivers(incomingTracks []trackDetails, currentTransceivers []*RTPTransceiver) {
926938
localTransceivers := append([]*RTPTransceiver{}, currentTransceivers...)
927939

928940
remoteIsPlanB := false
@@ -934,45 +946,54 @@ func (pc *PeerConnection) startRTPReceivers(incomingTracks map[uint32]trackDetai
934946
}
935947

936948
// Ensure we haven't already started a transceiver for this ssrc
937-
for ssrc := range incomingTracks {
938-
for i := range localTransceivers {
939-
if t := localTransceivers[i]; (t.Receiver()) == nil || t.Receiver().Track() == nil || t.Receiver().Track().ssrc != ssrc {
949+
for i := range incomingTracks {
950+
if len(incomingTracks) <= i {
951+
break
952+
}
953+
incomingTrack := incomingTracks[i]
954+
955+
for _, t := range localTransceivers {
956+
if (t.Receiver()) == nil || t.Receiver().Track() == nil || t.Receiver().Track().ssrc != incomingTrack.ssrc {
940957
continue
941958
}
942959

943-
delete(incomingTracks, ssrc)
960+
incomingTracks = filterTrackWithSSRC(incomingTracks, incomingTrack.ssrc)
944961
}
945962
}
946963

947-
for ssrc, incoming := range incomingTracks {
948-
for i := range localTransceivers {
949-
t := localTransceivers[i]
964+
for i := range incomingTracks {
965+
for j := range localTransceivers {
966+
if len(incomingTracks) <= i || len(localTransceivers) <= j {
967+
break
968+
}
969+
t := localTransceivers[j]
970+
incomingTrack := incomingTracks[i]
950971

951-
if t.Mid() != incoming.mid {
972+
if t.Mid() != incomingTrack.mid {
952973
continue
953974
}
954975

955-
if (incomingTracks[ssrc].kind != t.kind) ||
976+
if (incomingTrack.kind != t.kind) ||
956977
(t.Direction() != RTPTransceiverDirectionRecvonly && t.Direction() != RTPTransceiverDirectionSendrecv) ||
957978
(t.Receiver()) == nil ||
958979
(t.Receiver().haveReceived()) {
959980
continue
960981
}
961982

962-
delete(incomingTracks, ssrc)
963-
localTransceivers = append(localTransceivers[:i], localTransceivers[i+1:]...)
964-
pc.startReceiver(incoming, t.Receiver())
983+
incomingTracks = append(incomingTracks[:i], incomingTracks[i+1:]...)
984+
localTransceivers = append(localTransceivers[:j], localTransceivers[j+1:]...)
985+
pc.startReceiver(incomingTrack, t.Receiver())
965986
break
966987
}
967988
}
968989

969990
if remoteIsPlanB {
970-
for ssrc, incoming := range incomingTracks {
991+
for _, incoming := range incomingTracks {
971992
t, err := pc.AddTransceiverFromKind(incoming.kind, RtpTransceiverInit{
972993
Direction: RTPTransceiverDirectionSendrecv,
973994
})
974995
if err != nil {
975-
pc.log.Warnf("Could not add transceiver for remote SSRC %d: %s", ssrc, err)
996+
pc.log.Warnf("Could not add transceiver for remote SSRC %d: %s", incoming.ssrc, err)
976997
continue
977998
}
978999
pc.startReceiver(incoming, t.Receiver())
@@ -1036,58 +1057,115 @@ func (pc *PeerConnection) startSCTP() {
10361057
pc.sctpTransport.lock.Unlock()
10371058
}
10381059

1039-
// drainSRTP pulls and discards RTP/RTCP packets that don't match any a:ssrc lines
1040-
// If the remote SDP was only one media section the ssrc doesn't have to be explicitly declared
1041-
func (pc *PeerConnection) drainSRTP() {
1042-
handleUndeclaredSSRC := func(ssrc uint32) bool {
1043-
if remoteDescription := pc.RemoteDescription(); remoteDescription != nil {
1044-
if len(remoteDescription.parsed.MediaDescriptions) == 1 {
1045-
onlyMediaSection := remoteDescription.parsed.MediaDescriptions[0]
1046-
for _, a := range onlyMediaSection.Attributes {
1047-
if a.Key == ssrcStr {
1048-
return false
1049-
}
1050-
}
1051-
1052-
incoming := trackDetails{
1053-
ssrc: ssrc,
1054-
kind: RTPCodecTypeVideo,
1055-
}
1056-
if onlyMediaSection.MediaName.Media == RTPCodecTypeAudio.String() {
1057-
incoming.kind = RTPCodecTypeAudio
1058-
}
1060+
func (pc *PeerConnection) handleUndeclaredSSRC(rtpStream io.Reader, ssrc uint32) error {
1061+
remoteDescription := pc.RemoteDescription()
1062+
if remoteDescription == nil {
1063+
return fmt.Errorf("remote Description has not been set yet")
1064+
}
10591065

1060-
t, err := pc.AddTransceiverFromKind(incoming.kind, RtpTransceiverInit{
1061-
Direction: RTPTransceiverDirectionSendrecv,
1062-
})
1063-
if err != nil {
1064-
pc.log.Warnf("Could not add transceiver for remote SSRC %d: %s", ssrc, err)
1065-
return false
1066-
}
1067-
pc.startReceiver(incoming, t.Receiver())
1068-
return true
1066+
// If the remote SDP was only one media section the ssrc doesn't have to be explicitly declared
1067+
if len(remoteDescription.parsed.MediaDescriptions) == 1 {
1068+
onlyMediaSection := remoteDescription.parsed.MediaDescriptions[0]
1069+
for _, a := range onlyMediaSection.Attributes {
1070+
if a.Key == ssrcStr {
1071+
return fmt.Errorf("single media section has an explicit SSRC")
10691072
}
10701073
}
10711074

1072-
return false
1075+
incoming := trackDetails{
1076+
ssrc: ssrc,
1077+
kind: RTPCodecTypeVideo,
1078+
}
1079+
if onlyMediaSection.MediaName.Media == RTPCodecTypeAudio.String() {
1080+
incoming.kind = RTPCodecTypeAudio
1081+
}
1082+
1083+
t, err := pc.AddTransceiverFromKind(incoming.kind, RtpTransceiverInit{
1084+
Direction: RTPTransceiverDirectionSendrecv,
1085+
})
1086+
if err != nil {
1087+
return fmt.Errorf("could not add transceiver for remote SSRC %d: %s", ssrc, err)
1088+
}
1089+
pc.startReceiver(incoming, t.Receiver())
1090+
return nil
1091+
}
1092+
1093+
// Simulcast no longer uses SSRCes, but RID instead. We then use that value to populate rest of Track Data
1094+
matchedSDPMap, err := matchedAnswerExt(pc.RemoteDescription().parsed, pc.api.settingEngine.getSDPExtensions())
1095+
if err != nil {
1096+
return err
1097+
}
1098+
1099+
sdesMidExtMap := getExtMapByURI(matchedSDPMap, sdp.SDESMidURI)
1100+
sdesStreamIDExtMap := getExtMapByURI(matchedSDPMap, sdp.SDESRTPStreamIDURI)
1101+
if sdesMidExtMap == nil || sdesStreamIDExtMap == nil {
1102+
return fmt.Errorf("mid and rid RTP Extensions required for Simulcast")
1103+
}
1104+
1105+
b := make([]byte, receiveMTU)
1106+
var mid, rid string
1107+
for readCount := 0; readCount <= simulcastProbeCount; readCount++ {
1108+
i, err := rtpStream.Read(b)
1109+
if err != nil {
1110+
return err
1111+
}
1112+
1113+
maybeMid, maybeRid, payloadType, err := handleUnknownRTPPacket(b[:i], sdesMidExtMap, sdesStreamIDExtMap)
1114+
if err != nil {
1115+
return err
1116+
}
1117+
1118+
if maybeMid != "" {
1119+
mid = maybeMid
1120+
}
1121+
if maybeRid != "" {
1122+
rid = maybeRid
1123+
}
1124+
1125+
if mid == "" || rid == "" {
1126+
continue
1127+
}
1128+
1129+
codec, err := pc.api.mediaEngine.getCodec(payloadType)
1130+
if err != nil {
1131+
return err
1132+
}
1133+
1134+
for _, t := range pc.GetTransceivers() {
1135+
if t.Mid() != mid || t.Receiver() == nil {
1136+
continue
1137+
}
1138+
1139+
track, err := t.Receiver().receiveForRid(rid, codec, ssrc)
1140+
if err != nil {
1141+
return err
1142+
}
1143+
pc.onTrack(track, t.Receiver())
1144+
return nil
1145+
}
10731146
}
10741147

1148+
return fmt.Errorf("incoming SSRC failed Simulcast probing")
1149+
}
1150+
1151+
// undeclaredMediaProcessor handles RTP/RTCP packets that don't match any a:ssrc lines
1152+
func (pc *PeerConnection) undeclaredMediaProcessor() {
10751153
go func() {
10761154
for {
10771155
srtpSession, err := pc.dtlsTransport.getSRTPSession()
10781156
if err != nil {
1079-
pc.log.Warnf("drainSRTP failed to open SrtpSession: %v", err)
1157+
pc.log.Warnf("undeclaredMediaProcessor failed to open SrtpSession: %v", err)
10801158
return
10811159
}
10821160

1083-
_, ssrc, err := srtpSession.AcceptStream()
1161+
stream, ssrc, err := srtpSession.AcceptStream()
10841162
if err != nil {
10851163
pc.log.Warnf("Failed to accept RTP %v", err)
10861164
return
10871165
}
10881166

1089-
if !handleUndeclaredSSRC(ssrc) {
1090-
pc.log.Warnf("Incoming unhandled RTP ssrc(%d), OnTrack will not be fired", ssrc)
1167+
if err := pc.handleUndeclaredSSRC(stream, ssrc); err != nil {
1168+
pc.log.Errorf("Incoming unhandled RTP ssrc(%d), OnTrack will not be fired. %v", ssrc, err)
10911169
}
10921170
}
10931171
}()
@@ -1096,7 +1174,7 @@ func (pc *PeerConnection) drainSRTP() {
10961174
for {
10971175
srtcpSession, err := pc.dtlsTransport.getSRTCPSession()
10981176
if err != nil {
1099-
pc.log.Warnf("drainSRTP failed to open SrtcpSession: %v", err)
1177+
pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err)
11001178
return
11011179
}
11021180

@@ -1694,13 +1772,14 @@ func (pc *PeerConnection) startRTP(isRenegotiation bool, remoteDesc *SessionDesc
16941772

16951773
t.Receiver().Track().mu.Lock()
16961774
ssrc := t.Receiver().Track().ssrc
1697-
if _, ok := trackDetails[ssrc]; ok {
1698-
incoming := trackDetails[ssrc]
1699-
t.Receiver().Track().id = incoming.id
1700-
t.Receiver().Track().label = incoming.label
1775+
1776+
if details := trackDetailsForSSRC(trackDetails, ssrc); details != nil {
1777+
t.Receiver().Track().id = details.id
1778+
t.Receiver().Track().label = details.label
17011779
t.Receiver().Track().mu.Unlock()
17021780
continue
17031781
}
1782+
17041783
t.Receiver().Track().mu.Unlock()
17051784

17061785
if err := t.Receiver().Stop(); err != nil {
@@ -1721,7 +1800,7 @@ func (pc *PeerConnection) startRTP(isRenegotiation bool, remoteDesc *SessionDesc
17211800
pc.startRTPSenders(currentTransceivers)
17221801

17231802
if !isRenegotiation {
1724-
pc.drainSRTP()
1803+
pc.undeclaredMediaProcessor()
17251804
if haveApplicationMediaSection(remoteDesc.parsed) {
17261805
pc.startSCTP()
17271806
}

rtpcodingparameters.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package webrtc
44
// This is a subset of the RFC since Pion WebRTC doesn't implement encoding/decoding itself
55
// http://draft.ortc.org/#dom-rtcrtpcodingparameters
66
type RTPCodingParameters struct {
7+
RID string `json:"rid"`
78
SSRC uint32 `json:"ssrc"`
89
PayloadType uint8 `json:"payloadType"`
910
}

rtpreceiveparameters.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ package webrtc
22

33
// RTPReceiveParameters contains the RTP stack settings used by receivers
44
type RTPReceiveParameters struct {
5-
Encodings RTPDecodingParameters
5+
Encodings []RTPDecodingParameters
66
}

0 commit comments

Comments
 (0)