nandi/frqpublic Fork 0
a477f70
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Dial again when rejoining a call

Rejoining signalled correctly and then sat there with no video, because the
only thing that ever dialled the SFU was a token arriving — and the server
mints one when you join a session, not every time you join it. The second
join brought no token, so nothing dialled, and the call was live everywhere
except where you could see or hear it.

Two changes, both taken from how sleek does it.

The token is remembered per session id, past the end of the call, so a
rejoin has something to dial with even when the server sends nothing new.

And the dial is attempted from every signal that might mean the call is
ready — the server agreeing we joined as well as a token arriving — because
none of them is reliably the one that comes last. What makes that safe is
that it was already guarded: can-dial? refuses a remote SFU with no token
rather than retrying in a loop that looks like a hang, and a call already up
or on its way is left alone rather than re-dialled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-08-30T18:08:41-07:00 Browse files
a477f70 parent: 326bb74
modified src/frq/av.jolt +54 -10
@@ -189,6 +189,15 @@
189189 ;; without depending on the client that owns the connection.
190190 (defonce on-dropped (atom nil))
191191
192+;; SFU tokens, by session id.
193+;;
194+;; The server mints one when you join, and does not necessarily mint another
195+;; when you join the same call again so a client that forgets it on the way
196+;; out has nothing to dial with on the way back in, and rejoining signals
197+;; correctly and then sits there with no video. Kept past the call for exactly
198+;; that, and overwritten whenever a fresh one arrives.
199+(defonce ^:private session-tokens (atom {}))
200+
192201 ;; Feeds pushed to Vidya, so the ones that stop can be dropped again. Without
193202 ;; this the last frame of someone who left hangs on the wall for the rest of
194203 ;; the call.
@@ -248,7 +257,15 @@
248257 ;; ended it. Leaving the local call set would leave the controls up over
249258 ;; a session the SFU has already forgotten.
250259 (when (and (= :ended action) (in-call? channel))
251- (stop-media!))))
260+ (stop-media!))
261+ ;; The server agreeing we are in the call is the other moment worth
262+ ;; dialling on. A join we opened optimistically has no session id until
263+ ;; this arrives and a *re*join often brings no token with it, because the
264+ ;; server already minted one for this session and does not mint a second.
265+ (when (and (not= :ended action) (in-call? channel))
266+ (when (and (seq session-id) (str/blank? (:session-id @local-call)))
267+ (swap! local-call assoc :session-id session-id))
268+ true)))
252269
253270 ;; --- the media plane, as this client uses it ---------------------------------
254271
@@ -284,6 +301,8 @@
284301 (try (announce call) (catch Exception _ nil))))
285302 (stop-media!))
286303
304+(declare try-start-media!)
305+
287306 (defn- start-media!
288307 "Dial the SFU for the call we have already joined over IRC.
289308
@@ -305,23 +324,45 @@
305324 (do (swap! local-call assoc :media :failed)
306325 (reset! media-error (str "no SFU for " server))))))
307326
327+(defn try-start-media!
328+ "Dial the SFU if there is a call to dial for, and we are not already on it.
329+
330+ Called from every signal that might mean the call is ready the server
331+ agreeing we joined, a token arriving, a rejoin because none of them is
332+ reliably the one that comes last. What makes that safe is the two guards:
333+ `can-dial?` refuses a remote SFU with no token rather than retrying in a
334+ loop that looks like a hang, and a call already up or on its way is left
335+ alone rather than re-dialled."
336+ [server]
337+ (when-let [{:keys [session-id token media]} @local-call]
338+ (when (and (seq session-id)
339+ (not (contains? #{:dialling :live} media))
340+ (can-dial? server token))
341+ (start-media! server))))
342+
308343 (defn apply-token!
309- "The server minted us an SFU token — dial the media plane with it.
344+ "The server minted us an SFU token — remember it, and dial.
310345
311346 This arrives as a TAGMSG directed at our own nick rather than at the channel,
312347 so the buffer it came in on says nothing about which call it is for; the
313- session id in the tag does. One that names a different session than ours is
314- not ours."
348+ session id in the tag does. One naming a different session than ours is not
349+ ours.
350+
351+ Remembered past the end of the call, because the server does not always mint
352+ a second one when you rejoin the same session."
315353 [server session-id token]
316354 (when-let [lc @local-call]
317355 (when (or (str/blank? session-id)
318356 (str/blank? (:session-id lc))
319357 (= session-id (:session-id lc)))
320- (swap! local-call
321- #(-> %
322- (assoc :token token :awaiting-start? false)
323- (cond-> (seq session-id) (assoc :session-id session-id))))
324- (start-media! server))))
358+ (let [sid (if (seq session-id) session-id (:session-id lc))]
359+ (when (seq sid)
360+ (swap! session-tokens assoc sid token))
361+ (swap! local-call
362+ #(-> %
363+ (assoc :token token :awaiting-start? false)
364+ (cond-> (seq sid) (assoc :session-id sid)))))
365+ (try-start-media! server))))
325366
326367 (defn begin!
327368 "Record that this device is joining `channel`, before the server has agreed.
@@ -338,7 +379,10 @@
338379 :session-id (or session-id "")
339380 :instance instance
340381 :nick nick
341- :token nil
382+ ;; What we were given last time we were in this call, if
383+ ;; anything. A rejoin the server answers with no new token
384+ ;; still has something to dial with.
385+ :token (get @session-tokens (or session-id ""))
342386 :awaiting-start? (str/blank? (or session-id ""))
343387 :muted? (boolean muted?)
344388 :speaker-muted? (boolean speaker-muted?)
@@ -189,6 +189,15 @@
189 ;; without depending on the client that owns the connection.189 ;; without depending on the client that owns the connection.
190 (defonce on-dropped (atom nil))190 (defonce on-dropped (atom nil))
191 191
192+;; SFU tokens, by session id.
193+;;
194+;; The server mints one when you join, and does not necessarily mint another
195+;; when you join the same call again so a client that forgets it on the way
196+;; out has nothing to dial with on the way back in, and rejoining signals
197+;; correctly and then sits there with no video. Kept past the call for exactly
198+;; that, and overwritten whenever a fresh one arrives.
199+(defonce ^:private session-tokens (atom {}))
200+
192 ;; Feeds pushed to Vidya, so the ones that stop can be dropped again. Without201 ;; Feeds pushed to Vidya, so the ones that stop can be dropped again. Without
193 ;; this the last frame of someone who left hangs on the wall for the rest of202 ;; this the last frame of someone who left hangs on the wall for the rest of
194 ;; the call.203 ;; the call.
@@ -248,7 +257,15 @@
248 ;; ended it. Leaving the local call set would leave the controls up over257 ;; ended it. Leaving the local call set would leave the controls up over
249 ;; a session the SFU has already forgotten.258 ;; a session the SFU has already forgotten.
250 (when (and (= :ended action) (in-call? channel))259 (when (and (= :ended action) (in-call? channel))
251- (stop-media!))))260+ (stop-media!))
261+ ;; The server agreeing we are in the call is the other moment worth
262+ ;; dialling on. A join we opened optimistically has no session id until
263+ ;; this arrives and a *re*join often brings no token with it, because the
264+ ;; server already minted one for this session and does not mint a second.
265+ (when (and (not= :ended action) (in-call? channel))
266+ (when (and (seq session-id) (str/blank? (:session-id @local-call)))
267+ (swap! local-call assoc :session-id session-id))
268+ true)))
252 269
253 ;; --- the media plane, as this client uses it ---------------------------------270 ;; --- the media plane, as this client uses it ---------------------------------
254 271
@@ -284,6 +301,8 @@
284 (try (announce call) (catch Exception _ nil))))301 (try (announce call) (catch Exception _ nil))))
285 (stop-media!))302 (stop-media!))
286 303
304+(declare try-start-media!)
305+
287 (defn- start-media!306 (defn- start-media!
288 "Dial the SFU for the call we have already joined over IRC.307 "Dial the SFU for the call we have already joined over IRC.
289 308
@@ -305,23 +324,45 @@
305 (do (swap! local-call assoc :media :failed)324 (do (swap! local-call assoc :media :failed)
306 (reset! media-error (str "no SFU for " server))))))325 (reset! media-error (str "no SFU for " server))))))
307 326
327+(defn try-start-media!
328+ "Dial the SFU if there is a call to dial for, and we are not already on it.
329+
330+ Called from every signal that might mean the call is ready the server
331+ agreeing we joined, a token arriving, a rejoin because none of them is
332+ reliably the one that comes last. What makes that safe is the two guards:
333+ `can-dial?` refuses a remote SFU with no token rather than retrying in a
334+ loop that looks like a hang, and a call already up or on its way is left
335+ alone rather than re-dialled."
336+ [server]
337+ (when-let [{:keys [session-id token media]} @local-call]
338+ (when (and (seq session-id)
339+ (not (contains? #{:dialling :live} media))
340+ (can-dial? server token))
341+ (start-media! server))))
342+
308 (defn apply-token!343 (defn apply-token!
309- "The server minted us an SFU token — dial the media plane with it.344+ "The server minted us an SFU token — remember it, and dial.
310 345
311 This arrives as a TAGMSG directed at our own nick rather than at the channel,346 This arrives as a TAGMSG directed at our own nick rather than at the channel,
312 so the buffer it came in on says nothing about which call it is for; the347 so the buffer it came in on says nothing about which call it is for; the
313- session id in the tag does. One that names a different session than ours is348+ session id in the tag does. One naming a different session than ours is not
314- not ours."349+ ours.
350+
351+ Remembered past the end of the call, because the server does not always mint
352+ a second one when you rejoin the same session."
315 [server session-id token]353 [server session-id token]
316 (when-let [lc @local-call]354 (when-let [lc @local-call]
317 (when (or (str/blank? session-id)355 (when (or (str/blank? session-id)
318 (str/blank? (:session-id lc))356 (str/blank? (:session-id lc))
319 (= session-id (:session-id lc)))357 (= session-id (:session-id lc)))
320- (swap! local-call358+ (let [sid (if (seq session-id) session-id (:session-id lc))]
321- #(-> %359+ (when (seq sid)
322- (assoc :token token :awaiting-start? false)360+ (swap! session-tokens assoc sid token))
323- (cond-> (seq session-id) (assoc :session-id session-id))))361+ (swap! local-call
324- (start-media! server))))362+ #(-> %
363+ (assoc :token token :awaiting-start? false)
364+ (cond-> (seq sid) (assoc :session-id sid)))))
365+ (try-start-media! server))))
325 366
326 (defn begin!367 (defn begin!
327 "Record that this device is joining `channel`, before the server has agreed.368 "Record that this device is joining `channel`, before the server has agreed.
@@ -338,7 +379,10 @@
338 :session-id (or session-id "")379 :session-id (or session-id "")
339 :instance instance380 :instance instance
340 :nick nick381 :nick nick
341- :token nil382+ ;; What we were given last time we were in this call, if
383+ ;; anything. A rejoin the server answers with no new token
384+ ;; still has something to dial with.
385+ :token (get @session-tokens (or session-id ""))
342 :awaiting-start? (str/blank? (or session-id ""))386 :awaiting-start? (str/blank? (or session-id ""))
343 :muted? (boolean muted?)387 :muted? (boolean muted?)
344 :speaker-muted? (boolean speaker-muted?)388 :speaker-muted? (boolean speaker-muted?)
modified src/frq/state.jolt +7 -0
@@ -276,6 +276,13 @@
276276 someone joining is a number quietly changing in a banner."
277277 [channel st]
278278 (av/apply-state! channel st)
279+ ;; And try to dial. The token may already be in hand — from this join, or
280+ ;; from the last time we were in this same session — in which case the
281+ ;; server's agreement that we are in the call is the last thing we were
282+ ;; waiting for. `try-start-media!` refuses if there is nothing to dial with
283+ ;; or a call is already up, so calling it on every state change is safe.
284+ (when (av/in-call? channel)
285+ (av/try-start-media! @form-host))
279286 (let [line (av/state-message st)]
280287 (when (seq line)
281288 (push-message! channel "*" line))))
@@ -276,6 +276,13 @@
276 someone joining is a number quietly changing in a banner."276 someone joining is a number quietly changing in a banner."
277 [channel st]277 [channel st]
278 (av/apply-state! channel st)278 (av/apply-state! channel st)
279+ ;; And try to dial. The token may already be in hand — from this join, or
280+ ;; from the last time we were in this same session — in which case the
281+ ;; server's agreement that we are in the call is the last thing we were
282+ ;; waiting for. `try-start-media!` refuses if there is nothing to dial with
283+ ;; or a call is already up, so calling it on every state change is safe.
284+ (when (av/in-call? channel)
285+ (av/try-start-media! @form-host))
279 (let [line (av/state-message st)]286 (let [line (av/state-message st)]
280 (when (seq line)287 (when (seq line)
281 (push-message! channel "*" line))))288 (push-message! channel "*" line))))