1. 多路视频流接入与统一管理
任务说明
本案例实现一个多路RTSP视频流接入管理器,支持动态添加/移除视频源,并统一跟踪各路的连接状态。核心目标是:给定若干RTSP地址,能够建立连接、读取视频帧、感知断流,并在运行中增删视频源。
环境依赖
需要 opencv-python(含cv2)与Python 3.8+。OpenCV自带RTSP支持,无需额外后端。
完整实现
import cv2
import threading
import time
from dataclasses import dataclass, field
from typing import Dict, Optional
@dataclass
class StreamSource:
"""单个视频源的状态与捕获句柄"""
url: str
cap: Optional[cv2.VideoCapture] = None
connected: bool = False
last_frame_time: float = 0.0
error: str = ""
lock: threading.Lock = field(default_factory=threading.Lock)
class StreamManager:
"""多路RTSP流统一管理器"""
def __init__(self, timeout_sec: float = 5.0):
self._streams: Dict[str, StreamSource] = {}
self._timeout = timeout_sec
self._monitor_thread: Optional[threading.Thread] = None
self._running = False
def add_stream(self, stream_id: str, url: str) -> bool:
"""动态添加视频源,返回是否成功建立连接"""
if stream_id in self._streams:
return False
cap = cv2.VideoCapture(url)
# 设置RTSP低延迟选项
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
ok, _ = cap.read()
src = StreamSource(url=url, cap=cap, connected=ok)
if not ok:
src.error = "Failed to open or read stream"
self._streams[stream_id] = src
return ok
def remove_stream(self, stream_id: str) -> bool:
"""动态移除视频源并释放资源"""
src = self._streams.pop(stream_id, None)
if src is None:
return False
with src.lock:
if src.cap:
src.cap.release()
return True
def read_frame(self, stream_id: str):
"""读取指定流的一帧,返回(ret, frame);断流时ret为False"""
src = self._streams.get(stream_id)
if src is None or not src.connected:
return False, None
with src.lock:
ret, frame = src.cap.read()
if ret:
src.last_frame_time = time.time()
else:
src.connected = False
src.error = "Stream disconnected"
return ret, frame
def get_status(self) -> dict:
"""返回所有流的状态快照"""
return {
sid: {
"connected": s.connected,
"last_frame_age": time.time() - s.last_frame_time if s.last_frame_time else -1,
"error": s.error
}
for sid, s in self._streams.items()
}
def _monitor_loop(self):
"""后台监控:定期探测断流并尝试重连"""
while self._running:
for sid, src in list(self._streams.items()):
if not src.connected:
# 尝试重连
with src.lock:
if src.cap:
src.cap.release()
new_cap = cv2.VideoCapture(src.url)
ok, _ = new_cap.read()
if ok:
src.cap = new_cap
src.connected = True
src.error = ""
else:
new_cap.release()
src.error = "Reconnect failed"
time.sleep(self._timeout)
def start_monitoring(self):
"""启动后台断流检测与重连线程"""
if self._monitor_thread and self._monitor_thread.is_alive():
return
self._running = True
self._monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
self._monitor_thread.start()
def stop_monitoring(self):
self._running = False
if self._monitor_thread:
self._monitor_thread.join(timeout=1.0)
def shutdown(self):
"""释放所有资源"""
self.stop_monitoring()
for sid in list(self._streams.keys()):
self.remove_stream(sid)
# 最小可运行示例
if __name__ == "__main__":
# 用本地测试视频文件模拟RTSP源(实际RTSP地址替换即可)
test_sources = {
"cam1": "sample1.mp4", # 替换为 rtsp://...
"cam2": "sample2.mp4",
}
mgr = StreamManager(timeout_sec=2.0)
for sid, url in test_sources.items():
ok = mgr.add_stream(sid, url)
print(f"Add {sid}: {'OK' if ok else 'FAIL'}")
mgr.start_monitoring()
# 读取几帧验证
for _ in range(5):
for sid in test_sources:
ret, frame = mgr.read_frame(sid)
if ret:
print(f"{sid}: frame {frame.shape}")
time.sleep(0.1)
# 动态移除一路
mgr.remove_stream("cam1")
print("After remove:", mgr.get_status())
mgr.shutdown()
print("Shutdown complete")
运行方式
# 准备两个测试视频文件(或用真实RTSP地址替换代码中的URL)
python stream_manager.py
验证方法
- 添加成功:控制台输出
Add cam1: OK,说明连接建立并可读取首帧。 - 帧读取:循环读取时打印
frame (height, width, 3),说明帧数据正常获取。 - 动态移除:
remove_stream("cam1")后,get_status()中不再包含cam1,且资源已释放。 - 断流检测:手动中断视频源(如关闭摄像头),约
timeout_sec秒后get_status()中该路connected变为False,并自动尝试重连。
关键说明
- 代码中
sample1.mp4为本地文件模拟RTSP;实际部署时替换为rtsp://user:pass@ip:port/stream格式地址即可,无需修改其他逻辑。 - 后台监控线程每
timeout_sec秒检查一次所有流,对断流自动重连,实现“统一管理连接状态”的目标。 - 线程安全通过每路流的
lock保证,避免读写竞争。