The nack package provides interceptors for implementing Negative Acknowledgment (NACK) based packet loss recovery. It includes a generator that detects lost packets and requests retransmission, and a responder that handles retransmission requests.
Overview
NACK is an RTCP feedback mechanism (RFC 4585) that allows receivers to request retransmission of lost packets. The package provides two complementary interceptors:
- GeneratorInterceptor: Detects missing packets and sends NACK requests
- ResponderInterceptor: Receives NACK requests and retransmits packets
GeneratorInterceptor
Detects lost packets on incoming RTP streams and generates NACK feedback.
Factory
type GeneratorInterceptorFactory struct {
// contains filtered or unexported fields
}
Constructor
func NewGeneratorInterceptor(opts ...GeneratorOption) (*GeneratorInterceptorFactory, error)
Creates a new NACK generator factory with the specified options.
Options
GeneratorSize
func GeneratorSize(size uint16) GeneratorOption
Sets the size of the packet history buffer.
Buffer size. Must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768. Default: 512
GeneratorSkipLastN
func GeneratorSkipLastN(skipLastN uint16) GeneratorOption
Sets the number of most recent packets to ignore when generating NACKs.
Number of packets before the last received packet to ignore. Default: 0
GeneratorMaxNacksPerPacket
func GeneratorMaxNacksPerPacket(maxNacks uint16) GeneratorOption
Sets the maximum number of times to NACK a missing packet.
Maximum NACK count per packet. 0 = unlimited. Default: 0
GeneratorInterval
func GeneratorInterval(interval time.Duration) GeneratorOption
Sets how often to send NACK feedback.
NACK send interval. Default: 100ms
WithGeneratorLoggerFactory
func WithGeneratorLoggerFactory(loggerFactory logging.LoggerFactory) GeneratorOption
Sets a custom logger factory.
GeneratorStreamsFilter
func GeneratorStreamsFilter(filter func(info *interceptor.StreamInfo) bool) GeneratorOption
Sets a filter to selectively enable NACK for specific streams.
Usage Example
import (
"time"
"github.com/pion/interceptor"
"github.com/pion/interceptor/pkg/nack"
)
// Create generator with custom options
generator, err := nack.NewGeneratorInterceptor(
nack.GeneratorSize(1024),
nack.GeneratorSkipLastN(1),
nack.GeneratorMaxNacksPerPacket(3),
nack.GeneratorInterval(100 * time.Millisecond),
)
if err != nil {
panic(err)
}
// Add to registry
registry := &interceptor.Registry{}
registry.Add(generator)
// Build interceptor
i, err := registry.Build("peer-connection-1")
if err != nil {
panic(err)
}
defer i.Close()
ResponderInterceptor
Buffers outgoing RTP packets and retransmits them when NACK feedback is received.
Factory
type ResponderInterceptorFactory struct {
// contains filtered or unexported fields
}
Constructor
func NewResponderInterceptor(opts ...ResponderOption) (*ResponderInterceptorFactory, error)
Creates a new NACK responder factory with the specified options.
Options
ResponderSize
func ResponderSize(size uint16) ResponderOption
Sets the size of the packet retransmission buffer.
Buffer size. Must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768. Default: 1024
WithResponderLoggerFactory
func WithResponderLoggerFactory(loggerFactory logging.LoggerFactory) ResponderOption
Sets a custom logger factory.
ResponderStreamsFilter
func ResponderStreamsFilter(filter func(info *interceptor.StreamInfo) bool) ResponderOption
Sets a filter to selectively enable NACK response for specific streams.
Usage Example
import (
"github.com/pion/interceptor"
"github.com/pion/interceptor/pkg/nack"
)
// Create responder with custom buffer size
responder, err := nack.NewResponderInterceptor(
nack.ResponderSize(2048),
)
if err != nil {
panic(err)
}
// Add to registry
registry := &interceptor.Registry{}
registry.Add(responder)
Complete Example: Both Sides
import (
"time"
"github.com/pion/interceptor"
"github.com/pion/interceptor/pkg/nack"
"github.com/pion/webrtc/v4"
)
func setupNACK() (*interceptor.Registry, error) {
registry := &interceptor.Registry{}
// Generator: detects lost packets and requests retransmission
generator, err := nack.NewGeneratorInterceptor(
nack.GeneratorSize(512),
nack.GeneratorInterval(100 * time.Millisecond),
)
if err != nil {
return nil, err
}
registry.Add(generator)
// Responder: retransmits packets when NACKed
responder, err := nack.NewResponderInterceptor(
nack.ResponderSize(1024),
)
if err != nil {
return nil, err
}
registry.Add(responder)
return registry, nil
}
func createPeerConnection() (*webrtc.PeerConnection, error) {
m := &webrtc.MediaEngine{}
if err := m.RegisterDefaultCodecs(); err != nil {
return nil, err
}
ir, err := setupNACK()
if err != nil {
return nil, err
}
api := webrtc.NewAPI(
webrtc.WithMediaEngine(m),
webrtc.WithInterceptorRegistry(ir),
)
return api.NewPeerConnection(webrtc.Configuration{})
}
Stream Filtering
import "strings"
// Only enable NACK for video streams
videoFilter := func(info *interceptor.StreamInfo) bool {
return strings.HasPrefix(info.MimeType, "video/")
}
generator, _ := nack.NewGeneratorInterceptor(
nack.GeneratorStreamsFilter(videoFilter),
)
responder, _ := nack.NewResponderInterceptor(
nack.ResponderStreamsFilter(videoFilter),
)
How It Works
Generator Flow
- Packet Reception: Tracks sequence numbers of incoming RTP packets
- Gap Detection: Identifies missing sequence numbers in the stream
- NACK Generation: Periodically sends NACK feedback listing missing packets
- Retry Logic: Optionally limits retransmission requests per packet
Responder Flow
- Packet Buffering: Stores outgoing RTP packets in a circular buffer
- NACK Reception: Listens for NACK feedback on RTCP reader
- Retransmission: Resends requested packets from the buffer
- Buffer Management: Automatically discards old packets
Configuration Guidelines
Buffer Sizes
- Small buffer (64-256): Low latency, limited retransmission window
- Medium buffer (512-1024): Balanced approach, good for most use cases
- Large buffer (2048+): High latency scenarios, extensive retransmission support
NACK Interval
- Fast (50-100ms): Quick recovery, more RTCP overhead
- Normal (100-200ms): Balanced recovery and overhead
- Slow (200ms+): Low overhead, slower recovery
SkipLastN
Set to 1-2 to avoid NACKing packets that may just be slightly delayed or reordered.
- Generator maintains per-SSRC packet history
- Responder buffers all outgoing packets
- Memory usage scales with buffer size
- NACK feedback sent at regular intervals (not per-packet)
See Also
Reference
For more details, see the pkg.go.dev documentation.