| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -57,9 +57,17 @@ else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					class CameraView: | 
					 | 
					 | 
					 | 
					class CameraView: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def __init__(self, name: str, stream_type: VisionStreamType): | 
					 | 
					 | 
					 | 
					  def __init__(self, name: str, stream_type: VisionStreamType): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self.client = VisionIpcClient(name, stream_type, conflate=True) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self._name = name | 
					 | 
					 | 
					 | 
					    self._name = name | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    # Primary stream | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self.client = VisionIpcClient(name, stream_type, conflate=True) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self._stream_type = stream_type | 
					 | 
					 | 
					 | 
					    self._stream_type = stream_type | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self.available_streams: list[VisionStreamType] = [] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    # Target stream for switching | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._target_client: VisionIpcClient | None = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._target_stream_type: VisionStreamType | None = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._switching: bool = False | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self._texture_needs_update = True | 
					 | 
					 | 
					 | 
					    self._texture_needs_update = True | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self.last_connection_attempt: float = 0.0 | 
					 | 
					 | 
					 | 
					    self.last_connection_attempt: float = 0.0 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -91,12 +99,20 @@ class CameraView: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self._placeholder_color = color | 
					 | 
					 | 
					 | 
					    self._placeholder_color = color | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def switch_stream(self, stream_type: VisionStreamType) -> None: | 
					 | 
					 | 
					 | 
					  def switch_stream(self, stream_type: VisionStreamType) -> None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self._stream_type != stream_type: | 
					 | 
					 | 
					 | 
					    if self._stream_type == stream_type: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      cloudlog.debug(f'switching stream from {self._stream_type} to {stream_type}') | 
					 | 
					 | 
					 | 
					      return | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._clear_textures() | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self.frame = None | 
					 | 
					 | 
					 | 
					    if self._switching and self._target_stream_type == stream_type: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._stream_type = stream_type | 
					 | 
					 | 
					 | 
					      return | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self.client = VisionIpcClient(self._name, stream_type, conflate=True) | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    cloudlog.debug(f'Preparing switch from {self._stream_type} to {stream_type}') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if self._target_client: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      del self._target_client | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._target_stream_type = stream_type | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._target_client = VisionIpcClient(self._name, stream_type, conflate=True) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    self._switching = True | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  @property | 
					 | 
					 | 
					 | 
					  @property | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def stream_type(self) -> VisionStreamType: | 
					 | 
					 | 
					 | 
					  def stream_type(self) -> VisionStreamType: | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -135,6 +151,9 @@ class CameraView: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ]) | 
					 | 
					 | 
					 | 
					    ]) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def render(self, rect: rl.Rectangle): | 
					 | 
					 | 
					 | 
					  def render(self, rect: rl.Rectangle): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if self._switching: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._handle_switch() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if not self._ensure_connection(): | 
					 | 
					 | 
					 | 
					    if not self._ensure_connection(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._draw_placeholder(rect) | 
					 | 
					 | 
					 | 
					      self._draw_placeholder(rect) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      return | 
					 | 
					 | 
					 | 
					      return | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -226,6 +245,7 @@ class CameraView: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def _ensure_connection(self) -> bool: | 
					 | 
					 | 
					 | 
					  def _ensure_connection(self) -> bool: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if not self.client.is_connected(): | 
					 | 
					 | 
					 | 
					    if not self.client.is_connected(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self.frame = None | 
					 | 
					 | 
					 | 
					      self.frame = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self.available_streams.clear() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      # Throttle connection attempts | 
					 | 
					 | 
					 | 
					      # Throttle connection attempts | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      current_time = rl.get_time() | 
					 | 
					 | 
					 | 
					      current_time = rl.get_time() | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -237,16 +257,57 @@ class CameraView: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return False | 
					 | 
					 | 
					 | 
					        return False | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      cloudlog.debug(f"Connected to {self._name} stream: {self._stream_type}, buffers: {self.client.num_buffers}") | 
					 | 
					 | 
					 | 
					      cloudlog.debug(f"Connected to {self._name} stream: {self._stream_type}, buffers: {self.client.num_buffers}") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._clear_textures() | 
					 | 
					 | 
					 | 
					      self._initialize_textures() | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self.available_streams = self.client.available_streams(self._name, block=False) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return True | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  def _handle_switch(self) -> None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Check if target stream is ready and switch immediately.""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if not self._target_client or not self._switching: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    # Try to connect target if needed | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if not self._target_client.is_connected(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      if not self._target_client.connect(False) or not self._target_client.num_buffers: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      cloudlog.debug(f"Target stream connected: {self._target_stream_type}") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    # Check if target has frames ready | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    target_frame = self._target_client.recv(timeout_ms=0) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if target_frame: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self.frame = target_frame # Update current frame to target frame | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._complete_switch() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  def _complete_switch(self) -> None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      """Instantly switch to target stream.""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      cloudlog.debug(f"Switching to {self._target_stream_type}") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      # Clean up current resources | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      if self.client: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        del self.client | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      # Switch to target | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self.client = self._target_client | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._stream_type = self._target_stream_type | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._texture_needs_update = True | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      # Reset state | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._target_client = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._target_stream_type = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._switching = False | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      # Initialize textures for new stream | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._initialize_textures() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  def _initialize_textures(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._clear_textures() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if not TICI: | 
					 | 
					 | 
					 | 
					      if not TICI: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), | 
					 | 
					 | 
					 | 
					        self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) | 
					 | 
					 | 
					 | 
					          int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), | 
					 | 
					 | 
					 | 
					        self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) | 
					 | 
					 | 
					 | 
					          int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return True | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def _clear_textures(self): | 
					 | 
					 | 
					 | 
					  def _clear_textures(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self.texture_y and self.texture_y.id: | 
					 | 
					 | 
					 | 
					    if self.texture_y and self.texture_y.id: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      rl.unload_texture(self.texture_y) | 
					 | 
					 | 
					 | 
					      rl.unload_texture(self.texture_y) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |