Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
64abd7f
Add refactored GCC implementation
mengelbart Oct 3, 2025
ffcffc8
Use GCC in tests
mengelbart Oct 10, 2025
c31987a
Add better logging for synctest tests
mengelbart Oct 17, 2025
4173883
Ignore test logs
mengelbart Oct 17, 2025
2a45f2a
Log unwrapped sequence numbers
mengelbart Oct 17, 2025
86c42b8
Rename test file
mengelbart Oct 17, 2025
4b8ca2e
Use a factory function for network config
mengelbart Oct 18, 2025
0743f16
Add more parameterized test cases
mengelbart Oct 18, 2025
bdaef09
Add pacer to tests
mengelbart Oct 19, 2025
8e3ea80
Add TWCC tests
mengelbart Oct 19, 2025
4debfc6
Set queue size based bandwidth delay product
mengelbart Dec 1, 2025
a43c2ce
Add application limited tests
mengelbart Dec 1, 2025
24e014f
Fix some linter issues
mengelbart Dec 1, 2025
8d86ddb
Add more test cases
mengelbart Dec 1, 2025
408e891
Rename tests to use dash instead of underscore
mengelbart Dec 2, 2025
92e2eba
Redirect stderr output in tests to save pion logs
mengelbart Dec 2, 2025
1e4253f
Update to go 1.25
mengelbart Dec 3, 2025
1279fcd
Enable trace logging in tests
mengelbart Dec 10, 2025
b9a4088
Change logging format for easier parsing
mengelbart Dec 22, 2025
b041c37
Fix capacity limits for tests
mengelbart Dec 22, 2025
f439c9b
Add minimum queue size for small BDPs
mengelbart Dec 22, 2025
e217618
Set codec target rate below pacing rate
mengelbart Dec 22, 2025
11fad6d
Rename testcases
mengelbart Jan 28, 2026
bca4ca0
Update pion
mengelbart Jan 28, 2026
679fcad
Add arrival group accumulator logging
mengelbart Jan 28, 2026
2199f78
Fix arrival group accumulation
mengelbart Jan 28, 2026
d79255a
Reduce feedback interval
mengelbart Jan 28, 2026
efb0d52
Run go mod tidy
mengelbart Jan 28, 2026
3669baf
Remove arrival group logging
mengelbart Jan 28, 2026
7ad66c5
Move simulation test to separate package
mengelbart Jan 28, 2026
6fb5b52
Add simulation test result plots
mengelbart Oct 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
bin/
vendor/
node_modules/
logs/

### Files ###
#############
Expand All @@ -26,3 +27,7 @@ cover.out
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

*.pdf
*.png
*.pyc
6 changes: 6 additions & 0 deletions bwe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package bwe implements data structures that are common to all bandwidth
// estimators.
package bwe
85 changes: 85 additions & 0 deletions gcc/arrival_group_accumulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package gcc

import (
"time"
)

type arrivalGroupItem struct {
SequenceNumber uint64
Departure time.Time
Arrival time.Time
Size int
}

type arrivalGroup []arrivalGroupItem

type arrivalGroupAccumulator struct {
next arrivalGroup
burstInterval time.Duration
maxBurstDuration time.Duration
}

func newArrivalGroupAccumulator() *arrivalGroupAccumulator {
return &arrivalGroupAccumulator{
next: make([]arrivalGroupItem, 0),
burstInterval: 5 * time.Millisecond,
maxBurstDuration: 5 * time.Millisecond,
}
}

