@@ -9,12 +9,14 @@ package webrtc
99//
1010import (
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