feat: 长保持连接,控制端可重启
This commit is contained in:
@@ -50,6 +50,9 @@ class OmniSocketVideoReceiver:
|
||||
self._latency_samples_ms: deque[float] = deque(maxlen=VIDEO_TIMESTAMP_SAMPLE_SIZE)
|
||||
self._frames_received = 0
|
||||
self._last_error = ""
|
||||
self._reconnect_count = 0
|
||||
self._ever_connected = False
|
||||
self._closing = threading.Event()
|
||||
self._load_backend()
|
||||
|
||||
def _load_backend(self) -> None:
|
||||
@@ -76,7 +79,7 @@ class OmniSocketVideoReceiver:
|
||||
return
|
||||
|
||||
with self._lock:
|
||||
if self._started:
|
||||
if self._started or self._closing.is_set():
|
||||
return
|
||||
self._started = True
|
||||
self._thread = threading.Thread(
|
||||
@@ -167,14 +170,19 @@ class OmniSocketVideoReceiver:
|
||||
return timestamp_ns, VIDEO_TIMESTAMP_UNIT, VIDEO_TIMESTAMP_ENDIANNESS
|
||||
|
||||
def _run(self) -> None:
|
||||
while True:
|
||||
while not self._closing.is_set():
|
||||
try:
|
||||
session, buffer_bytes = self._connect_session()
|
||||
self._session = session
|
||||
self._last_error = ""
|
||||
with self._lock:
|
||||
self._session = session
|
||||
self._last_error = ""
|
||||
if self._ever_connected:
|
||||
self._reconnect_count += 1
|
||||
else:
|
||||
self._ever_connected = True
|
||||
buffer = bytearray(buffer_bytes)
|
||||
|
||||
while True:
|
||||
while not self._closing.is_set():
|
||||
meta = session.recv_into(buffer, timeout_ms=1000)
|
||||
if meta is None:
|
||||
continue
|
||||
@@ -207,15 +215,25 @@ class OmniSocketVideoReceiver:
|
||||
self._latency_samples_ms.append(latency_ms)
|
||||
self._frames_received += 1
|
||||
except Exception as error: # pragma: no cover - runtime integration path
|
||||
self._last_error = str(error)
|
||||
time.sleep(2)
|
||||
if not self._closing.is_set():
|
||||
session_error = ""
|
||||
if self._session is not None:
|
||||
try:
|
||||
session_error = str(dict(self._session.stats()).get("last_server_error", "") or "")
|
||||
except Exception:
|
||||
session_error = ""
|
||||
self._last_error = session_error or str(error)
|
||||
time.sleep(2)
|
||||
finally:
|
||||
if self._session is not None:
|
||||
try:
|
||||
self._session.close()
|
||||
except Exception:
|
||||
pass
|
||||
with self._lock:
|
||||
self._session = None
|
||||
if self._closing.is_set():
|
||||
self._started = False
|
||||
|
||||
def get_latest_frame(self) -> bytes | None:
|
||||
self.ensure_started()
|
||||
@@ -231,11 +249,11 @@ class OmniSocketVideoReceiver:
|
||||
with self._lock:
|
||||
session = self._session
|
||||
if session is None:
|
||||
return {"connected": 0}
|
||||
return {"connected": 0, "registered": 0, "last_server_error": self._last_error}
|
||||
try:
|
||||
return dict(session.stats())
|
||||
except Exception:
|
||||
return {"connected": 0}
|
||||
return {"connected": 0, "registered": 0, "last_server_error": self._last_error}
|
||||
|
||||
def session_kcp_stats(self) -> dict[str, Any]:
|
||||
self.ensure_started()
|
||||
@@ -248,6 +266,7 @@ class OmniSocketVideoReceiver:
|
||||
config = load_omnisocket_config()
|
||||
transport_cfg = config.get("transport", {})
|
||||
video_cfg = config.get("video_receiver", {})
|
||||
session_stats = self.session_stats()
|
||||
with self._lock:
|
||||
has_recent_frame = self._latest_frame is not None and (
|
||||
time.time() - self._latest_received_at <= OMNISOCKET_FRAME_FRESH_SECONDS
|
||||
@@ -276,9 +295,12 @@ class OmniSocketVideoReceiver:
|
||||
"backend_ready": self._session_cls is not None,
|
||||
"mode": VIDEO_SOURCE_MODE,
|
||||
"connected": self._session is not None,
|
||||
"registered": bool(session_stats.get("registered", 0)),
|
||||
"has_recent_frame": has_recent_frame,
|
||||
"frames_received": self._frames_received,
|
||||
"latest_sequence": self._latest_sequence,
|
||||
"reconnect_count": self._reconnect_count,
|
||||
"last_server_error": str(session_stats.get("last_server_error", "") or ""),
|
||||
"last_error": self._last_error,
|
||||
"config_path": str(OMNISOCKET_CONFIG_PATH),
|
||||
"server_addr": str(transport_cfg.get("server_addr", "")),
|
||||
@@ -288,6 +310,19 @@ class OmniSocketVideoReceiver:
|
||||
"timing": timing_status,
|
||||
}
|
||||
|
||||
def close(self) -> None:
|
||||
self._closing.set()
|
||||
with self._lock:
|
||||
session = self._session
|
||||
if session is not None:
|
||||
try:
|
||||
session.close()
|
||||
except Exception:
|
||||
pass
|
||||
thread = self._thread
|
||||
if thread is not None and thread.is_alive():
|
||||
thread.join(timeout=0.5)
|
||||
|
||||
|
||||
class VideoFrameService:
|
||||
def __init__(self, receiver: OmniSocketVideoReceiver) -> None:
|
||||
|
||||
Reference in New Issue
Block a user