Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
48edde8
Add refactored GCC implementation
mengelbart Oct 3, 2025
7b43797
Use GCC in tests
mengelbart Oct 10, 2025
cadddba
Add better logging for synctest tests
mengelbart Oct 17, 2025
87a9f9c
Ignore test logs
mengelbart Oct 17, 2025
db75483
Log unwrapped sequence numbers
mengelbart Oct 17, 2025
604903e
Rename test file
mengelbart Oct 17, 2025
b0ea215
Use a factory function for network config
mengelbart Oct 18, 2025
13d2cd0
Add more parameterized test cases
mengelbart Oct 18, 2025
b5a3642
Add pacer to tests
mengelbart Oct 19, 2025
56b17b2
Add TWCC tests
mengelbart Oct 19, 2025
a3ece07
Set queue size based bandwidth delay product
mengelbart Dec 1, 2025
e1e8702
Add application limited tests
mengelbart Dec 1, 2025
b7a656f
Fix some linter issues
mengelbart Dec 1, 2025
2f8949a
Add more test cases
mengelbart Dec 1, 2025
4e79616
Rename tests to use dash instead of underscore
mengelbart Dec 2, 2025
494ef87
Redirect stderr output in tests to save pion logs
mengelbart Dec 2, 2025
30cbbab
Update to go 1.25
mengelbart Dec 3, 2025
b586375
Enable trace logging in tests
mengelbart Dec 10, 2025
6131cfa
Change logging format for easier parsing
mengelbart Dec 22, 2025
fd691ee
Fix capacity limits for tests
mengelbart Dec 22, 2025
31b51cc
Add minimum queue size for small BDPs
mengelbart Dec 22, 2025
66c93ee
Set codec target rate below pacing rate
mengelbart Dec 22, 2025
c480aae
Rename testcases
mengelbart Jan 28, 2026
7d83dbe
Update pion
mengelbart Jan 28, 2026
7a6e1b5
Add arrival group accumulator logging
mengelbart Jan 28, 2026
4ec1728
Fix arrival group accumulation
mengelbart Jan 28, 2026
33e8a35
Reduce feedback interval
mengelbart Jan 28, 2026
bf3f97c
Run go mod tidy
mengelbart Jan 28, 2026
6db05d3
Remove arrival group logging
mengelbart Jan 28, 2026
e4784c2
Move simulation test to separate package
mengelbart Jan 28, 2026
b2f256e
Add simulation test result plots
mengelbart Oct 17, 2025
56d942d
Convert logger option to logger factory option
mengelbart Jan 31, 2026
02935d6
Remove unnecessary log
mengelbart Jan 31, 2026
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