The GCC (Google Congestion Control) interceptor implements send-side bandwidth estimation combining both loss-based and delay-based congestion control. It dynamically adjusts the target bitrate based on network conditions.
Overview
GCC provides:
Delay-based control : Detects congestion from increasing packet delays
Loss-based control : Responds to packet loss rates
Combined estimation : Uses the minimum of both estimates
Adaptive pacing : Optional packet pacing to smooth transmission
Basic Usage
import (
" github.com/pion/interceptor/pkg/gcc "
" github.com/pion/interceptor/pkg/twcc "
)
// Create GCC bandwidth estimator
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEInitialBitrate ( 1_000_000 ), // 1 Mbps
gcc . SendSideBWEMinBitrate ( 100_000 ), // 100 Kbps
gcc . SendSideBWEMaxBitrate ( 5_000_000 ), // 5 Mbps
)
if err != nil {
panic ( err )
}
defer bwe . Close ()
// Get notified of bitrate changes
bwe . OnTargetBitrateChange ( func ( bitrate int ) {
log . Printf ( "New target bitrate: %d bps" , bitrate )
// Update encoder bitrate here
})
// Add streams to the estimator
writer := bwe . AddStream ( streamInfo , rtpWriter )
// Feed TWCC feedback to the estimator
bwe . WriteRTCP ( rtcpPackets , attributes )
Configuration Options
SendSideBWEInitialBitrate
Sets the initial target bitrate in bits per second.
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEInitialBitrate ( 1_000_000 ), // Default: 10,000 bps
)
SendSideBWEMinBitrate
Sets the minimum allowed bitrate.
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEMinBitrate ( 50_000 ), // Default: 5,000 bps
)
The estimator will never recommend a bitrate below this value, even under severe congestion.
SendSideBWEMaxBitrate
Sets the maximum allowed bitrate.
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEMaxBitrate ( 10_000_000 ), // Default: 50,000,000 bps
)
SendSideBWEPacer
Provides a custom pacer implementation for controlling packet transmission rate.
import " github.com/pion/interceptor/pkg/gcc "
// Use leaky bucket pacer (default)
pacer := gcc . NewLeakyBucketPacer ( initialBitrate , loggerFactory )
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEPacer ( pacer ),
)
If no pacer is specified, GCC uses a leaky bucket pacer by default.
WithLoggerFactory
Sets a custom logger factory for debugging.
bwe , err := gcc . NewSendSideBWE (
gcc . WithLoggerFactory ( myLoggerFactory ),
)
Complete Example
package main
import (
" log "
" github.com/pion/interceptor "
" github.com/pion/interceptor/pkg/gcc "
" github.com/pion/interceptor/pkg/twcc "
" github.com/pion/webrtc/v4 "
)
func main () {
// Create media engine
m := & webrtc . MediaEngine {}
if err := m . RegisterDefaultCodecs (); err != nil {
panic ( err )
}
// Create interceptor registry
i := & interceptor . Registry {}
// Register TWCC for feedback
if err := webrtc . ConfigureTWCCHeaderExtensionSender ( m , i ); err != nil {
panic ( err )
}
twccFactory , err := twcc . NewSenderInterceptor ()
if err != nil {
panic ( err )
}
i . Add ( twccFactory )
// Create GCC bandwidth estimator
bwe , err := gcc . NewSendSideBWE (
gcc . SendSideBWEInitialBitrate ( 1_000_000 ),
gcc . SendSideBWEMinBitrate ( 100_000 ),
gcc . SendSideBWEMaxBitrate ( 5_000_000 ),
)
if err != nil {
panic ( err )
}
defer bwe . Close ()
// Handle bitrate changes
bwe . OnTargetBitrateChange ( func ( bitrate int ) {
log . Printf ( "Target bitrate changed to: %d bps ( %.2f Mbps)" ,
bitrate , float64 ( bitrate ) / 1_000_000 )
// Update your encoder's target bitrate here
// encoder.SetBitrate(bitrate)
})
// Get current statistics
stats := bwe . GetStats ()
log . Printf ( "BWE Stats: %+v " , stats )
// Get current target bitrate
currentBitrate := bwe . GetTargetBitrate ()
log . Printf ( "Current target: %d bps" , currentBitrate )
}
How It Works
Delay-Based Estimation
Arrival Time Tracking : Records when packets arrive
Gradient Calculation : Computes delay gradients using Kalman filter
Overuse Detection : Detects when delay is increasing (congestion)
Rate Adjustment : Decreases bitrate during congestion, increases when stable
Loss-Based Estimation
Loss Rate Calculation : Monitors packet loss percentage
Threshold Detection : Compares loss rate against thresholds
Multiplicative Decrease : Reduces bitrate proportionally to loss
Additive Increase : Slowly increases bitrate when loss is low
Combined Control
// GCC uses the minimum of both estimates
targetBitrate = min ( delayBasedEstimate , lossBasedEstimate )
The delay controller responds quickly to congestion onset, while the loss controller prevents persistent overuse.
Getting Statistics
GCC provides detailed internal statistics:
stats := bwe . GetStats ()
for key , value := range stats {
log . Printf ( " %s : %v " , key , value )
}
// Available statistics:
// - lossTargetBitrate: Bitrate from loss-based controller
// - averageLoss: Average packet loss rate
// - delayTargetBitrate: Bitrate from delay-based controller
// - delayMeasurement: Current delay measurement (ms)
// - delayEstimate: Estimated delay (ms)
// - delayThreshold: Overuse threshold (ms)
// - usage: Network usage state (Normal/Over/Under)
// - state: Controller state (Hold/Increase/Decrease)
Working with TWCC
GCC requires transport-wide congestion control feedback:
import (
" github.com/pion/interceptor/pkg/gcc "
" github.com/pion/interceptor/pkg/twcc "
)
// Setup TWCC header extension
if err := webrtc . ConfigureTWCCHeaderExtensionSender ( m , i ); err != nil {
panic ( err )
}
// Add TWCC sender interceptor
twccFactory , err := twcc . NewSenderInterceptor ()
if err != nil {
panic ( err )
}
i . Add ( twccFactory )
// Create GCC
bwe , err := gcc . NewSendSideBWE ()
if err != nil {
panic ( err )
}
// Feed RTCP feedback to GCC
rtcpReader := interceptor . RTCPReaderFunc ( func ( b [] byte , a interceptor . Attributes ) ( int , interceptor . Attributes , error ) {
n , attr , err := reader . Read ( b , a )
if err != nil {
return 0 , nil , err
}
// Parse RTCP packets
pkts , err := rtcp . Unmarshal ( b [: n ])
if err != nil {
return n , attr , err
}
// Feed to bandwidth estimator
if err := bwe . WriteRTCP ( pkts , attr ); err != nil {
log . Printf ( "Error writing RTCP: %v " , err )
}
return n , attr , nil
})
Set based on expected network conditions:
Low latency networks : Start higher (2-5 Mbps)
Mobile networks : Start conservative (500 Kbps - 1 Mbps)
Unknown networks : Use default (1 Mbps)
Set appropriate bounds for your use case:
Voice : 50-100 Kbps min, 500 Kbps max
Video (SD) : 200 Kbps min, 2 Mbps max
Video (HD) : 500 Kbps min, 5 Mbps max
Video (4K) : 2 Mbps min, 20 Mbps max
GCC adapts within:
Congestion detection : 100-500ms
Bitrate increase : 1-5 seconds
Bitrate decrease : Immediate (100-200ms)
Best Practices
Monitor Callbacks : Always implement OnTargetBitrateChange to adjust your encoder
Set Bounds : Define realistic min/max bitrates for your content
Check Stats : Periodically log statistics to understand network behavior
Combine with FEC : Use with NACK or FlexFEC for resilience
Start Conservative : Better to start low and ramp up than overwhelm the network
GCC requires TWCC feedback to function. Ensure both sender and receiver support the Transport-CC RTP header extension.
TWCC - Required for providing feedback to GCC
Pacing - Alternative pacing implementation
Stats - Monitor bandwidth usage and performance