@@ -80,14 +80,19 @@ def playback_worker(
8080 playback_queue .task_done ()
8181
8282
83- def receiver_loop () -> None :
83+ def receiver_loop (stop_event : threading . Event | None = None ) -> None :
8484 """
8585 Main receiver loop entry point.
8686
8787 Listens for incoming packets, deserializes JanusPackets, synthesizes audio,
8888 and plays it through the audio service. Supports both TCP and UDP protocols.
8989 Configuration is loaded from environment variables.
9090
91+ Args:
92+ stop_event: Optional threading event to signal shutdown. If None, creates
93+ an internal event and waits for KeyboardInterrupt. If provided,
94+ exits when the event is set.
95+
9196 Returns:
9297 None
9398
@@ -122,7 +127,13 @@ def receiver_loop() -> None:
122127 logger .info (f"Listening for Transmissions on UDP port { receiver_port } ..." )
123128
124129 playback_queue = queue .Queue (maxsize = 100 )
125- stop_event = threading .Event ()
130+
131+ # Use provided stop_event or create internal one
132+ if stop_event is None :
133+ stop_event = threading .Event ()
134+ use_keyboard_interrupt = True
135+ else :
136+ use_keyboard_interrupt = False
126137
127138 playback_thread = threading .Thread (
128139 target = playback_worker ,
@@ -133,6 +144,10 @@ def receiver_loop() -> None:
133144
134145 try :
135146 while True :
147+ # Exit if stop_event is set (test mode)
148+ if not use_keyboard_interrupt and stop_event .is_set ():
149+ break
150+
136151 try :
137152 if use_tcp :
138153 length_bytes = recv_exact (sock , 4 )
@@ -146,7 +161,12 @@ def receiver_loop() -> None:
146161 logger .info ("Connection closed while reading packet" )
147162 break
148163 else :
149- data , addr = sock .recvfrom (4096 )
164+ # Set timeout for UDP to allow periodic stop_event checking
165+ sock .settimeout (0.5 )
166+ try :
167+ data , addr = sock .recvfrom (4096 )
168+ except socket .timeout :
169+ continue
150170
151171 try :
152172 packet = JanusPacket .deserialize (data )
0 commit comments