Skip to content

Commit

Permalink
Bolting on send side simulcast
Browse files Browse the repository at this point in the history
Introduces AddEncoding method in RTP sender to add simulcast encodings.

Added UTs for AddEncoding.
Also modified the Simulcast send test to use the new API.
  • Loading branch information
boks1971 authored and davidzhao committed Feb 25, 2022
1 parent e2b8d4c commit 37e16a3
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 101 deletions.
17 changes: 13 additions & 4 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ var (
// ErrRTPSenderNewTrackHasIncorrectKind indicates that the new track is of a different kind than the previous/original
ErrRTPSenderNewTrackHasIncorrectKind = errors.New("new track must be of the same kind as previous")

// ErrRTPSenderNewTrackHasIncorrectEnvelope indicates that the new track has a different envelope than the previous/original
ErrRTPSenderNewTrackHasIncorrectEnvelope = errors.New("new track must have the same envelope as previous")

// ErrUnbindFailed indicates that a TrackLocal was not able to be unbind
ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection")

Expand Down Expand Up @@ -202,10 +205,16 @@ var (
errRTPReceiverWithSSRCTrackStreamNotFound = errors.New("unable to find stream for Track with SSRC")
errRTPReceiverForRIDTrackStreamNotFound = errors.New("no trackStreams found for RID")

errRTPSenderTrackNil = errors.New("Track must not be nil")
errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil")
errRTPSenderSendAlreadyCalled = errors.New("Send has already been called")
errRTPSenderTrackRemoved = errors.New("Sender Track has been removed or replaced to nil")
errRTPSenderTrackNil = errors.New("Track must not be nil")
errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil")
errRTPSenderSendAlreadyCalled = errors.New("Send has already been called")
errRTPSenderStopped = errors.New("Sender has already been stopped")
errRTPSenderTrackRemoved = errors.New("Sender Track has been removed or replaced to nil")
errRTPSenderRidNil = errors.New("Sender cannot add encoding as rid is empty")
errRTPSenderNoBaseEncoding = errors.New("Sender cannot add encoding as there is no base track")
errRTPSenderBaseEncodingMismatch = errors.New("Sender cannot add encoding as provided track does not match base track")
errRTPSenderRIDCollision = errors.New("Sender cannot encoding due to RID collision")
errRTPSenderNoTrackForRID = errors.New("Sender does not have track for RID")

errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil")
errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending")
Expand Down
65 changes: 49 additions & 16 deletions peerconnection_media_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func TestPeerConnection_Media_Sample(t *testing.T) {
go func() {
for {
time.Sleep(time.Millisecond * 100)
if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{SenderSSRC: uint32(sender.ssrc), MediaSSRC: uint32(sender.ssrc)}}); routineErr != nil {
if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{SenderSSRC: uint32(sender.trackEncodings[0].ssrc), MediaSSRC: uint32(sender.trackEncodings[0].ssrc)}}); routineErr != nil {
awaitRTCPSenderSend <- routineErr
}

Expand Down Expand Up @@ -643,12 +643,12 @@ func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
track1, sender1 := addTrack()
assert.Equal(t, 1, len(pc.GetTransceivers()))
assert.Equal(t, sender1, tr.Sender())
assert.Equal(t, track1, tr.Sender().track)
assert.Equal(t, track1, tr.Sender().Track())
require.NoError(t, pc.RemoveTrack(sender1))

track2, _ := addTrack()
assert.Equal(t, 1, len(pc.GetTransceivers()))
assert.Equal(t, track2, tr.Sender().track)
assert.Equal(t, track2, tr.Sender().Track())

addTrack()
assert.Equal(t, 2, len(pc.GetTransceivers()))
Expand Down Expand Up @@ -1256,23 +1256,47 @@ func TestPeerConnection_Simulcast(t *testing.T) {
pcOffer, pcAnswer, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
assert.NoError(t, err)

vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("a"))
assert.NoError(t, err)

_, err = pcOffer.AddTrack(vp8Writer)
sender, err := pcOffer.AddTrack(vp8WriterA)
assert.NoError(t, err)
assert.NotNil(t, sender)

vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("b"))
assert.NoError(t, err)
err = sender.AddEncoding(vp8WriterB)
assert.NoError(t, err)

vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("c"))
assert.NoError(t, err)
err = sender.AddEncoding(vp8WriterC)
assert.NoError(t, err)

