Commit Graph
3813 Commits
Author SHA1 Message Date
David Chen a37a2d64fe revert log additions 2026-07-17 14:10:07 -07:00
David Chen fd361b967c remove else if 2026-07-17 13:10:13 -07:00
David Chen da0379adf6 adding observability metrics for frame metadata usage 2026-07-17 13:02:19 -07:00
Raja SubramanianandGitHub 38d7efca83 Record rtc_success prom as soon as RTC connects. (#4677)
Was doing it on participant close. But, that misaligns `rtc_init`
(number of attempts) a bit if the participant sessions are long.
Record it as soon as RTC connects so that it is more time aligned with
`rtc_init`.
2026-07-16 12:49:27 +05:30
cce14a6fec fix: don't undercount data channel bitrate over idle gaps (#4676)
* fix: don't undercount data channel bitrate over idle gaps

BitrateCalculator divided drained bytes by wall-clock time, including
idle gaps between sparse writes. A fast channel fed sparsely (e.g. 1 KB
every second, acked in a few ms) therefore reported its low offered load
(~8 kbps) instead of its drain capacity (~800 kbps). In the unreliable
writer that shrinks targetLatencyLimit and drops bursts on a channel
that is actually keeping up.

Measure the rate over backlogged ("busy") time only, so idle time no
longer dilutes the estimate. Backlog is detected from the buffer
occupancy just before a write (bufferedAmount - bytesWritten): since no
bytes are added between writes the buffer only drains, so a non-zero
pre-write level means it never emptied and the interval was genuinely
busy. Gating on the post-write buffered amount would be wrong -- it
always includes the just-enqueued (not yet SACKed) bytes and is ~never
zero, collapsing the estimate back to wall-clock. When no backlog is
ever observed the calculator reports no estimate (ok=false) instead of a
confidently-low number.

Introduce BitrateMode:
- BusyOnly (writer default): busy denominator, all drained bytes counted.
- ExcludeIdleDrain: also drops bytes drained across non-backlogged
  intervals for a clean drain-capacity estimate free of idle
  contamination.
- WallClock: elapsed-time denominator, all bytes; for the test-client
  reader which has no send buffer to observe.

Call sites keep the conservative behaviour (writers BusyOnly, reader
WallClock). Tests exercise both writer modes and the mixed idle-then-
backlog window where they diverge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Trim comments in data channel bitrate calculator

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Make BitrateMode.String a method on the enum

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 00:29:23 +05:30
Alex FishandGitHub cc87a83e1f TEL-730: Log soft validation errors. (#4671) 2026-07-15 09:06:01 -07:00
dcd08bec63 Fix goroutine leak from orphaned signal relay streams (#4674)
* Fix goroutine leak from orphaned signal relay streams

signalService.RelaySignal blocks on the first `<-stream.Channel()` waiting for the StartSession message. psrpc's streamHandler.handleOpenRequest only closes the stream after the handler returns, so if a stream is opened but the client goes away before sending StartSession, the channel is never fed and never closed, and this goroutine blocks forever. Under mass reconnects this leaks one goroutine (and its retained objects) per orphaned stream; they only clear on process restart.

Wrap the initial receive in a select that also returns when the stream context is cancelled or after config.SignalRelay.RetryTimeout, so an orphaned stream returns before Hijack() and psrpc closes it.

Signed-off-by: SKaterinenko <skaterinenko@gmail.com>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Paul Wells <paulwe@gmail.com>
2026-07-15 05:41:16 -07:00
Raja SubramanianandGitHub 0c7b93d39a Add more details to unreliable data channel drop error. (#4673)
Some data track related tests are failing due to setting a target
latency of 100ms. Would be good to understand if taget latency or min
buffer is causing the drops.
2026-07-15 17:12:39 +05:30
Raja SubramanianandGitHub d4888f7d2a Update pion/ice to deal with hang on close. (#4672)
Potential cause of #4549, #4591
2026-07-15 14:14:49 +05:30
Florian LoretanandGitHub 7818dd21b9 fix: bound data-track buffering under downlink congestion (targetLatency) (#4667)
* fix: bound data-track buffering under downlink congestion

The SFU data-track down-channel writer was constructed with targetLatency=0,
which disables the buffered-amount/latency-based drop in writeUnreliable. Under
subscriber downlink congestion, frames were queued unbounded and in order in the
per-subscriber SCTP send buffer instead of being dropped, so end-to-end latency
grew without limit and only drained once congestion cleared. This contradicts
data tracks being a low-latency, lossy transport.

Wire a dedicated `datachannel_data_track_target_latency` config through to both
data-track writer call sites (mirroring how `datachannel_lossy_target_latency`
controls the lossy publishData channel), reusing lossyDataChannelMinBufferedAmount
as the drop floor. Defaults to 100ms so data tracks are latency-bounded out of
the box; set to 0 to restore the previous unbounded behavior.

Fixes #4666

* fix: track drained bytes on successful unreliable data channel writes

writeUnreliable only called BitrateCalculator.AddBytes when a write failed,
so on the common success path the calculator never received samples. As a
result Bitrate() had no data and the latency-based drop threshold collapsed to
the static minBufferedAmount floor, making the per-subscriber latency control
(datachannel_lossy_target_latency and the new datachannel_data_track_target_latency)
effectively inert. Call AddBytes after every write, matching writeReliable.
2026-07-14 10:06:09 +08:00
lukasIOandGitHub 1c89e727a6 Add drop-on-close signal test scenario (#4669) 2026-07-13 18:00:01 +02:00
2c9eee4c54 Guard against nil TC qdisc stats to prevent SIGSEGV (#4668)
Newer kernels report qdisc stats via TCA_STATS2 (Stats2) while older
kernels only populate the legacy TCA_STATS attribute (Stats). Whichever
is unused is left nil, so unconditionally dereferencing Stats crashed
with a SIGSEGV during telemetry init. Prefer Stats2 and fall back to
Stats, skipping when both are nil.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:30:44 +02:00
Raja SubramanianandGitHub 2bd3b9d67f Add a method to get forward stats via API method. (#4664)
Use by cloud simulated tracks.
Introduces a lock, but it is used only in the background worker (where
it will be almost always uncontested) and when API GetStats is accessed.
Does not touch the per-packet Update path.
2026-07-12 21:46:14 +05:30
Raja SubramanianandGitHub 788b01bc5c protocol deps to get parallel exec alloc reduction (#4662) 2026-07-12 14:34:12 +05:30
345bc5eeb0 Move ForwardStats aggregation off the packet forwarding path (#4660)
* Move ForwardStats aggregation off the packet forwarding path

ForwardStats is a process-wide singleton and its Update runs for every
forwarded packet. It previously took a shared mutex, updated a windowed
aggregate, and observed into a global Prometheus histogram on every call.

Update now only buffers the transit sample into a sharded lock-free ring
(one atomic add to reserve a slot, one atomic store to publish). A
background worker drains the ring every summary interval, observes each
sample into the histogram (per-packet fidelity retained) and folds the
interval summary into a window ring for the latency/jitter gauges. Under
sustained overload the oldest excess samples are dropped and counted, and
the count is logged.

Ring slots are atomic.Int64 so producer store / consumer load are
synchronized (race-clean). Capacity is numShards*shardCap = 131072
samples; at the default 50ms summary interval that sustains ~2.6M
samples/s before dropping.

Benchmark: BenchmarkForwardStatsUpdate (0 allocs/op both), Update per call:

  cores   before      after     speedup
  1       ~8.6 ns     ~1.5 ns   ~6x
  8       ~175 ns     ~22 ns    ~8x

The before path (mutex + windowed Welford aggregate + per-packet histogram
observe) degrades ~20x from 1 to 8 cores under contention; the after path
does not block and stays an order of magnitude lower under load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix ring publish race and report-path double-flush

Addresses two review findings on the forward-stats sample buffer.

1. Publish race (reserve-before-store): push reserved a ring slot by
   advancing writeIdx and stored the value afterwards, so drain could read a
   slot the producer had reserved but not yet written, getting a stale/zero
   value; the producer's later store then landed behind the read cursor and
   was lost. Each slot now carries a publish epoch. push stores the value and
   then publishes seq = index+1. drain reads a slot only when seq == r+1; a
   reserved-but-unpublished slot at the cursor stops the drain and is picked
   up on the next call, so no sample is read stale or lost. A slot overwritten
   during the read is detected on re-check and counted as dropped.

   The windowed latency/jitter stats did not permanently drift even before
   this change (report recomputes from a fixed-size ring that overwrites, not
   discounts, old buckets, so any error aged out within the report window),
   but these values feed the capacity manager, so the buffer is made exact.

2. Report-path double-flush: run() flushed on both the summary tick and the
   report tick, and every flush advances the window ring, so the ring cycled
   faster than intended and the effective window was shorter than configured
   (~5% at 50ms/1s/1m). The report tick no longer flushes; the summary ticker
   keeps the ring current to within one summary interval.

Benchmark, Update per call (0 allocs/op), 1 and 8 cores:

  after fixes:                              ~5.5 ns / ~29 ns
  before fixes (this branch):               ~1.5 ns / ~22 ns
  baseline (mutex + per-packet histogram):  ~8.6 ns / ~175 ns

The publish epoch adds one atomic store to push; the path stays non-blocking,
zero-allocation, and well below the baseline under contention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 12:46:46 +05:30
Raja SubramanianandGitHub 44323799bc Omit rtx/out-of-order packets from forwarding delay measurement. (#4659)
They could inflate because of the burst of NACK responses living in the
queue for a longer time.
2026-07-11 16:18:57 +05:30
Raja SubramanianandGitHub 424c7a602a Record final RTC state as success/failure. (#4658)
Leave out canceled attempts. Should make it easier to do percentages.

Not putting these in node stats yet. Will observe in prom before using
it in node stats.
2026-07-11 00:09:16 +05:30
Raja SubramanianandGitHub 19c3d00fc9 Add option to exclude local IPv6 candidates. (#4657)
Could be useful option to try in certain conditions where flakey IPv6
infrastructure is suspected for connection issues.
2026-07-10 15:56:58 +05:30
lukasIOandGitHub aff752a4bc Add signal tests to test-server (#4653)
* add signal tests to test-server

* update action enum
2026-07-09 16:07:37 +02:00
Raja SubramanianandGitHub cb46452b5d Export migration to LocalParticipant interface. (#4652)
Can be used during delayed egress start.
2026-07-09 12:23:33 +05:30
Denys SmirnovandGitHub a4bea6981a Update protocol. (#4648) 2026-07-08 19:27:00 +02:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a3c6208a74 Update actions/checkout action to v7 (#4646)
Generated by renovateBot

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-07 01:59:12 -07:00
Raja SubramanianandGitHub bf777e6513 Make IsConnectionCanceled available at LocalParticipant interface. (#4643)
Can be used to keep track of pariticpants failing connection in a room
by checking this when room closes the participant.
2026-07-05 16:08:10 +05:30
David ZhaoandGitHub 0bc73fd0da added mocking function for SIP dialing methods (#4642) 2026-07-04 12:47:02 -07:00
David ZhaoandGitHub 00348c1299 simpler mock protocol (#4641)
enabling us to build a comprehensive set of API tests for our clients
2026-07-03 04:17:59 -07:00
8f6a9cb8b7 Release v1.13.3. (#4640)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1.13.3
2026-07-03 11:03:59 +05:30
d1b031a9dd Stop WHIP session notifier when participant leaves (#4637)
The WHIP connection-notify loop kept issuing RPCs after the participant
had left the room. Guard sendConnectionNotify against a closed
participant (returning ErrParticipantNotFound) and treat that error as a
clean loop exit. Also reorder DeleteSession so the participant is removed
before its WHIP OnClose entry is cleared.

Adds tests covering loop termination on participant leave, context
cancellation, and the closed-participant guard.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 14:07:21 +01:00
cnderrauberandGitHub 46e5caedbe Report average bitrates for whip ingress (#4634) 2026-07-01 17:06:49 +08:00
Jacob GelmanandGitHub fcc46d3c45 Use camel case log name in DataBlobKey (#4633) 2026-06-29 21:33:55 -07:00
a47e21b6cb Data track schema metadata (#4622)
* Async attributes on participant.

How it is different from existing participant attributes?
1. Async attribute can be added one at a time.
2. These are not included in `ParticipantInfo`.
3. Get an attribute bt participant identity and async attribute ID as
   and when needed.

* clean up

* get full definitions, not just ids

* listener OnDataTrackSchema

* name length config

* data blob

* deps

* static check

* Add missing request ID

* Update protocol commit

* Wire up StoreDataBlobResponse

* Pass request ID through in GetDataBlobResponse

* Pin protocol for schema metadata

* Pass through schema and frame encoding

* Support custom encoding identifiers

* Rename config key

* Increase default length to 32

* Make log messages more generic

* Use getters with built-in null check

* Do not bump deps

* Rename function

* Use protocol v1.48.1 release

---------

Co-authored-by: boks1971 <raja.gobi@tutanota.com>
2026-06-29 10:44:08 -07:00
David ZhaoandGitHub ad76898f03 support auth checks with mock server (#4629)
* support auth checks with mock server

* simplify unit tests
2026-06-28 23:19:05 -07:00
cnderrauberandGitHub 2aec61c11b update webrtc to fix interop issue with bundled datachannel (#4631) 2026-06-29 10:33:51 +08:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
930a2b6ad7 Update module github.com/urfave/cli/v3 to v3.10.0 (#4612)
Generated by renovateBot

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-28 14:57:35 -07:00
David ZhaoandGitHub bfb75d1fc3 feat: mock API server for testing server SDKs (#4627)
* feat: mock API server for testing server SDKs

* fixed lint
2026-06-28 09:28:18 +02:00
a81f06b960 Release v1.13.2. (#4625)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1.13.2
2026-06-27 12:06:30 +05:30
Raja SubramanianandGitHub 23090163ce Configurable migration wait duration for longer waits in simulation. (#4624)
Only applies if it is more than the default 3 seconds.
2026-06-26 20:01:32 +05:30
cnderrauberandGitHub eb3de092f0 update pion/sctp (#4623)
* update pion/sctp

* webrtc & datachannel
2026-06-25 21:43:07 +08:00
1faab0c48e Add support for data blob (a. k. a. async participant attributes) (#4619)
* Async attributes on participant.

How it is different from existing participant attributes?
1. Async attribute can be added one at a time.
2. These are not included in `ParticipantInfo`.
3. Get an attribute bt participant identity and async attribute ID as
   and when needed.

* clean up

* get full definitions, not just ids

* listener OnDataTrackSchema

* name length config

* data blob

* deps

* static check

* Add missing request ID

* Update protocol commit

* Wire up StoreDataBlobResponse

* Pass request ID through in GetDataBlobResponse

* deps

* atomic

* sctp at 1.9.5

* remove proto clone

---------

Co-authored-by: Jacob Gelman <3182119+ladvoc@users.noreply.github.com>
2026-06-24 14:42:37 +05:30
Raja SubramanianandGitHub 0cf53e2f0d Add option to force drain rtcService/agentService connections. (#4618)
When force: true, drain as fast as possible.
2026-06-23 16:10:50 +05:30
6658dd5454 Echo offered audio payload types in single-PC subscriber answer (#4614)
In single peer connection mode, when the server answers a subscriber's
offer, configureSenderAudio set the sender codec preferences from the
server MediaEngine's payload types. The answer could therefore advertise
Opus on a payload type the offerer never offered (server PT 111 vs
offered PT 109). Chrome tolerates this; Firefox decodes 0 samples
(silence) -- packets are received but never decoded. The forwarded RTP
already uses the offered PT, so only the answer SDP was inconsistent.
This regressed in v1.12.0 once the single-PC MediaEngine became a union
of publish+subscribe codecs.

Parse the remote offer's audio rtpmap and remap the sender audio codec
preferences to echo the offered payload types (RFC 3264 6.1) before
SetCodecPreferences.

Fixes #4599

Co-authored-by: laosun <14806343+cnvipstar@users.noreply.github.com>
2026-06-23 10:36:41 +05:30
Raja SubramanianandGitHub 4facbc582a Move lock to addPendingTrack function. (#4617)
Wrapping the function with the lock outside in the only invocation was
not needed.
2026-06-23 10:30:59 +05:30
Raja SubramanianandGitHub 1b69630a28 Prometheus metric for join latency. (#4616)
* Prometheus metric for join latency.

Also including a couple of other failures in the signal connection path
and moving the signal connected to after all that.

Not doing counters for the new signal failure paths. I should not have
done for the other two I added a little while ago also (
validation failure and start participant failure) as those are not
scalable to keep adding to node stats. Will probably remove those two
from node stats later. Can add those counters if they are useful.

* deprecate signal failed counters
2026-06-22 22:07:32 +05:30
Ryan GausandGitHub 86a79f83fc fix: report participant capabilities in ParticipantInfo (#4606) 2026-06-22 09:23:33 -04:00
Raja SubramanianandGitHub f7085535da Tighten up publish latency stat. (#4615)
Previously it was anchored to participant transitioning to `ACTIVE` if
the add track request happened before that. But, that has a few issues
1.`ACTIVE` is for primary peer connection which could be subscriber peer
connection.
2. `ACTIVE` also include data channel establishment.

Switch to first connected time of publisher peer connection for that to
get a more accurate measure of track publish time.
2026-06-22 17:36:06 +05:30
CloudWebRTCandGitHub 13ce35fc87 fix: Clear the enableStartAtDesiredQuality flags in MaybeExpireAcquireGrace. (#4613) 2026-06-22 14:03:47 +08:00
Raja SubramanianandGitHub a3a6b6de96 triviial: remove usused config. (#4611)
noticed a config in deploy config while cleaning up some other usused
config. small clean up. probably there is a bunch more that can be
cleaned up, but doing a quick one as I noticed this.
2026-06-21 16:37:21 +05:30
cfedcc71d0 feat: acquire requested video layer directly at HIGH quality by default (#4595)
* feat: acquire requested video layer directly at HIGH quality by default

Two changes that together remove the visible low->high quality ramp for a new
subscriber (both publisher-first and subscriber-first join orders):

1. Default a subscriber's initial video quality to HIGH on bind instead of LOW
   for adaptive stream, so the subscribed max layer is the top layer. Adaptive
   stream clients can still scale down afterwards based on viewport.

2. On initial layer acquisition the forwarder/selector latch directly onto the
   allocator's target (the requested top layer) instead of opportunistically
   latching onto the first lower key frame that arrives. A short
   initial-acquisition grace aims the target at the requested layer; if it does
   not show up in time, the target falls back to the highest layer seen so
   acquisition never stalls.

Always on - no configuration flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: gate start-at-desired-quality behind EnableStartAtDesiredQuality flag

Put the "acquire requested video layer directly at HIGH quality" behavior
behind a per-subscriber EnableStartAtDesiredQuality flag (default off, so
the original low->high ramp-up is restored unless enabled).

Plumbed from config.RTC.EnableStartAtDesiredQuality through ParticipantParams
-> SubscribedTrack/DownTrack -> Forwarder -> simulcast selector, gating all
three behavior changes: the HIGH default on bind, the forwarder's
initial-acquisition grace, and the selector's direct-latch-onto-target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* remove config.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-19 20:12:54 +08:00
Raja SubramanianandGitHub a011d995da Do not call nil callback (#4607) 2026-06-18 23:24:33 +05:30
Raja SubramanianandGitHub 9a7fe3cc68 Do not log due to negative getting interpreted as large unsigned positive (#4605) 2026-06-18 20:08:52 +05:30
Denys SmirnovandGitHub 35b5390c27 Update protocol. (#4601) 2026-06-18 13:29:21 +02:00