diff --git a/voice.py b/voice.py index aea4064..60c245b 100644 --- a/voice.py +++ b/voice.py @@ -709,6 +709,27 @@ class VoiceSession: track_source = getattr(pub, 'source', None) or "unknown" self._video_track = t logger.info("Video track stored from %s source=%s for on-demand vision", p.identity, track_source) + # Screen share starts → Element Call rotates E2EE key. + # Proactively poll timeline for the new key instead of waiting + # for DEC_FAILED (MAT-164). + async def _proactive_key_poll(pid=p.identity): + pre_key = self._caller_key + for attempt in range(6): # 6 × 500ms = 3s + await asyncio.sleep(0.5) + if self._caller_key != pre_key: + logger.info("Proactive poll: key rotated via sync (attempt %d)", attempt + 1) + return + new_key = await self._fetch_encryption_key_http() + if new_key and new_key != pre_key: + logger.info("Proactive poll: got new key from timeline (attempt %d, %s)", + attempt + 1, new_key.hex()[:8]) + self.on_encryption_key( + self._caller_identity.split(":")[0] if self._caller_identity else "", + self._caller_identity.split(":")[-1] if self._caller_identity else "", + new_key, 0) + return + logger.info("Proactive poll: no key rotation after 3s") + asyncio.ensure_future(_proactive_key_poll()) if int(t.kind) in (1, 2) and e2ee_opts is not None: # audio + video tracks caller_id = p.identity track_type = "video" if int(t.kind) == 2 else "audio" @@ -754,8 +775,8 @@ class VoiceSession: now = time.monotonic() if int(state) == 3: _dec_failed_count[p_id] = _dec_failed_count.get(p_id, 0) + 1 - # After 3+ DEC_FAILED: re-fetch key from timeline (key may have rotated) - if _dec_failed_count[p_id] >= 3 and not _refetch_in_progress: + # After 1+ DEC_FAILED: re-fetch key from timeline (key may have rotated) + if _dec_failed_count[p_id] >= 1 and not _refetch_in_progress: _refetch_in_progress = True _p_id_copy = p_id # capture for closure async def _refetch_key(): @@ -781,9 +802,9 @@ class VoiceSession: finally: _refetch_in_progress = False asyncio.ensure_future(_refetch_key()) - # Cooldown: only re-key every 5s to avoid tight loops + # Cooldown: only re-key every 2s to avoid tight loops last = _last_rekey_time.get(p_id, 0) - if (now - last) < 5.0: + if (now - last) < 2.0: return _last_rekey_time[p_id] = now if self._caller_all_keys: