Skip to content

Commit 92fce5f

Browse files
committed
Add an end-to-end test for the NACK sender
Test that NACKs are negotiated correctly, and that we receive the expected NACK if we negotiated it.
1 parent 92d573c commit 92fce5f

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

interceptor_test.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ package webrtc
99
//
1010
import (
1111
"context"
12+
"io"
1213
"sync/atomic"
1314
"testing"
1415
"time"
1516

1617
"github.com/pion/interceptor"
1718
mock_interceptor "github.com/pion/interceptor/pkg/mock"
19+
"github.com/pion/rtcp"
1820
"github.com/pion/rtp"
1921
"github.com/pion/transport/v3/test"
2022
"github.com/pion/webrtc/v4/pkg/media"
@@ -285,3 +287,152 @@ func Test_Interceptor_ZeroSSRC(t *testing.T) {
285287
<-probeReceiverCreated
286288
closePairNow(t, offerer, answerer)
287289
}
290+
291+
// TestInterceptorNack is an end-to-end test for the NACK sender.
292+
// It test that:
293+
// - we get a NACK if we negotiated generic NACks;
294+
// - we don't get a NACK if we did not negotiate generick NACKs;
295+
// - the NACK corresponds to the missing packet.
296+
func TestInterceptorNack(t *testing.T) {
297+
to := test.TimeOut(time.Second * 20)
298+
defer to.Stop()
299+
300+
t.Run("Nack", func(t *testing.T) { testInterceptorNack(t, true) })
301+
t.Run("NoNack", func(t *testing.T) { testInterceptorNack(t, false) })
302+
}
303+
304+
func testInterceptorNack(t *testing.T, requestNack bool) {
305+
const numPackets = 20
306+
307+
ir := interceptor.Registry{}
308+
m := MediaEngine{}
309+
var capability []RTCPFeedback
310+
if requestNack {
311+
capability = append(capability, RTCPFeedback{"nack", ""})
312+
}
313+
err := m.RegisterCodec(
314+
RTPCodecParameters{
315+
RTPCodecCapability: RTPCodecCapability{
316+
"video/VP8", 90000, 0,
317+
"",
318+
capability,
319+
},
320+
PayloadType: 96,
321+
},
322+
RTPCodecTypeVideo,
323+
)
324+
assert.NoError(t, err)
325+
api := NewAPI(
326+
WithMediaEngine(&m),
327+
WithInterceptorRegistry(&ir),
328+
)
329+
330+
pc1, err := api.NewPeerConnection(Configuration{})
331+
assert.NoError(t, err)
332+
333+
track1, err := NewTrackLocalStaticRTP(
334+
RTPCodecCapability{MimeType: MimeTypeVP8},
335+
"video", "pion",
336+
)
337+
assert.NoError(t, err)
338+
sender, err := pc1.AddTrack(track1)
339+
assert.NoError(t, err)
340+
341+
pc2, err := NewPeerConnection(Configuration{})
342+
assert.NoError(t, err)
343+
344+
offer, err := pc1.CreateOffer(nil)
345+
assert.NoError(t, err)
346+
err = pc1.SetLocalDescription(offer)
347+
assert.NoError(t, err)
348+
<-GatheringCompletePromise(pc1)
349+
350+
err = pc2.SetRemoteDescription(*pc1.LocalDescription())
351+
assert.NoError(t, err)
352+
answer, err := pc2.CreateAnswer(nil)
353+
assert.NoError(t, err)
354+
err = pc2.SetLocalDescription(answer)
355+
assert.NoError(t, err)
356+
<-GatheringCompletePromise(pc2)
357+
358+
err = pc1.SetRemoteDescription(*pc2.LocalDescription())
359+
assert.NoError(t, err)
360+
361+
var gotNack bool
362+
rtcpDone := make(chan struct{})
363+
go func() {
364+
defer close(rtcpDone)
365+
buf := make([]byte, 1500)
366+
for {
367+
n, _, err2 := sender.Read(buf)
368+
// nolint
369+
if err2 == io.EOF {
370+
break
371+
}
372+
assert.NoError(t, err2)
373+
ps, err2 := rtcp.Unmarshal(buf[:n])
374+
assert.NoError(t, err2)
375+
for _, p := range ps {
376+
if pn, ok := p.(*rtcp.TransportLayerNack); ok {
377+
assert.Equal(t, len(pn.Nacks), 1)
378+
assert.Equal(t,
379+
pn.Nacks[0].PacketID, uint16(1),
380+
)
381+
assert.Equal(t,
382+
pn.Nacks[0].LostPackets,
383+
rtcp.PacketBitmap(0),
384+
)
385+
gotNack = true
386+
}
387+
}
388+
}
389+
}()
390+
391+
done := make(chan struct{})
392+
pc2.OnTrack(func(track2 *TrackRemote, _ *RTPReceiver) {
393+
for i := 0; i < numPackets; i++ {
394+
if i == 1 {
395+
continue
396+
}
397+
p, _, err2 := track2.ReadRTP()
398+
assert.NoError(t, err2)
399+
assert.Equal(t, p.SequenceNumber, uint16(i))
400+
}
401+
close(done)
402+
})
403+
404+
go func() {
405+
for i := 0; i < numPackets; i++ {
406+
time.Sleep(20 * time.Millisecond)
407+
if i == 1 {
408+
continue
409+
}
410+
var p rtp.Packet
411+
p.Version = 2
412+
p.Marker = true
413+
p.PayloadType = 96
414+
p.SequenceNumber = uint16(i)
415+
p.Timestamp = uint32(i * 90000 / 50)
416+
p.Payload = []byte{42}
417+
err2 := track1.WriteRTP(&p)
418+
assert.NoError(t, err2)
419+
}
420+
}()
421+
422+
<-done
423+
err = pc1.Close()
424+
assert.NoError(t, err)
425+
err = pc2.Close()
426+
assert.NoError(t, err)
427+
<-rtcpDone
428+
429+
if requestNack {
430+
if !gotNack {
431+
t.Errorf("Expected to get a NACK, got none")
432+
}
433+
} else {
434+
if gotNack {
435+
t.Errorf("Expected to get no NACK, got one")
436+
}
437+
}
438+
}

0 commit comments

Comments
 (0)