Learn how to compose multiple interceptors into processing pipelines using the Chain type
The Chain type allows you to combine multiple interceptors into a single interceptor that executes them in sequence. This enables you to build complex packet processing pipelines from simple, focused interceptors.
A chain executes interceptors in order, passing the output of each interceptor as the input to the next. This creates a pipeline where each interceptor can:
Modify packets before passing them to the next interceptor
// Chain is an interceptor that runs all child interceptors in order.type Chain struct { interceptors []Interceptor}// NewChain returns a new Chain interceptor.func NewChain(interceptors []Interceptor) *Chain { return &Chain{interceptors: interceptors}}
Interceptors are executed in the order they appear in the slice. The first interceptor receives packets first for local/outgoing streams, and last for remote/incoming streams due to the wrapping pattern.
For outgoing streams, interceptors process packets in the order they appear in the chain. For incoming streams, they process packets in reverse order due to the reader/writer wrapping pattern.
Place validation interceptors early to reject invalid packets:
chain := interceptor.NewChain([]interceptor.Interceptor{ &ValidationInterceptor{}, // Drop invalid packets early &ExpensiveProcessing{}, // Only process valid packets &NetworkTransmission{},})
Each interceptor in the chain adds overhead. Keep chains as short as possible by combining related functionality into single interceptors.
// Less efficient: Many small interceptorschain := interceptor.NewChain([]interceptor.Interceptor{ &PacketCounterInterceptor{}, &ByteCounterInterceptor{}, &BitrateCalculatorInterceptor{},})// More efficient: Combined interceptorchain := interceptor.NewChain([]interceptor.Interceptor{ &ComprehensiveStatsInterceptor{}, // Does all three})
Avoid unnecessary allocations
Reuse buffers and objects where possible to reduce GC pressure.
type OptimizedInterceptor struct { NoOp bufferPool sync.Pool}func (i *OptimizedInterceptor) BindLocalStream(info *StreamInfo, writer RTPWriter) RTPWriter { return RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes Attributes) (int, error) { buf := i.bufferPool.Get().([]byte) defer i.bufferPool.Put(buf) // Process with pooled buffer return writer.Write(header, payload, attributes) })}