如何通过IP地址与VideoStream访问客户端摄像头?报错解决
问题分析与解决方案
核心问题根源
你遇到的AttributeError: 'NoneType' object has no attribute 'shape'本质是vs.read()返回了None,说明服务器无法通过rtsp://客户端IP获取到视频流。原因有两个:
- 客户端摄像头没有对外提供RTSP流媒体服务,直接用IP拼接RTSP地址是无效的——RTSP需要客户端侧有对应的服务主动推送摄像头流。
- 代码本身存在基础逻辑漏洞,会导致运行异常。
先修复代码基础问题
1. 补全缺失依赖与初始化
你的代码漏了导入socket模块,且未初始化客户端IP列表,在GUI2的__init__方法中补充:
def __init__(self): self.PORT = 5000 import socket # 补全socket导入 self.SERVER = socket.gethostbyname(socket.gethostname()) self.ADDRESS = (self.SERVER, self.PORT) self.FORMAT = "utf-8" self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.bind(self.ADDRESS) self.clients = [] # 初始化客户端IP列表 # ... 其余代码不变
2. 修复视频流循环逻辑
原startstream方法会卡在第一个客户端的死循环里,无法处理后续客户端;同时缺少cv2.waitKey()会导致窗口无响应,还要处理frame为None的异常:
def startstream(self): try: # 为每个客户端创建独立线程处理,避免阻塞 def stream_client(ip): # 这里后续要替换为正确的视频流地址 vs = VideoStream(src=f"rtsp://{ip}:8554/stream").start() while True: frame = vs.read() if frame is None: print(f"无法获取{ip}的视频流,断开连接") break frame = imutils.resize(frame, width=1080) cv2.imshow(f"摄像头 - {ip}", frame) # 按q键退出当前摄像头窗口 if cv2.waitKey(1) & 0xFF == ord('q'): break vs.stop() cv2.destroyWindow(f"摄像头 - {ip}") for x in self.clients: Thread(target=stream_client, args=(x,)).start() except Exception as e: print(traceback.format_exc())
访问客户端摄像头的可行方案
方案一:客户端开启RTSP流媒体服务
这是最直接的方式,需要在客户端用工具把摄像头推成RTSP流:
- 安装ffmpeg(官网下载或通过包管理器安装)
- 在客户端执行命令(先通过
ffmpeg -list_devices true -f dshow -i dummy查看本地摄像头名称,替换命令中的Integrated Camera):
ffmpeg -f dshow -i video="Integrated Camera" -vcodec libx264 -preset ultrafast -tune zerolatency -rtsp_transport tcp -muxdelay 0.1 rtsp://客户端IP:8554/stream
- 服务器端使用
rtsp://客户端IP:8554/stream作为VideoStream的src即可获取流。
方案二:Socket直接传输视频帧(适合轻量场景)
如果不想部署RTSP服务,可以让客户端主动把摄像头帧通过Socket发送给服务器:
客户端代码示例
import socket import cv2 import numpy as np client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(("服务器IP", 5001)) # 用单独端口传输视频 cap = cv2.VideoCapture(0) encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90] while True: ret, frame = cap.read() if not ret: break result, imgencode = cv2.imencode('.jpg', frame, encode_param) data = np.array(imgencode) string_data = data.tobytes() # 先发送数据长度,再发送帧数据 client_socket.send(str(len(string_data)).ljust(16).encode()) client_socket.send(string_data) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() client_socket.close()
服务器端接收逻辑(替换原startstream方法)
def startstream(self): def receive_frame(conn, addr): while True: try: # 接收数据长度 length = conn.recv(16) if not length: break length = int(length.strip()) # 接收完整帧数据 data = b'' while len(data) < length: data += conn.recv(length - len(data)) # 解码并显示 data = np.frombuffer(data, np.uint8) frame = cv2.imdecode(data, cv2.IMREAD_COLOR) frame = imutils.resize(frame, width=1080) cv2.imshow(f"摄像头 - {addr[0]}", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break except: break conn.close() cv2.destroyWindow(f"摄像头 - {addr[0]}") # 启动视频接收服务 video_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) video_server.bind((self.SERVER, 5001)) video_server.listen(30) while True: conn, addr = video_server.accept() Thread(target=receive_frame, args=(conn, addr)).start()
内容的提问来源于stack exchange,提问作者N. Tanaka
相关产品推荐
相关产品推荐

