fix(nest): per-stream session state and re-arming stream extend timer - #2335
fix(nest): per-stream session state and re-arming stream extend timer#2335FariAmrid wants to merge 1 commit into
Conversation
Nest streams died shortly after start because: - NewAPI returned the cached *API shared by every stream with the same credentials, so concurrent streams overwrote each other's session state and ExtendStream targeted the wrong session - StartExtendStreamTimer scheduled a single extend at expiresAt-1m and never re-armed; with short-lived sessions the delay is <= 0, so the only extend fired immediately and the session died one expiry later - StopExtendStreamTimer nils extendTimer while the goroutine dereferences it (panic) and the goroutine leaked after Stop NewAPI now returns a per-caller instance carrying the cached token, and the extend timer is a stop-channel-controlled loop that extends 30s before each expiry and retries failed extends before giving up. Fixes AlexxIT#2319 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
PR #2335 completely fixes the Nest session expiration issue on my system. Thank you! I noticed one minor startup behavior. After a cold start of Frigate, the initial ffmpeg processes sometimes start before the Nest producer has received the first video keyframe, resulting in one expected startup error: Could not find codec parameters for stream 0 (Video: h264, none): unspecified size The watchdog immediately restarts ffmpeg, after which both Nest cameras run normally for hours without any further issues. So this is no longer a stability problem—only a cosmetic startup race. It might be possible to eliminate the initial log messages by delaying the RTSP restream until the producer is fully initialized, or by waiting until the first keyframe has been received before Frigate attempts to consume the stream. |
Problem
Nest WebRTC/RTSP streams die shortly after start (often within a minute) and never recover. With several cameras on the same credentials it is worse: only the most recently started stream can be extended at all. This became much more visible recently as Nest media sessions got shorter-lived (see #2319), but the underlying defects are older and likely also explain long-standing reports like #2108 and #723.
Root causes (
pkg/nest/api.go)All streams with the same credentials share one
*APIinstance.NewAPIreturns the cached object itself, andExchangeSDP/GenerateRtspStreamstore per-session state (StreamProjectID,StreamDeviceID,StreamSessionID,StreamExpiresAt, …) on it. Every new stream overwrites the previous stream's session, soExtendStreamtargets whichever camera connected last — extensions for every other camera are lost. TheextendTimer != nilguard also means only one timer ever exists for all streams.The extend timer is single-shot and never re-arms.
StartExtendStreamTimerschedules one extend atexpiresAt − 1 minand the goroutine exits afterwards, so a session survives at most two expiry windows even when everything works. WhenexpiresAtis less than a minute away, the computed delay is ≤ 0, the timer fires immediately, and the only extend is spent right away — the session then dies at the next expiry.Race + goroutine leak (nest: nil-pointer panic in StartExtendStreamTimer (race with StopExtendStreamTimer) crash-loops Nest WEB_RTC sources #2319).
StopExtendStreamTimersetsa.extendTimer = nilwhile the goroutine dereferencesa.extendTimer.C→ nil-pointer panic. And becauseTimer.Stopdoes not close the channel, a stopped timer leaves that goroutine blocked forever — one leaked goroutine per stream start/stop cycle.Fix
NewAPIreturns a fresh per-callerAPIcarrying the cached token (token caching behavior is unchanged), so each stream owns its session state and extend loop.refreshTokennow uses a stored credentials key instead of the reverse cache lookup by token — that lookup also failed whenever the cached token had rotated.StartExtendStreamTimerstarts a loop that extends the session 30 s before every expiry (minimum 5 s delay), re-arms after each success using the refreshedexpiresAt, and retries a failed extend up to 3 times, 2 s apart, before giving up.StopExtendStreamTimerstays idempotent.No logging added, consistent with the logging removal in #1669.
Testing
Equivalent patch (same logic, plus debug logging) running in production since 2026-07-01 on linux/arm64 Docker with 4 WebRTC-only cameras (Nest Cam battery/wired gen 2 + Nest Doorbell,
supportedProtocols=[WEB_RTC]), feeding ffmpeg recorders through the RTSP restream:Could not find codec parameters, empty recordings);Fixes #2319