@@ -9,12 +9,14 @@ package webrtc
9
9
//
10
10
import (
11
11
"context"
12
+ "io"
12
13
"sync/atomic"
13
14
"testing"
14
15
"time"
15
16
16
17
"github.com/pion/interceptor"
17
18
mock_interceptor "github.com/pion/interceptor/pkg/mock"
19
+ "github.com/pion/rtcp"
18
20
"github.com/pion/rtp"
19
21
"github.com/pion/transport/v3/test"
20
22
"github.com/pion/webrtc/v4/pkg/media"
@@ -285,3 +287,152 @@ func Test_Interceptor_ZeroSSRC(t *testing.T) {
285
287
<- probeReceiverCreated
286
288
closePairNow (t , offerer , answerer )
287
289
}
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