@@ -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"
@@ -284,3 +286,146 @@ func Test_Interceptor_ZeroSSRC(t *testing.T) {
284
286
<- probeReceiverCreated
285
287
closePairNow (t , offerer , answerer )
286
288
}
289
+
290
+ // TestInterceptorNack is an end-to-end test for the NACK sender.
291
+ // It test that:
292
+ // - we get a NACK if we negotiated generic NACks;
293
+ // - we don't get a NACK if we did not negotiate generick NACKs;
294
+ // - the NACK corresponds to the missing packet.
295
+ func TestInterceptorNack (t * testing.T ) {
296
+ const numPackets = 20
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
+ ir := interceptor.Registry {}
306
+ m := MediaEngine {}
307
+ var capability []RTCPFeedback
308
+ if requestNack {
309
+ capability = append (capability , RTCPFeedback {"nack" , "" })
310
+ }
311
+ err := m .RegisterCodec (
312
+ RTPCodecParameters {
313
+ RTPCodecCapability : RTPCodecCapability {
314
+ "video/VP8" , 90000 , 0 ,
315
+ "" ,
316
+ capability ,
317
+ },
318
+ PayloadType : 96 ,
319
+ },
320
+ RTPCodecTypeVideo ,
321
+ )
322
+ assert .NoError (t , err )
323
+ api := NewAPI (
324
+ WithMediaEngine (& m ),
325
+ WithInterceptorRegistry (& ir ),
326
+ )
327
+
328
+ pc1 , err := api .NewPeerConnection (Configuration {})
329
+ assert .NoError (t , err )
330
+ defer pc1 .Close ()
331
+
332
+ track1 , err := NewTrackLocalStaticRTP (
333
+ RTPCodecCapability {MimeType : MimeTypeVP8 },
334
+ "video" , "pion" ,
335
+ )
336
+ assert .NoError (t , err )
337
+ sender , err := pc1 .AddTrack (track1 )
338
+ assert .NoError (t , err )
339
+
340
+ pc2 , err := NewPeerConnection (Configuration {})
341
+ assert .NoError (t , err )
342
+ defer pc2 .Close ()
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
+ gotNack := false
362
+ go func () {
363
+ buf := make ([]byte , 1500 )
364
+ for {
365
+ n , _ , err := sender .Read (buf )
366
+ if err == io .EOF {
367
+ break
368
+ }
369
+ assert .NoError (t , err )
370
+ ps , err := rtcp .Unmarshal (buf [:n ])
371
+ assert .NoError (t , err )
372
+ for _ , p := range ps {
373
+ if pn , ok := p .(* rtcp.TransportLayerNack ); ok {
374
+ assert .Equal (t , len (pn .Nacks ), 1 )
375
+ assert .Equal (t ,
376
+ pn .Nacks [0 ].PacketID , uint16 (1 ),
377
+ )
378
+ assert .Equal (t ,
379
+ pn .Nacks [0 ].LostPackets ,
380
+ rtcp .PacketBitmap (0 ),
381
+ )
382
+ gotNack = true
383
+ }
384
+ }
385
+ }
386
+ }()
387
+
388
+ const numPackets = 20
389
+ done := make (chan struct {})
390
+ pc2 .OnTrack (func (track2 * TrackRemote , receiver * RTPReceiver ) {
391
+ for i := 0 ; i < numPackets ; i ++ {
392
+ if i == 1 {
393
+ continue
394
+ }
395
+ p , _ , err := track2 .ReadRTP ()
396
+ assert .NoError (t , err )
397
+ assert .Equal (t , p .SequenceNumber , uint16 (i ))
398
+ }
399
+ done <- struct {}{}
400
+ })
401
+
402
+ go func () {
403
+ for i := 0 ; i < numPackets ; i ++ {
404
+ time .Sleep (20 * time .Millisecond )
405
+ if i == 1 {
406
+ continue
407
+ }
408
+ var p rtp.Packet
409
+ p .Version = 2
410
+ p .Marker = true
411
+ p .PayloadType = 96
412
+ p .SequenceNumber = uint16 (i )
413
+ p .Timestamp = uint32 (i * 90000 / 50 )
414
+ p .Payload = []byte {42 }
415
+ err := track1 .WriteRTP (& p )
416
+ assert .NoError (t , err )
417
+ }
418
+ }()
419
+
420
+ <- done
421
+
422
+ if requestNack {
423
+ if ! gotNack {
424
+ t .Errorf ("Expected to get a NACK, got none" )
425
+ }
426
+ } else {
427
+ if gotNack {
428
+ t .Errorf ("Expected to get no NACK, got one" )
429
+ }
430
+ }
431
+ }
0 commit comments