@@ -125,6 +125,11 @@ async def _start_server(self) -> None:
125125 ping_interval = 20 ,
126126 )
127127
128+ # Get the actual port if OS assigned one (e.g., when port=0)
129+ if self .port == 0 :
130+ server_socket = list (self ._server .sockets )[0 ]
131+ self .port = server_socket .getsockname ()[1 ]
132+
128133 logger .info (f"WebSocket camera server started on { self .host } :{ self .port } " )
129134
130135 await self ._stop_event .wait ()
@@ -171,7 +176,7 @@ async def _ws_handler(self, conn: websockets.ServerConnection) -> None:
171176 logger .warning (f"Could not send welcome message to { client_addr } : { e } " )
172177
173178 async for message in conn :
174- frame = await self ._parse_message (message )
179+ frame = self ._parse_message (message )
175180 if frame is not None :
176181 # Drop old frames until there's room for the new one
177182 while True :
@@ -195,28 +200,29 @@ async def _ws_handler(self, conn: websockets.ServerConnection) -> None:
195200 self ._client = None
196201 logger .info (f"Client removed: { client_addr } " )
197202
198- async def _parse_message (self , message ) -> np .ndarray | None :
203+ def _parse_message (self , message : str | bytes ) -> np .ndarray | None :
199204 """Parse WebSocket message to extract frame."""
200205 try :
201- if self .frame_format == "base64 " :
202- # Expect base64 encoded image
206+ if self .frame_format == "binary " :
207+ # Expect raw binary image data
203208 if isinstance (message , str ):
204- image_data = base64 .b64decode (message )
209+ # Use latin-1 encoding to preserve binary data
210+ image_data = message .encode ("latin-1" )
205211 else :
206- image_data = base64 . b64decode ( message . decode ())
212+ image_data = message
207213
208- # Decode image
209214 nparr = np .frombuffer (image_data , np .uint8 )
210215 frame = cv2 .imdecode (nparr , cv2 .IMREAD_UNCHANGED )
211216 return frame
212217
213- elif self .frame_format == "binary " :
214- # Expect raw binary image data
218+ elif self .frame_format == "base64 " :
219+ # Expect base64 encoded image
215220 if isinstance (message , str ):
216- image_data = message . encode ( )
221+ image_data = base64 . b64decode ( message )
217222 else :
218- image_data = message
223+ image_data = base64 . b64decode ( message . decode ())
219224
225+ # Decode image
220226 nparr = np .frombuffer (image_data , np .uint8 )
221227 frame = cv2 .imdecode (nparr , cv2 .IMREAD_UNCHANGED )
222228 return frame
@@ -251,10 +257,11 @@ async def _parse_message(self, message) -> np.ndarray | None:
251257
252258 def _close_camera (self ):
253259 """Stop the WebSocket server."""
254- if self ._loop and not self ._loop .is_closed ():
260+ # Only attempt async cleanup if the event loop is running
261+ if self ._loop and not self ._loop .is_closed () and self ._loop .is_running ():
255262 try :
256263 future = asyncio .run_coroutine_threadsafe (self ._stop_and_disconnect_client (), self ._loop )
257- future .result (timeout = 1.0 )
264+ future .result (1.0 )
258265 except CancelledError :
259266 logger .debug (f"Error stopping WebSocket server: CancelledError" )
260267 except TimeoutError :
@@ -293,7 +300,8 @@ async def _stop_and_disconnect_client(self):
293300 except Exception as e :
294301 logger .warning (f"Error closing client in stop event: { e } " )
295302 finally :
296- await self ._client .close ()
303+ if self ._client :
304+ await self ._client .close ()
297305
298306 self ._stop_event .set ()
299307
@@ -308,14 +316,15 @@ def _read_frame(self) -> np.ndarray | None:
308316
309317 async def _send_to_client (self , message : str | bytes | dict ) -> None :
310318 """Send a message to the connected client."""
311- if self ._client is None :
312- raise ConnectionError ("No client connected to send message to" )
313-
314319 if isinstance (message , dict ):
315320 message = json .dumps (message )
316321
317- try :
318- await self ._client .send (message )
319- except Exception as e :
320- logger .warning (f"Error sending to client: { e } " )
321- raise
322+ async with self ._client_lock :
323+ if self ._client is None :
324+ raise ConnectionError ("No client connected to send message to" )
325+
326+ try :
327+ await self ._client .send (message )
328+ except Exception as e :
329+ logger .warning (f"Error sending to client: { e } " )
330+ raise
0 commit comments