refactor: update FFmpegRTSPReader to use JPG format for single frame updates and improve image quality
All checks were successful
Build Worker Base and Application Images / check-base-changes (push) Successful in 7s
Build Worker Base and Application Images / build-base (push) Has been skipped
Build Worker Base and Application Images / build-docker (push) Successful in 2m48s
Build Worker Base and Application Images / deploy-stack (push) Successful in 10s

This commit is contained in:
Siwat Sirichai 2025-09-26 02:51:30 +07:00
parent 95c39a008f
commit 73c3367681

View file

@ -94,16 +94,19 @@ class FFmpegRTSPReader:
self.temp_file = f"/tmp/claude/camera_{self.camera_id.replace(' ', '_')}.raw" self.temp_file = f"/tmp/claude/camera_{self.camera_id.replace(' ', '_')}.raw"
os.makedirs("/tmp/claude", exist_ok=True) os.makedirs("/tmp/claude", exist_ok=True)
# Change to JPG format which properly supports -update 1
self.temp_file = f"/tmp/claude/camera_{self.camera_id.replace(' ', '_')}.jpg"
cmd = [ cmd = [
'ffmpeg', 'ffmpeg',
'-hwaccel', 'cuda', '-hwaccel', 'cuda',
'-hwaccel_device', '0', '-hwaccel_device', '0',
'-rtsp_transport', 'tcp', '-rtsp_transport', 'tcp',
'-i', self.rtsp_url, '-i', self.rtsp_url,
'-f', 'rawvideo', '-f', 'image2',
'-pix_fmt', 'bgr24', '-update', '1', # This actually works with image2 format
'-q:v', '2', # High quality JPEG
'-an', # No audio '-an', # No audio
'-update', '1', # Update single frame in place
'-y', # Overwrite output file '-y', # Overwrite output file
self.temp_file self.temp_file
] ]
@ -173,32 +176,27 @@ class FFmpegRTSPReader:
if self.frame_ready_event.wait(timeout=restart_check_interval): if self.frame_ready_event.wait(timeout=restart_check_interval):
self.frame_ready_event.clear() self.frame_ready_event.clear()
# Read current frame with concurrency safety # Read JPEG frame with concurrency safety
try: try:
# Try to read frame multiple times to handle race conditions # Try to read JPEG multiple times to handle race conditions
frame_data = None frame = None
for attempt in range(3): for attempt in range(3):
try: try:
with open(self.temp_file, 'rb') as f: # Read and decode JPEG directly
frame_data = f.read(bytes_per_frame) frame = cv2.imread(self.temp_file)
# Validate we got a complete frame if frame is not None and frame.shape == (self.height, self.width, 3):
if len(frame_data) == bytes_per_frame: break
break else:
else: logger.debug(f"Camera {self.camera_id}: Invalid frame shape or None, attempt {attempt+1}")
logger.debug(f"Camera {self.camera_id}: Partial read {len(frame_data)}/{bytes_per_frame}, attempt {attempt+1}") time.sleep(0.01) # Brief wait before retry
time.sleep(0.01) # Brief wait before retry
except (IOError, OSError) as e: except (IOError, OSError) as e:
logger.debug(f"Camera {self.camera_id}: Read error on attempt {attempt+1}: {e}") logger.debug(f"Camera {self.camera_id}: Read error on attempt {attempt+1}: {e}")
time.sleep(0.01) time.sleep(0.01)
if frame_data and len(frame_data) == bytes_per_frame: if frame is not None:
# Convert to numpy array # Call frame callback directly
frame = np.frombuffer(frame_data, dtype=np.uint8)
frame = frame.reshape((self.height, self.width, 3))
# Call frame callback directly - trust the retry logic caught corruption
if self.frame_callback: if self.frame_callback:
self.frame_callback(self.camera_id, frame) self.frame_callback(self.camera_id, frame)
@ -207,10 +205,10 @@ class FFmpegRTSPReader:
# Log progress # Log progress
current_time = time.time() current_time = time.time()
if current_time - last_log_time >= 30: if current_time - last_log_time >= 30:
logger.info(f"Camera {self.camera_id}: {frame_count} frames processed reactively") logger.info(f"Camera {self.camera_id}: {frame_count} JPEG frames processed reactively")
last_log_time = current_time last_log_time = current_time
else: else:
logger.debug(f"Camera {self.camera_id}: Failed to read complete frame after retries") logger.debug(f"Camera {self.camera_id}: Failed to read valid JPEG after retries")
except (IOError, OSError) as e: except (IOError, OSError) as e:
logger.debug(f"Camera {self.camera_id}: File read error: {e}") logger.debug(f"Camera {self.camera_id}: File read error: {e}")