groupcall <State> and get: the whole call in one snapshot #6

Closed
ann wants to merge 0 commits from feat/groupcall-state into develop
Owner

Adds groupcall <State> and groupcall get: the whole group call as one snapshot, for a host that draws from one rather than rebuilding it from the events.

Why. quackchat's Qt GroupCall and its Android GroupCallRegistry both render from upstream tacky's <State>. Without it each would have to rebuild the snapshot from <Changed>, <Joined>, <PeerJoined>, <PeerLeft>, <Left>, <Warning>, <VideoPreview> and every leg's calls events. The Android service also has no simple way to make the participants requests that rebuild needs.

What a state holds.

  • The room: jid, chat, hosted, count.
  • Ours: phase (idle, joining, live, ended), reason, warning, since, video, camera, preview.
  • participants: one row per occupant announcing the call or preparing to, us left out. Each row has nick, bare jid, joined, video, and our leg's state (expected, connecting, active, ended, failed) with its reason, warning and incoming stream.

When it is sent.

  • At most once per room per event-loop turn, and only when something changed.
  • ended goes out once, ahead of <Left>, with no participants.
  • A last idle with count 0 and no participants means nothing is left to show. A disconnect sends one too.

How it is built. It is a projection: groupcall.tcl subscribes to its own events, and to the calls events of the legs <PeerJoined> named, after the handlers that act on them. It only keeps what no other state holds. The call's logic is unchanged, except that a hosted join's Calls entry now records the join's video, and that join and set_video now trigger a state update (neither emits an event of its own).

get -acc A ?-jid R? ?-chat C? answers list<GroupcallState> as it stands, so a host can re-seed after a restart or reattach.

Also in this PR (separate commit). The schema's AvatarMeta called the hash id, but the avatar metadata reply has always used hash. The schema now says hash, and a test checks the reply's keys against the schema.

Tests.

  • test_groupcall: 21 new State cases, all 158 pass. They cover the banner, joining then live, legs following their events, a leg ending and a new one starting, a hidden JID, leaving with others and alone, ended arriving before <Left>, a failed hosted join, the camera, one state per burst, get and its filters, a disconnect, and the JSON types on the wire.
  • test_avatar_xep0084: 12 pass.
  • Full Tcl unit suite (datachannel lane): 5,515 passed, 0 failed, 10 skipped. check-api is clean.
  • Not run: the webrtc lane and the integration rigs.

Schema additions are marked since 1.0.0-rc6. Nothing existing changes shape, apart from the AvatarMeta doc fix.