func (a *arrivalGroupAccumulator) onPacketAcked(
sequenceNumber uint64,
size int,
departure, arrival time.Time,
) arrivalGroup {
if len(a.next) == 0 {
a.next = append(a.next, arrivalGroupItem{
SequenceNumber: sequenceNumber,
Size: size,
Departure: departure,
Arrival: arrival,
})

return nil
}

sendTimeDelta := departure.Sub(a.next[0].Departure)
if sendTimeDelta < a.burstInterval {
a.next = append(a.next, arrivalGroupItem{
SequenceNumber: sequenceNumber,
Size: size,
Departure: departure,
Arrival: arrival,
})

return nil
}

arrivalTimeDeltaFirst := arrival.Sub(a.next[0].Arrival)
propagationDelta := arrivalTimeDeltaFirst - sendTimeDelta

if propagationDelta < 0 && arrivalTimeDeltaFirst < a.maxBurstDuration {
a.next = append(a.next, arrivalGroupItem{
SequenceNumber: sequenceNumber,
Size: size,
Departure: departure,
Arrival: arrival,
})

return nil
}

group := make(arrivalGroup, len(a.next))
copy(group, a.next)
a.next = arrivalGroup{arrivalGroupItem{
SequenceNumber: sequenceNumber,
Size: size,
Departure: departure,
Arrival: arrival,
}}

return group
}
214 changes: 214 additions & 0 deletions gcc/arrival_group_accumulator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package gcc

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestArrivalGroupAccumulator(t *testing.T) {
type logItem struct {
SequenceNumber uint64
Departure time.Time
Arrival time.Time
}
triggerNewGroupElement := logItem{
Departure: time.Time{}.Add(time.Second),
Arrival: time.Time{}.Add(time.Second),
}
cases := []struct {
name string
log []logItem
exp []arrivalGroup
}{
{
name: "emptyCreatesNoGroups",
log: []logItem{},
exp: []arrivalGroup{},
},
{
name: "createsSingleElementGroup",
log: []logItem{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(time.Millisecond),
},
triggerNewGroupElement,
},
exp: []arrivalGroup{
{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(time.Millisecond),
},
},
},
},
{
name: "createsTwoElementGroup",
log: []logItem{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
{
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(20 * time.Millisecond),
},
triggerNewGroupElement,
},
exp: []arrivalGroup{{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
{
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(20 * time.Millisecond),
},
}},
},
{
name: "createsTwoArrivalGroups1",
log: []logItem{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
{
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(20 * time.Millisecond),
},
{
Departure: time.Time{}.Add(9 * time.Millisecond),
Arrival: time.Time{}.Add(24 * time.Millisecond),
},
triggerNewGroupElement,
},
exp: []arrivalGroup{
{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
{
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(20 * time.Millisecond),
},
},
{
{
Departure: time.Time{}.Add(9 * time.Millisecond),
Arrival: time.Time{}.Add(24 * time.Millisecond),
},
},
},
},
{
name: "ignoresOutOfOrderPackets",
log: []logItem{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
{
Departure: time.Time{}.Add(6 * time.Millisecond),
Arrival: time.Time{}.Add(34 * time.Millisecond),
},
{
Departure: time.Time{}.Add(8 * time.Millisecond),
Arrival: time.Time{}.Add(30 * time.Millisecond),
},
triggerNewGroupElement,
},
exp: []arrivalGroup{
{
{
Departure: time.Time{},
Arrival: time.Time{}.Add(15 * time.Millisecond),
},
},
{
{
Departure: time.Time{}.Add(6 * time.Millisecond),
Arrival: time.Time{}.Add(34 * time.Millisecond),
},
{
Departure: time.Time{}.Add(8 * time.Millisecond),
Arrival: time.Time{}.Add(30 * time.Millisecond),
},
},
},
},
{
name: "newGroupBecauseOfInterDepartureTime",
log: []logItem{
{
SequenceNumber: 0,
Departure: time.Time{},
Arrival: time.Time{}.Add(4 * time.Millisecond),
},
{
SequenceNumber: 1,
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(4 * time.Millisecond),
},
{
SequenceNumber: 2,
Departure: time.Time{}.Add(6 * time.Millisecond),
Arrival: time.Time{}.Add(10 * time.Millisecond),
},
{
SequenceNumber: 3,
Departure: time.Time{}.Add(9 * time.Millisecond),
Arrival: time.Time{}.Add(10 * time.Millisecond),
},
triggerNewGroupElement,
},
exp: []arrivalGroup{
{
{
SequenceNumber: 0,
Departure: time.Time{},
Arrival: time.Time{}.Add(4 * time.Millisecond),
},
{
SequenceNumber: 1,
Departure: time.Time{}.Add(3 * time.Millisecond),
Arrival: time.Time{}.Add(4 * time.Millisecond),
},
},
{
{
SequenceNumber: 2,
Departure: time.Time{}.Add(6 * time.Millisecond),
Arrival: time.Time{}.Add(10 * time.Millisecond),
},
{
SequenceNumber: 3,
Departure: time.Time{}.Add(9 * time.Millisecond),
Arrival: time.Time{}.Add(10 * time.Millisecond),
},
},
},
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
aga := newArrivalGroupAccumulator()
received := []arrivalGroup{}
for _, ack := range tc.log {
next := aga.onPacketAcked(ack.SequenceNumber, 0, ack.Departure, ack.Arrival)
if next != nil {
received = append(received, next)
}
}
assert.Equal(t, tc.exp, received)
})
}
}
Loading
Loading