Commit Graph
1596 Commits
Author SHA1 Message Date
Martin Njalale 5cd0f37342 rtc: record a room's departure when the participant leaves it (#4913)
* rtc: record a room's departure when the participant leaves it

RemoveParticipant removed the participant from the room, closed it, and
only then recorded leftAt. CloseIfEmpty runs every second, so a tick
landing during the close saw an empty room with a stale or zero
departure time and closed it at once: with no departure recorded it
compares against the room's creation time and empty_timeout, and with an
earlier departure it measures the grace period from that older
timestamp. Either way departure_timeout is skipped.

Record the departure where the participant is removed, under the lock
that already covers it.

* rtc: drop the removal wait from the departure timeout test

The close signal already pins the interleaving, and the room's close
state is decided by the CloseIfEmpty call itself.
2026-09-28 14:06:26 +05:30
Raja SubramanianandClaude Fable 5.1 6cfd19bd9b rtc: split EndSession out of MoveToRoom (#4917)
* rtc: split EndSession out of MoveToRoom

A participant's session in a room can end without the participant
closing: today on a move, and next on a room re-key, where a room ended
by the sweeper while participants were joining restarts under a new room
id with the same participants in it.

EndSession is the room-independent half of MoveToRoom: report the
published tracks unpublished, report the session end, run the caller's
leave under the ending session's telemetry guard, then take a fresh
guard and reset the deferred resolvers for the next session. The guard
matters: ParticipantLeft releases it and the telemetry service refuses
to open a stats worker for a released guard, so without a fresh one the
next session's join would silently get no worker.

MoveToRoom keeps the subscriber teardown and the onClose fan-out, passed
in as the leave.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* rtc: rename EndSession to LeaveSession, add JoinSession

EndSession read as terminal, but the participant survives it and joins
another session, so name the pair for what happens to the participant:
LeaveSession and JoinSession.

JoinSession is the other half that review pointed out was missing from
the API: the caller reports the join, then the published tracks are
reported published again under the new session, pairing the unpublish
LeaveSession reported. Without it the re-publish lived only in the
caller, with nothing in OSS proving the two halves balance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 13:41:30 +05:30
Raja Subramanian 93f5b3eb61 Add a local participant method to get recently disconnected (#4911)
participants.

While migrating, the old node will have the recently disconnected
participants in the migrating out participant's cache and needs to sent
out to the participant from the migrating in node via the new WebSocket.
This method can be used to exchange that data between the two nodes.

Keeping it as `recently` disconnected and leaving the semantics upto how
recent for implementation.
2026-09-26 01:33:38 +05:30
Raja Subramanian 9d8ee5a319 Report disconnected for older disconnected participants. (#4910)
There are reports of disconnected participant update missing -
https://github.com/livekit/client-sdk-js/pull/2096. That results in
clients retaining disconnected participant(s) in its roster.

Two changes
1. Look for participant disconnects a little farther back than last
   signal time as client app might have lost visibility earlier.
2. Look at all participants within the relaxed window above. The update
   cache is LRU. So, in theory can stop when an entry is before the time
   threshold above. But, theoretically it is possible that `updatedAt`
   time and LRU queuing time can flip ordering although very unlikely.

I think we should do one of the following to make it more robust, but
both require client side changes
1. Send full participant list in `ReconnectResponse`. Clients can use
   that to drop participants not in the list.
2. Add a sequence number to all signal responses (maybe signal requests
   as well) and have client send up the last response it saw in
   `SyncState` message. Server can then replay all the messages after
   that. This also requires a message cache on server side.
2026-09-26 00:08:42 +05:30
Pablo Fuente Pérez f7ec53ca73 dynacast: re-send subscribed qualities after every publisher answer (#4907)
* dynacast: re-send subscribed qualities after every publisher answer

The SFU answers publisher offers without the simulcast pause markers
(SetIgnoreRidPauseForRecv), so applying the answer re-enables in the
browser the layers that dynacast had paused. Dynacast only notifies
changes, so those layers stayed on until the subscribed quality of the
track changed again.

Send the publisher its committed subscribed qualities again 1 s after
each answer, once the client has applied it: livekit-client handles the
next signal message without waiting for the answer to be applied.

Fixes https://github.com/livekit/livekit/issues/4906

* dynacast: resend once per burst of publisher answers, only for paused layers

In SPC, every subscription change of a participant goes through a new publisher offer and answer, so answers come in bursts when a room is busy. Each answer scheduled its own resend of the subscribed qualities, sending the same update several times in a row.

Thist change keeps one pending resend per participant instead. It fires 2 s after the last answer, and never later than 8 s after the first answer of the burst, so a steady stream of answers cannot postpone it forever. It also skips the resend for tracks whose committed qualities are all HIGH: with every layer enabled, an answer has nothing paused to re-enable. We can save those messages.
2026-09-25 16:23:40 +05:30
Paul Wells 1ba2cadccd agent: release JobTerminate handler when job ends before registration (#4908)
* agent: release JobTerminate handler when job ends before registration

JobRequest registered the job's JobTerminate handler after AssignJob
returned, so an ended UpdateJobStatus or worker disconnect handled in
between deregistered nothing and the handler leaked. AssignJob also sent
the assignment before recording the job as running, so an immediate
ended update was dropped.

Record the job before sending the assignment, and after registering the
handler recheck under h.mu that the job is still running and its worker
still registered, deregistering otherwise.

Fixes #4901

* replace utils.CloneProto with proto.CloneOf

* agent: wait on worker readiness and poll for released handlers in test
2026-09-25 00:58:22 -07:00
Raja Subramanian f52b0e2bd5 rtc: parse each remote offer once per negotiation (#4904) 2026-09-25 05:01:30 +05:30
cnderrauber 6b2e3ece69 Increase receiver loadbalance threshold when batch io enabled (#4899)
* Increase receiver loadbalance threshold when batch io enabled

With batch IO the send syscall moves to the BatchConn flush goroutine, so WriteRTP only
translates the packet and enqueues it. Increash the threshold to reduce

* lint
2026-09-23 13:54:40 +08:00
Sergey KaterinenkoandClaude Opus 5 abf98e5a28 Do not request media sections for transceivers that cannot send (#4892)
* Do not request media sections for transceivers that cannot send

getNumUnmatchedTransceivers counted every local transceiver without a mid.
A transceiver whose track was removed before its section was negotiated
(direction recvonly, no sender) or that was stopped (inactive) can never be
matched to the recvonly section the remote adds in response to a
MediaSectionsRequirement, so it stayed "outstanding" forever: every answer
asked the remote for a section for it again, the remote appended another
batch of transceivers each round, and its offer grew by a fixed amount per
negotiation until it exceeded limit.signal_message_size_limit and the
signalling connection was closed (full reconnect, then the cycle restarts).

Count only transceivers that have something to send. A released transceiver
that AddTrack reuses later becomes sendrecv and is counted again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Address review: blank lines between test functions, assert transceiver reuse

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:07:26 +05:30
Alex 502346df00 Propagate SIP_TRUNK_FAILURE (#4890) 2026-09-21 15:40:21 -07:00
cnderrauber 885c869ffa Add sdk lable to session start time and participant join (#4885) 2026-09-21 13:14:36 +08:00
Benjamin PrachtandClaude Opus 5 d2ec76a0bf test: fix two data races in the test suites (#4878)
* test: stop registering the global DefaultConfig with the logger

InitLoggerFromConfig hands the pointer to zaputil.ComponentLeveler as its
level resolver, and protocol a879e94 gave logger.Config a
ResolveComponentLevel that takes c.lock. The two test packages passed
&config.DefaultConfig.Logging, so every first-time component level
resolution locked a mutex inside the global DefaultConfig while
config.NewConfig marshalled that same global from another goroutine:

  Read at 0x39baa08  yaml.Marshal -> pkg/config/config.go:633 (NewConfig)
  Write at 0x39baa08 ComponentLeveler.resolve -> zaputil/leveler.go:117

That tripped the race detector in pkg/rtc TestPreferMediaCodecForPublisher
and broke CI on master.

Register a test-local LoggingConfig instead, so the logger never touches
the global that NewConfig reads. DefaultConfig.Logging only ever sets
PionLevel, so behavior is unchanged. A plain struct copy is not an option
here: logger.Config holds a mutex and vet's copylocks would reject it.

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

* test: make RTCClient callbacks safe to set while the client runs

The scenario tests assign c2.OnDataReceived after waitUntilConnected, so
the assignment raced the data channel goroutine already reading the field
in handleDataMessage:

  Read  test/client.(*RTCClient).handleDataMessage  client.go:1146
  Write test.scenarioDataPublish.func1              scenarios.go:160

This is what failed TestMultinodeDataPublishing on master.

Replace the three exported callback fields with atomic pointers behind
SetOnConnected/SetOnDataReceived/SetOnDataUnlabeledReceived. OnConnected
and OnDataUnlabeledReceived have no writers today, but they are read from
the same background goroutines and would race the moment one appeared, so
all three move together rather than leaving a split API on one struct.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 22:35:30 -07:00
Raja SubramanianandClaude Opus 4.8 1d3ff5621c rtc: add GetSubscribedDataTracks to LocalParticipant (#4862)
SubscriptionManager tracks data-track subscriptions but exposed no accessor
for the active ones, so callers could see subscribed and published media tracks
and published data tracks, but not subscribed data tracks. Add
GetSubscribedDataTracks(), returning the bound data down-tracks, mirroring
GetSubscribedTracks() for media, so a participant's full track set can be
accounted for. Regenerated the LocalParticipant fake.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-12 20:00:53 +05:30
Viacheslav Sergeev 443549ae90 Fix/reconcile data track subscription deadlock (#4843)
* fixed deadlock in reconcileDataTrackSubscription

* test

* review changes
2026-09-05 19:20:17 +05:30
Pablo Fuente Pérez a8ce3985ca Revert PR #4838 in favor of a better solution (#4840) 2026-09-05 01:02:48 +05:30
Pablo Fuente Pérez 407fe3fe26 rtc: send connection quality to participants that subscribe after their first update (#4838)
* rtc: send connection quality to participants that subscribe after their first update

Added tracking for participants' connection quality updates to ensure all subscribed participants receive necessary updates, even if they were added after the last quality announcement

* rtc: record sent connection quality only after a successful send

A failed SendConnectionQualityUpdate must not mark the participant as informed, otherwise the update is never retried while qualities stay stable. Also simplify the untold-subscription check.
2026-09-04 23:49:33 +05:30
Jacob Gelman 4645bca189 Move data track packet serialization to protocol (#4829)
* Pin protocol to data track serialization branch

* Use datatrack package from protocol repo

* Update protocol sha
2026-09-01 13:02:04 -07:00
Raja SubramanianandClaude Opus 5 0aae7a4894 fix: hold signal messages until the ReconnectResponse goes out (#4827)
* fix: hold signal messages until the ReconnectResponse goes out

Clients take the ReconnectResponse as the first message on a resumed or
migrated in signal connection, anything ahead of it is dropped. Hold
messages back until it has been written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: keep queued participant updates on a path that always flushes

Queue only while the participant is not ready, that queue is always
drained by the join or reconnect response. Log a dropped SDP, it leaves
the negotiation waiting until the state machine recovers it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: flush queued updates whenever the connection opens

Queue participant updates while the handshake is pending again, and give
the signaller a hook that fires when the connection opens, on an explicit
open and on the handshake window expiring, so the queue always drains.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: drive the handshake window off a timer and read the gate atomically

The window ran only when something asked whether the handshake was
pending, so a connection with nothing else to send held its queue.
Reading the gate under the lock the flush takes closes the race where an
update queued just after a flush.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: ignore a handshake timeout from a wait that has ended

Stop cannot cancel a timeout that is already running, so tag each wait
and let a timeout act only on its own. Count opens atomically in the
test, the timer fires on its own goroutine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 13:59:04 +05:30
Paul Wells 54496c765d report a reason on room-ended telemetry (#4815)
* report a reason on room-ended telemetry

Room.Close already took a ParticipantCloseReason, but OnClose was func()
with no arguments, so the reason was structurally dropped before it could
reach RoomEnded.

Add types.RoomCloseReason and carry it through Close -> OnClose -> RoomEnded,
where it lands on both the analytics event and the room_finished webhook.

Room.Close now takes the room reason and derives the participant reason from
it, so the two can never disagree. ToParticipantCloseReason maps each reason
to exactly what its call site passed before, so no participant-facing
behaviour changes; a table test pins that mapping.

* fix(test): update webhook test for RoomCloseReason

test/ was outside the packages checked before pushing, so this Close call
site was missed. Also assert the reason reaches the room_finished webhook,
which the unit test cannot cover since it runs with a nil notifier.
2026-08-28 23:41:59 -07:00
cnderrauber d185224719 warp log (#4811) 2026-08-28 16:59:21 +08:00
Raja Subramanian c362e61d3c Set up track info properly for dummy receiver. (#4807)
In migration cases, dummy receiver trackInfo is used by relay tracks to
set up the receivers and those need the proper track info.

Also check for proper receiver when adding a migrated track.
2026-08-28 00:27:50 +05:30
Alex FeldgendlerandClaude Opus 5 dc2bd1d3b4 Wait for the callbacks these tests assert on. (#4803)
TestUnsubscribe checked that the changed-notifier observer was gone as
soon as the unsubscribe had settled, but setDesired leaves the
RemoveObserver call to a goroutine of its own and nothing the test waits
on orders against it, so CI caught the assertion running first.
TestSubscribe has the same defect on the unsubscribed callback, which
unmarkSubscribedTo delivers with a bare go while the subscribed one is
called inline.

Wait for both, each with a message of its own, so that a leak that is
real still says which of them broke.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 12:25:01 +05:30
Raja SubramanianandClaude Opus 5 bbdfd50d62 Fix simulcast RTX pairing broken by pion/webrtc#3470 (#4800)
pion no longer starts the repair stream reader when a custom BufferFactory
is set, so the mid/rid/rsid extensions were never observed and simulcast RTX
streams were never paired with their primary streams.

Extract the extensions on the buffer write path instead. Migrated publishers
send no extensions at all, so pair those from SimTracks.

Adds an integration test covering both paths, and moves the vnet setup it
shares with the downtrack test into pkg/testutils/vnettest.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:01:22 +05:30
Raja Subramanian b0710f7a6a Reduce locking in media track + telemetry listener on move participant. (#4790)
* Reduce locking in media track + telemetry listener on move participant.

Telemetry listener was not getting it from new room on room move.

* update comment

* fix signal bytes reporting

* room aware telemetry listener in media track

* data track telemetry listener
2026-08-25 02:45:40 +05:30
Raja Subramanian 26975ef057 Join failure is a canceled connection attempt. (#4788)
Could happen due to participant being already in room, exceeding max
participants, etc.
2026-08-24 10:56:17 +05:30
cnderrauber 803864dc7b Add bytes cap on migration data cache size (#4782) 2026-08-21 16:49:09 +08:00
Raja Subramanian 6a64df20df Add a small cache for data messages received via SendData API. (#4781)
SendData API messages do not have sender ID or sequence number. So, they
were not cached and hence excluded from the reliable caching feature
which is meant to provide reliability of data channel messages between
the time other participants see participant as ACTIVE (which happens on
ICE connected) and data channel being open (DTLS done + data channels
opened).

Add a small cache for that and flush those messages on data channel
establishment.
2026-08-20 13:51:10 +05:30
Raja Subramanian 972c64a29b Check for ICE connection before closing participant on signal close. (#4780)
* Check for ICE connection before closing participant on signal close.

Only close the participant if ICE has not connected by the time signal
source is closed. If ICE had connected, candidates have been exchanged
and link was established. So, it should be resumable.

Waiting for DTLS closed the participant sometimes in the windowa after
ICE connection, but before DTLS finishes and that unnecessarily closed
the participant forcing a full reconnect.

* variable name
2026-08-20 13:07:28 +05:30
Raja Subramanian 0ef7ee440c Fix deadlock in regress codec check. (#4775) 2026-08-19 20:17:53 +05:30
cnderrauber dbe06aa8d1 Experimental WARP (#4649)
* Experimental WARP

* fix panic

* go dep

* stats
2026-08-14 16:26:08 +08:00
Raja Subramanian 9d676e3a60 Limit number of pending tracks per participant. (#4750)
* Limit number of pending tracks per participant.

Prevents just a signalling connection adding tracks without actually
publishing them growing a large number.

* add to supervisor only if pending track is accepted
2026-08-13 16:48:49 +05:30
Raja Subramanian d51533e25c Make subscription limit log Debugw as it could spam in a large room. (#4748) 2026-08-13 13:33:32 +05:30
Raja Subramanian 2cd50a961f Record publish time on participant close for pending tracks. (#4738)
With https://github.com/livekit/livekit/pull/4706, there was a case of
some downstream component taking a long time while lock was held. While
the underlying cause of holding a lock while doing callback was removed
in that PR, to catch such cases, some publish side metric anomaly would
be useful to monitor and alert on.

Adding a publish time record for pending tracks on participant close.
That would inflate the publish time for participants not being able to
publish and can be alerted on as it will spike up the value at node
level and at cluster level if multiple nodes have the issue.
2026-08-12 21:41:52 +05:30
Raja SubramanianandClaude Opus 4.8 3c6e56232e Add configurable read-message size limit on signalling WebSockets (#4743)
* Add configurable read-message size limit on signalling WebSockets

Set a read limit on both the client-facing (/rtc) and agent worker
WebSocket connections so an oversized frame is rejected by the transport
before being buffered. The limits are operator-tunable via
signal_message_size_limit and agent_signal_message_size_limit, both
defaulting to 2 MiB (0 disables).

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

* Add tests for signalling WebSocket read-message size limit

Cover the configurable signal_message_size_limit added in the prior
commit:
- config: assert both limits default to 2 MiB and that a YAML override
  (including 0 to disable) is parsed correctly.
- full-path integration: a real client connects to /rtc on a single-node
  server and an oversized frame is rejected by the transport with a 1009
  close; a 0 limit leaves the connection unbounded and signalling
  proceeds.

Adds setupSingleNodeTestWithConfig so a single-node server can be started
with config overrides.

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

* Bound decompressed size of signalling WebSocket messages

conn.SetReadLimit only accounts for the compressed bytes read off the
wire, and the client-facing /rtc upgrader negotiates permessage-deflate,
so a small compressed frame could still expand into a much larger buffer
once inflated. Enforce the same limit on the decompressed message by
reading through NextReader + io.LimitReader in WSSignalConnection instead
of the unbounded ReadMessage.

The transport-level SetReadLimit is kept as the cheap wire-level guard;
the new check is the decompressed-size backstop.

Adds NextReader to the WebsocketClient interface (regenerated fake) and a
unit test plus permessage-deflate integration tests covering the
amplification case.

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-08-12 12:07:26 +05:30
Raja SubramanianandClaude Opus 4.8 c4c356f6ca Cover a couple of more cases on data track runt packet handling. (#4741)
* Cover a couple of more cases on data track runt packet handling.

* Guard data track header parser against extensions-size integer wraparound.

Widen the extensions-size arithmetic to int so a 0xFFFF wire value no
longer wraps in uint16, and reject any packet whose computed hdrSize
exceeds the buffer before slicing the payload.

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-08-11 18:25:10 +05:30
Raja Subramanian d279899b7c Fix publish track count on migration in. (#4740)
* Fix publish track count on migration in.

https://github.com/livekit/livekit/pull/4707 addressed the case of
publish tracks overcounting due to synthesised track publish on migrate
in. But, it introduced an issue where published tracks count could go
negative because unpublish subtracted the counter irrespective of the
track actually migrated in or not.

Fix it by keeping track of local publish.

Also, the older code was skipping publisher track count increase if the
synthesised publish was handled first. Address it by checking if the
track is actually new (i. e. fresh local publish) when the track was
already created in the migrate in path.

* fix pub time for tracks published after migration

* test

* prevent multiple track egresses
2026-08-11 16:13:57 +05:30
Kuba Podgórski 335990afa5 return psrpc.FailedPrecondition for "participant client version does not support moving" error (#4736) 2026-08-09 00:42:24 -07:00
Anunay MaheshwariandSimon Beeli 4e921aa1b6 Expand room details in webhook events (#4730)
* pass room proto directly to telemetry events

* Keep telemetry analytics events on a minimal room, gate full room in webhooks

---------

Co-authored-by: Simon Beeli <simon.beeli@gmx.ch>
2026-08-07 15:29:41 +05:30
Raja Subramanian 3f9cf6bfc2 Do not report end time for participant if the participant is migrating (#4728)
out.
2026-08-06 00:59:37 +05:30
Raja Subramanian 52ef3cd649 Include data track susbcriptions in WaitForSubscription. (#4727) 2026-08-05 13:28:48 +05:30
Nikita Davydov 2a9bb36ee0 Apply ICE preference when switching to TCP on unstable UDP (#4703)
onMediaLossUpdate notified the participant handler directly, which only
sends a leave request with resume action. handleConnectionFailed that
actually switches the ICE preference to TCP/TLS was never called on
this path, so the client reconnected over UDP again and the fallback
kept firing every 30-60s without ever migrating.

Fixes livekit/livekit#4702
2026-08-04 13:04:36 +05:30
cnderrauber 4d177cb01b Remove H.264 baseline (42001f) from default enabled codecs (#4723)
Users can explicitly enable this profile if they are certain that all device support it.
2026-08-04 11:12:42 +08:00
Raja Subramanian ced94b8645 log high stream start latency. (#4714)
* log high stream start latency.

There is something wrong in measurement as audio is showing high p99
latency. Must be misattributing samples. So, logging for high latency to
understand this better.

* use correct variable

* time since create
2026-07-30 17:10:59 +05:30
cnderrauber 436a0cc3d3 Support more h264 profiles (#4708)
* Register h264 main profile if enabled explicitly

We don't support the h264 main profile for compatibility,
user can enabled it by set fmtp explicitly in codec config
to enable it if want to use it in special scenario.

* go mod
2026-07-28 16:18:19 +08:00
Raja Subramanian b8a073cb68 A bit better counting for track publish. (#4707)
- Count a publish attempt on a migrating in tarck as there is no
  AddTrack for that.
- Add cancel publish only if the participant connection is canceled
- Do not add publish counter for synthetic publish attempts which
  happens for migrating in tracks. It will be counted on migrating in
  node when the track is actually published, i. e. negotiated/packets
  flowing.
2026-07-28 02:18:14 +05:30
Raja Subramanian 25e3774cbb Do not call telemetry listener under pending track lock. (#4706)
* Do not call telemetry listener under pending track lock.

Fix the TrackPublishRequested call of telemetry listener.

Audited other callbacks to ensure that it is not under lock.

* missed some paths of recording it, thanks Devin
2026-07-27 23:52:21 +05:30
Raja Subramanian 0aa296e126 Record subscribe stream start time in prometheus. (#4704)
* Record subscribe stream start time in prometheus.

Adjust for mutes, i. e. take the last unmute time as the start point and
calculate time till the first byte is sent.

* close the tiny window of race

* Prevent long tail sample when publisher glitches.

Thanks to @milos-lk for this.

Publisher restarting would have reset the layer and would have caused a
sample with very high stream start time. We only need to capture when we
do a dummy start or when the state is seeded to a different node upon
migration.

* reduce a diff

* test

* changed the wrong thing, thank you Devin
2026-07-27 20:37:44 +05:30
Raja Subramanian 1bbd4702b6 Spelling fixes (#4698) 2026-07-26 22:26:41 +05:30
Raja Subramanian c684997c4f Add country to participant closing log (#4693) 2026-07-20 15:32:42 +05:30
Benjamin PrachtandCopilot Autofix powered by AI 14aeeb3c56 Potential fix for code scanning alert no. 42: Incorrect conversion between integer types (#4684)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-07-17 09:56:26 +02:00