Adds `groupcall <State>` and `groupcall get`: the whole group call as one snapshot, for a host that draws from one rather than rebuilding it from the events. **Why.** quackchat's Qt `GroupCall` and its Android `GroupCallRegistry` both render from upstream tacky's `<State>`. Without it each would have to rebuild the snapshot from `<Changed>`, `<Joined>`, `<PeerJoined>`, `<PeerLeft>`, `<Left>`, `<Warning>`, `<VideoPreview>` and every leg's `calls` events. The Android service also has no simple way to make the `participants` requests that rebuild needs. **What a state holds.** - The room: `jid`, `chat`, `hosted`, `count`. - Ours: `phase` (`idle`, `joining`, `live`, `ended`), `reason`, `warning`, `since`, `video`, `camera`, `preview`. - `participants`: one row per occupant announcing the call or preparing to, us left out. Each row has `nick`, bare `jid`, `joined`, `video`, and our leg's state (`expected`, `connecting`, `active`, `ended`, `failed`) with its `reason`, `warning` and incoming `stream`. **When it is sent.** - At most once per room per event-loop turn, and only when something changed. - `ended` goes out once, ahead of `<Left>`, with no participants. - A last `idle` with count 0 and no participants means nothing is left to show. A disconnect sends one too. **How it is built.** It is a projection: groupcall.tcl subscribes to its own events, and to the calls events of the legs `<PeerJoined>` named, after the handlers that act on them. It only keeps what no other state holds. The call's logic is unchanged, except that a hosted join's `Calls` entry now records the join's video, and that join and `set_video` now trigger a state update (neither emits an event of its own). `get -acc A ?-jid R? ?-chat C?` answers `list<GroupcallState>` as it stands, so a host can re-seed after a restart or reattach. **Also in this PR (separate commit).** The schema's `AvatarMeta` called the hash `id`, but the `avatar metadata` reply has always used `hash`. The schema now says `hash`, and a test checks the reply's keys against the schema. **Tests.** - test_groupcall: 21 new State cases, all 158 pass. They cover the banner, joining then live, legs following their events, a leg ending and a new one starting, a hidden JID, leaving with others and alone, `ended` arriving before `<Left>`, a failed hosted join, the camera, one state per burst, `get` and its filters, a disconnect, and the JSON types on the wire. - test_avatar_xep0084: 12 pass. - Full Tcl unit suite (datachannel lane): 5,515 passed, 0 failed, 10 skipped. `check-api` is clean. - Not run: the webrtc lane and the integration rigs. Schema additions are marked `since 1.0.0-rc6`. Nothing existing changes shape, apart from the `AvatarMeta` doc fix.
groupcall <State> and groupcall get: the whole group call in one snapshot, for a host that draws from one rather than from the events it is made of (upstream tacky's <State>, which quackchat's Qt GroupCall and Android GroupCallRegistry read; a host otherwise rebuilds it from <Changed>, <Joined>, <PeerJoined>, <PeerLeft>, <Left>, <Warning>, <VideoPreview> and the calls events of each leg, which Android's service cannot ask participants for). A state is the room's jid, chat, hosted, phase (idle: a call in a group chat of ours we are not in; joining; live; ended), reason, warning, since, video, camera, preview, count and a participant per occupant announcing the call or preparing to, us left out, with nick, bare jid, joined, video, and our leg's state (expected, connecting, active, ended, failed), reason, warning and incoming stream. It goes out at most once per room per event-loop turn and only when it changed (an after-idle flush, compared with the last one sent); ended once, ahead of <Left>, with the call's video, camera and since, and no participants; a last idle with count 0 and nobody in it says there is nothing left to show, as after a disconnect, whose rooms forget their calls without a word. It is a projection: groupcall.tcl subscribes to its own events and the calls events of the sids <PeerJoined> handed out, after the handlers that act on them, and keeps only what no other state holds (when we announced, the camera's toggle, the call's last warning, each leg's warning, stream and whether its <Active> came, a leg that ended while its peer stays); the call's own logic is unchanged but for the hosted join's Calls entry, which now carries the join's video, and a touch from that join and from set_video, which emit nothing themselves. get -acc A ?-jid R? ?-chat C? answers list<GroupcallState> as it stands, for a re-seed. Schema: the request, the event, GroupcallState and GroupcallStateParticipant, since 1.0.0-rc6; wire.tcl types both; features.md's XEP-0272 row says so; test_wire_surface counts 388 requests. Tests in test_groupcall (gc_events leaves <State> out): the banner, nothing to show, joining then live with the self-view, legs following their calls events, a leg ended and a new one, the host backend's track id, another sid's events no news, a hidden JID, a participant leaving, our leave with others and alone, the ended state ahead of <Left>, a hosted join that fails, the camera, one state per burst, get and its filters, a disconnect, and the JSON both reach the host as; test_groupcall 158 passed, the Tcl unit suite 5,515 passed, 0 failed, 10 skipped (datachannel); check-api clean. a2e6278fad
Author
Owner

Reviewed and merged into develop by hand with a merge commit (923fb688 on develop; the forge does not mark a hand merge as merged, so this PR is closed here). On top of the branch: the module's destructor now cancels the flushes still due (3a8ec196). Two rc5 leftovers the full gate turned up beside it are fixed on develop too: the fault shim wraps tacky_net_socket_new_via (test_net_faults had failed since the proxy), and test_storage_package expects the proxy and tor keys. Gate: Tcl unit suite 5,525 passed; test_media_dtls_race and two Rust socket timing tests flake under the gate's parallel load and pass alone; check-api, check-headers and check-native-neutral clean.

Reviewed and merged into develop by hand with a merge commit (923fb688 on develop; the forge does not mark a hand merge as merged, so this PR is closed here). On top of the branch: the module's destructor now cancels the <State> flushes still due (3a8ec196). Two rc5 leftovers the full gate turned up beside it are fixed on develop too: the fault shim wraps tacky_net_socket_new_via (test_net_faults had failed since the proxy), and test_storage_package expects the proxy and tor keys. Gate: Tcl unit suite 5,525 passed; test_media_dtls_race and two Rust socket timing tests flake under the gate's parallel load and pass alone; check-api, check-headers and check-native-neutral clean.
ann closed this pull request 2026-10-16 07:48:35 +00:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/libtacky!6
No description provided.