1717import asyncio
1818import json
1919from dataclasses import dataclass
20- from typing import Any , AsyncIterator , Optional
20+ from typing import TYPE_CHECKING , Any , AsyncIterator , Optional
2121
2222from ._ffi_client import FfiClient , FfiHandle
2323from ._proto import audio_frame_pb2 as proto_audio_frame
3030from .track import Track
3131from .frame_processor import FrameProcessor
3232
33+ if TYPE_CHECKING :
34+ from .room import Room
35+
3336
3437@dataclass
3538class AudioFrameEvent :
@@ -65,6 +68,7 @@ def __init__(
6568 num_channels : int = 1 ,
6669 frame_size_ms : int | None = None ,
6770 noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [AudioFrame ]] = None ,
71+ room : Optional ["Room" ] = None ,
6872 ** kwargs ,
6973 ) -> None :
7074 """Initialize an `AudioStream` instance.
@@ -81,6 +85,9 @@ def __init__(
8185 noise_cancellation (Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]], optional):
8286 If noise cancellation is used, pass a `NoiseCancellationOptions` or `FrameProcessor[AudioFrame]` instance
8387 created by the noise cancellation module.
88+ room (Optional[Room], optional): The room this stream's track belongs to, used to
89+ resolve `room_name`, `participant_identity`, and `publication_sid`. May be `None`
90+ if the track is not (yet) associated with a room.
8491
8592 Example:
8693 ```python
@@ -98,6 +105,8 @@ def __init__(
98105 ```
99106 """
100107 self ._track : Track | None = track
108+ self ._room : Room | None = room
109+ print ("ROOM:" , room )
101110 self ._sample_rate = sample_rate
102111 self ._num_channels = num_channels
103112 self ._frame_size_ms = frame_size_ms
@@ -132,6 +141,9 @@ def __init__(
132141 self ._ffi_handle = FfiHandle (stream .handle .id )
133142 self ._info = stream .info
134143
144+ if self ._track is not None :
145+ self ._track ._register_audio_stream (self )
146+
135147 @classmethod
136148 def from_participant (
137149 cls ,
@@ -144,6 +156,7 @@ def from_participant(
144156 num_channels : int = 1 ,
145157 frame_size_ms : int | None = None ,
146158 noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [AudioFrame ]] = None ,
159+ room : Optional ["Room" ] = None ,
147160 ) -> AudioStream :
148161 """Create an `AudioStream` from a participant's audio track.
149162
@@ -181,6 +194,7 @@ def from_participant(
181194 num_channels = num_channels ,
182195 noise_cancellation = noise_cancellation ,
183196 frame_size_ms = frame_size_ms ,
197+ room = room ,
184198 )
185199
186200 @classmethod
@@ -194,6 +208,7 @@ def from_track(
194208 num_channels : int = 1 ,
195209 frame_size_ms : int | None = None ,
196210 noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [AudioFrame ]] = None ,
211+ room : Optional ["Room" ] = None ,
197212 ) -> AudioStream :
198213 """Create an `AudioStream` from an existing audio track.
199214
@@ -227,8 +242,54 @@ def from_track(
227242 num_channels = num_channels ,
228243 noise_cancellation = noise_cancellation ,
229244 frame_size_ms = frame_size_ms ,
245+ room = room ,
230246 )
231247
248+ def _set_room (self , room : Optional ["Room" ]) -> None :
249+ self ._room = room
250+ print ("ROOM UPDATE:" , room )
251+
252+ @property
253+ def room (self ) -> Optional ["Room" ]:
254+ return self ._room
255+
256+ @property
257+ def room_name (self ) -> Optional [str ]:
258+ return self ._room .name if self ._room is not None else None
259+
260+ @property
261+ def participant_identity (self ) -> Optional [str ]:
262+ pub = self ._find_publication ()
263+ if pub is None :
264+ return None
265+ identity , _ = pub
266+ return identity
267+
268+ @property
269+ def publication_sid (self ) -> Optional [str ]:
270+ pub = self ._find_publication ()
271+ if pub is None :
272+ return None
273+ _ , sid = pub
274+ return sid
275+
276+ def _find_publication (self ) -> Optional [tuple [str , str ]]:
277+ if self ._room is None or self ._track is None :
278+ return None
279+ track_sid = self ._track .sid
280+ if not track_sid :
281+ return None
282+ for participant in self ._room .remote_participants .values ():
283+ publication = participant .track_publications .get (track_sid )
284+ if publication is not None :
285+ return participant .identity , publication .sid
286+ local = self ._room ._local_participant
287+ if local is not None :
288+ for publication in local .track_publications .values ():
289+ if publication .sid == track_sid :
290+ return local .identity , publication .sid
291+ return None
292+
232293 def __del__ (self ) -> None :
233294 FfiClient .instance .queue .unsubscribe (self ._ffi_queue )
234295
@@ -303,6 +364,8 @@ async def aclose(self) -> None:
303364 This method cleans up resources associated with the audio stream and waits for
304365 any pending operations to complete.
305366 """
367+ if self ._track is not None :
368+ self ._track ._unregister_audio_stream (self )
306369 self ._ffi_handle .dispose ()
307370 await self ._task
308371
0 commit comments