ridMap = map[string]int{}
pcAnswer.OnTrack(onTrackHandler)

assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
sessionDescription = strings.Split(sessionDescription, "a=end-of-candidates\r\n")[0]
sessionDescription = filterSsrc(sessionDescription)
for _, rid := range rids {
sessionDescription += "a=" + sdpAttributeRid + ":" + rid + " send\r\n"
parameters := sender.GetParameters()
assert.Equal(t, "a", parameters.Encodings[0].RID)
assert.Equal(t, "b", parameters.Encodings[1].RID)
assert.Equal(t, "c", parameters.Encodings[2].RID)

var midID, ridID, rsidID uint8
for _, extension := range parameters.HeaderExtensions {
switch extension.URI {
case sdp.SDESMidURI:
midID = uint8(extension.ID)
case sdp.SDESRTPStreamIDURI:
ridID = uint8(extension.ID)
case sdesRepairRTPStreamIDURI:
rsidID = uint8(extension.ID)
}
return sessionDescription + "a=simulcast:send " + strings.Join(rids, ";") + "\r\n"
}))
}
assert.NotZero(t, midID)
assert.NotZero(t, ridID)
assert.NotZero(t, rsidID)

assert.NoError(t, signalPair(pcOffer, pcAnswer))

for sequenceNumber := uint16(0); !ridsFullfilled(); sequenceNumber++ {
time.Sleep(20 * time.Millisecond)
Expand All @@ -1284,17 +1308,26 @@ func TestPeerConnection_Simulcast(t *testing.T) {
SequenceNumber: sequenceNumber,
PayloadType: 96,
}
assert.NoError(t, header.SetExtension(1, []byte("0")))
assert.NoError(t, header.SetExtension(midID, []byte("0")))

// Send RSID for first 10 packets
if sequenceNumber >= 10 {
assert.NoError(t, header.SetExtension(2, []byte(rid)))
assert.NoError(t, header.SetExtension(ridID, []byte(rid)))
} else {
assert.NoError(t, header.SetExtension(3, []byte(rid)))
assert.NoError(t, header.SetExtension(rsidID, []byte(rid)))
header.SSRC += 10
}

_, err := vp8Writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00})
var writer *TrackLocalStaticRTP
switch rid {
case "a":
writer = vp8WriterA
case "b":
writer = vp8WriterB
case "c":
writer = vp8WriterC
}
_, err = writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00})
assert.NoError(t, err)
}
}
Expand Down
8 changes: 4 additions & 4 deletions peerconnection_renegotiation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) {
// Must have 3 media descriptions (2 video channels)
assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)

assert.True(t, sdpMidHasSsrc(offer, "0", sender1.ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "0", sender1.ssrc, offer.SDP)
assert.True(t, sdpMidHasSsrc(offer, "0", sender1.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "0", sender1.trackEncodings[0].ssrc, offer.SDP)

// Remove first track, must keep same number of media
// descriptions and same track ssrc for mid 1 as previous
Expand All @@ -382,7 +382,7 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) {

assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)

assert.True(t, sdpMidHasSsrc(offer, "1", sender2.ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "1", sender2.ssrc, offer.SDP)
assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP)

_, err = pcAnswer.CreateAnswer(nil)
assert.Equal(t, err, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState})
Expand All @@ -402,8 +402,8 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) {
// We reuse the existing non-sending transceiver
assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)

assert.True(t, sdpMidHasSsrc(offer, "0", sender3.ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "0", sender3.ssrc, offer.SDP)
assert.True(t, sdpMidHasSsrc(offer, "1", sender2.ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "1", sender2.ssrc, offer.SDP)
assert.True(t, sdpMidHasSsrc(offer, "0", sender3.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "0", sender3.trackEncodings[0].ssrc, offer.SDP)
assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP)

closePairNow(t, pcOffer, pcAnswer)
}
Expand Down
Loading

0 comments on commit 37e16a3

Please sign in to comment.