From 246277646205ab4568ccc1e84a81a4b916ea176b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Tue, 29 Apr 2025 11:39:38 +0200 Subject: [PATCH 1/4] Update room.py --- livekit-rtc/livekit/rtc/room.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/livekit-rtc/livekit/rtc/room.py b/livekit-rtc/livekit/rtc/room.py index 1a6cf622..f1ea6d5e 100644 --- a/livekit-rtc/livekit/rtc/room.py +++ b/livekit-rtc/livekit/rtc/room.py @@ -24,6 +24,7 @@ from ._proto import ffi_pb2 as proto_ffi from ._proto import participant_pb2 as proto_participant from ._proto import room_pb2 as proto_room +from ._proto import stats_pb2 as proto_stats from ._proto.room_pb2 import ConnectionState from ._proto.track_pb2 import TrackKind from ._proto.rpc_pb2 import RpcMethodInvocationEvent @@ -120,6 +121,12 @@ class SipDTMF: """Participant who sent the DTMF digit. None when sent by a server SDK.""" +@dataclass +class SessionStats: + publisher_stats: list[proto_stats.RtcStats] + subscriber_stats: list[proto_stats.RtcStats] + + class ConnectError(Exception): def __init__(self, message: str): self.message = message @@ -408,6 +415,29 @@ def on_participant_connected(participant): # start listening to room events self._task = self._loop.create_task(self._listen_task()) + async def get_session_stats(self) -> SessionStats: + if not self.isconnected(): + raise RuntimeError("the room isn't connected") + + req = proto_ffi.FfiRequest() + req.get_session_stats.room_handle = self._ffi_handle.handle # type: ignore + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.get_session_stats.async_id == resp.get_session_stats.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.get_session_stats.error: + raise RuntimeError(cb.get_session_stats.error) + + publisher_stats = list(cb.get_session_stats.result.publisher_stats) + subscriber_stats = list(cb.get_session_stats.result.subscriber_stats) + return SessionStats(publisher_stats=publisher_stats, subscriber_stats=subscriber_stats) + def register_byte_stream_handler(self, topic: str, handler: ByteStreamHandler): existing_handler = self._byte_stream_handlers.get(topic) if existing_handler is None: @@ -446,6 +476,7 @@ async def disconnect(self) -> None: await queue.wait_for(lambda e: e.disconnect.async_id == resp.disconnect.async_id) finally: FfiClient.instance.queue.unsubscribe(queue) + await self._task FfiClient.instance.queue.unsubscribe(self._ffi_queue) From e0d652da8d72afb7d2901e3edaab0ae7ee6ffa73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Tue, 29 Apr 2025 11:40:26 +0200 Subject: [PATCH 2/4] expose --- livekit-rtc/livekit/rtc/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/livekit-rtc/livekit/rtc/__init__.py b/livekit-rtc/livekit/rtc/__init__.py index 8b3e7729..7a17d426 100644 --- a/livekit-rtc/livekit/rtc/__init__.py +++ b/livekit-rtc/livekit/rtc/__init__.py @@ -50,7 +50,15 @@ Participant, RemoteParticipant, ) -from .room import ConnectError, DataPacket, Room, RoomOptions, RtcConfiguration, SipDTMF +from .room import ( + ConnectError, + DataPacket, + Room, + RoomOptions, + RtcConfiguration, + SipDTMF, + SessionStats, +) from .track import ( AudioTrack, LocalAudioTrack, @@ -123,6 +131,7 @@ "RoomOptions", "RtcConfiguration", "SipDTMF", + "SessionStats", "DataPacket", "LocalAudioTrack", "LocalVideoTrack", From 5f31ead9a6b438e0e8cece3a507bf47d19d73738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Tue, 29 Apr 2025 11:42:48 +0200 Subject: [PATCH 3/4] fmt --- livekit-api/livekit/api/room_service.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/livekit-api/livekit/api/room_service.py b/livekit-api/livekit/api/room_service.py index b12469c4..3d521548 100644 --- a/livekit-api/livekit/api/room_service.py +++ b/livekit-api/livekit/api/room_service.py @@ -215,7 +215,11 @@ async def forward_participant(self, forward: ForwardParticipantRequest) -> None: SVC, "ForwardParticipant", forward, - self._auth_header(VideoGrants(room_admin=True, room=forward.room, destination_room=forward.destination_room)), + self._auth_header( + VideoGrants( + room_admin=True, room=forward.room, destination_room=forward.destination_room + ) + ), ForwardParticipantResponse, ) From fc490b21d715943c0899a9c0493e19143bfb1b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Thu, 8 May 2025 17:47:00 +0200 Subject: [PATCH 4/4] session_stats->rtc_stats --- livekit-rtc/livekit/rtc/__init__.py | 4 ++-- livekit-rtc/livekit/rtc/room.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/livekit-rtc/livekit/rtc/__init__.py b/livekit-rtc/livekit/rtc/__init__.py index 7a17d426..dd02cc7a 100644 --- a/livekit-rtc/livekit/rtc/__init__.py +++ b/livekit-rtc/livekit/rtc/__init__.py @@ -57,7 +57,7 @@ RoomOptions, RtcConfiguration, SipDTMF, - SessionStats, + RtcStats, ) from .track import ( AudioTrack, @@ -131,7 +131,7 @@ "RoomOptions", "RtcConfiguration", "SipDTMF", - "SessionStats", + "RtcStats", "DataPacket", "LocalAudioTrack", "LocalVideoTrack", diff --git a/livekit-rtc/livekit/rtc/room.py b/livekit-rtc/livekit/rtc/room.py index f1ea6d5e..d532cd0a 100644 --- a/livekit-rtc/livekit/rtc/room.py +++ b/livekit-rtc/livekit/rtc/room.py @@ -122,7 +122,7 @@ class SipDTMF: @dataclass -class SessionStats: +class RtcStats: publisher_stats: list[proto_stats.RtcStats] subscriber_stats: list[proto_stats.RtcStats] @@ -415,7 +415,7 @@ def on_participant_connected(participant): # start listening to room events self._task = self._loop.create_task(self._listen_task()) - async def get_session_stats(self) -> SessionStats: + async def get_rtc_stats(self) -> RtcStats: if not self.isconnected(): raise RuntimeError("the room isn't connected") @@ -436,7 +436,8 @@ async def get_session_stats(self) -> SessionStats: publisher_stats = list(cb.get_session_stats.result.publisher_stats) subscriber_stats = list(cb.get_session_stats.result.subscriber_stats) - return SessionStats(publisher_stats=publisher_stats, subscriber_stats=subscriber_stats) + + return RtcStats(publisher_stats=publisher_stats, subscriber_stats=subscriber_stats) def register_byte_stream_handler(self, topic: str, handler: ByteStreamHandler): existing_handler = self._byte_stream_handlers.get(topic)