rtsp_player.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import av
  2. import numpy as np
  3. import time
  4. import cv2
  5. import logging
  6. from PySide6.QtCore import QThread, Signal, QMutex
  7. from PySide6.QtGui import QImage, QPixmap
  8. from typing import List, Dict, Any
  9. logger = logging.getLogger(__name__)
  10. class RTSPPlayer(QThread):
  11. frame_ready = Signal(QPixmap)
  12. first_frame_received = Signal()
  13. timeout = Signal()
  14. def __init__(self, rtsp_url: str, target_width: int = 640, target_height: int = 360, timeout_sec: float = 5.0):
  15. super().__init__()
  16. self.rtsp_url = rtsp_url
  17. self.target_width = target_width
  18. self.target_height = target_height
  19. self.timeout_sec = timeout_sec
  20. self.running = False
  21. self.ai_objects: List[Dict[str, Any]] = []
  22. self.target_fps = 25
  23. self.mutex = QMutex()
  24. def set_target_size(self, width: int, height: int):
  25. self.mutex.lock()
  26. self.target_width = width
  27. self.target_height = height
  28. self.mutex.unlock()
  29. def set_ai_objects(self, objects):
  30. self.ai_objects = objects
  31. def run(self):
  32. self.running = True
  33. options = {
  34. 'rtsp_transport': 'tcp',
  35. 'buffer_size': '1024',
  36. 'max_delay': '50000',
  37. 'flags': 'low_delay',
  38. 'fflags': 'nobuffer+discardcorrupt',
  39. 'analyzeduration': '100000',
  40. 'probesize': '100000',
  41. 'timeout': str(int(self.timeout_sec * 1000000)),
  42. 'stimeout': str(int(self.timeout_sec * 1000000)),
  43. }
  44. start_time = time.time()
  45. first_packet = True
  46. container = None
  47. try:
  48. container = av.open(self.rtsp_url, options=options)
  49. video_stream = container.streams.video[0]
  50. video_stream.thread_type = 'AUTO'
  51. video_stream.thread_count = 1
  52. last_emit = 0
  53. frame_interval = 1.0 / self.target_fps
  54. for packet in container.demux(video_stream):
  55. if not self.running:
  56. break
  57. if first_packet:
  58. first_packet = False
  59. self.first_frame_received.emit()
  60. for frame in packet.decode():
  61. if not self.running:
  62. break
  63. img = frame.to_rgb().to_ndarray()
  64. now = time.time()
  65. if now - last_emit >= frame_interval:
  66. # 先缩放到目标尺寸
  67. self.mutex.lock()
  68. tw, th = self.target_width, self.target_height
  69. self.mutex.unlock()
  70. if tw > 0 and th > 0 and (img.shape[1] != tw or img.shape[0] != th):
  71. img = cv2.resize(img, (tw, th), interpolation=cv2.INTER_LINEAR)
  72. # 再在缩放后的图像上绘制 AI 标注
  73. for obj in self.ai_objects:
  74. x = obj.get('x', 0)
  75. y = obj.get('y', 0)
  76. w = obj.get('w', 0)
  77. h = obj.get('h', 0)
  78. label = obj.get('label', '')
  79. color = obj.get('color', (0, 255, 0))
  80. cv2.rectangle(img, (x, y), (x+w, y+h), color, 2)
  81. cv2.putText(img, label, (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
  82. # 转换为 QPixmap
  83. h, w, ch = img.shape
  84. bytes_per_line = ch * w
  85. qt_img = QImage(img.data, w, h, bytes_per_line, QImage.Format_RGB888)
  86. pixmap = QPixmap.fromImage(qt_img)
  87. self.frame_ready.emit(pixmap)
  88. last_emit = now
  89. if first_packet and (time.time() - start_time) > self.timeout_sec:
  90. logger.warning("RTSP连接超时(未收到任何包)")
  91. self.timeout.emit()
  92. self.running = False
  93. break
  94. if not self.running:
  95. break
  96. except Exception as e:
  97. logger.error(f"RTSP错误: {e}")
  98. if first_packet:
  99. self.timeout.emit()
  100. finally:
  101. if container:
  102. container.close()
  103. self.running = False
  104. def stop(self):
  105. self.running = False
  106. self.wait()