Flask如何在不同页面独立传输多路摄像头视频流
Flask多摄像头实时流切换阻塞问题解决方案
问题描述
开发支持多摄像头实时流查看的Flask应用,需求为允许用户在不同页面切换查看2路独立摄像头的实时视频流。
当前单路流实现存在核心故障:视频流启动后,程序永久阻塞在gen_frames()函数的while True循环中,用户无法返回首页选择其他摄像头流。此前尝试threading多线程方案但未掌握实现逻辑,未落地。
核心需求:实现用户返回Flask主页面时自动中断帧生成的死循环。
原有问题代码
import cv2 app = Flask(__name__) global camera def gen_frames(): while True: success, frame = camera.read() # read the camera frame if not success: break else: ret, buffer = cv2.imencode('.jpg', frame) frame = buffer.tobytes() yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result @app.route('/video_feed') def video_feed(): return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') @app.route('/Camera<int:x>') def camera(x): global camera camera = cv2.VideoCapture(x-1) return render_template('cameras.html') @app.route('/') def index(): return render_template('index.html')
配套文件说明
index.html:首页,包含跳转至各摄像头页面的超链接cameras.html:单摄像头展示页,包含视频流展示组件与返回首页的超链接
问题根因
- 使用全局变量存储摄像头实例,全局状态会被不同请求覆盖,且生命周期和请求解绑,无法在用户断开连接时及时释放
- 帧生成循环没有检测客户端连接状态,即使用户已经离开页面、断开了流请求,循环依然会在服务端持续运行
- 视频流路由没有和摄像头编号绑定,依赖全局变量传参,切换摄像头时旧流没有退出通道
修正实现
后端代码改动
- 移除全局摄像头变量,每个流请求独立初始化对应摄像头实例,避免状态冲突
- 在帧生成循环中增加客户端连接状态检测,用户跳走/断开时立刻退出循环
- 用
finally块保证摄像头资源无论循环因何种原因退出都会被释放 - 视频流路由直接接收摄像头编号参数,和页面路由绑定,不依赖全局变量传参
修正后完整后端代码:
import cv2 from flask import Flask, Response, render_template, request app = Flask(__name__) def gen_frames(camera_id): # 每个流请求独立初始化摄像头,不共享全局实例 cap = cv2.VideoCapture(camera_id) try: while True: # 检测客户端是否已断开连接,断开则直接退出循环 if request.environ.get('wsgi.input_terminated', False): break success, frame = cap.read() if not success: break ret, buffer = cv2.imencode('.jpg', frame) frame_bytes = buffer.tobytes() yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n') finally: # 确保资源一定会被释放 cap.release() @app.route('/video_feed/<int:camera_id>') def video_feed(camera_id): return Response( gen_frames(camera_id), mimetype='multipart/x-mixed-replace; boundary=frame' ) @app.route('/Camera<int:x>') def camera_page(x): # 将当前摄像头id传给模板,直接绑定对应流地址 return render_template('cameras.html', current_cam_id=x-1) @app.route('/') def index(): return render_template('index.html') if __name__ == '__main__': app.run(debug=True, threaded=True)
前端模板改动
修改cameras.html中的视频流标签地址,匹配带参数的流路由:
<!-- 摄像头视频展示组件 --> <img src="{{ url_for('video_feed', camera_id=current_cam_id) }}" width="720" height="480" alt="摄像头实时流">
效果说明
- 用户点击返回首页、切换其他摄像头页面时,浏览器会自动断开当前的视频流HTTP连接,服务端检测到断开状态后会立刻退出帧生成循环,释放摄像头资源,不会出现永久阻塞
- 每个流请求持有独立的摄像头实例,不会出现全局变量被覆盖导致的串流、资源抢占问题,两个摄像头可同时被访问
- 无需复杂的多线程逻辑,基础场景下性能完全满足需求;如果需要支持更高并发,可在这个基础上增加后台帧缓存线程,不用改动核心的连接检测、资源释放逻辑
内容的提问来源于stack exchange,提问作者Thiago Cerqueira
相关产品推荐
相关产品推荐

