diff --git a/.idea/FristProject.iml b/.idea/FristProject.iml
index 719bacc..8678458 100644
--- a/.idea/FristProject.iml
+++ b/.idea/FristProject.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 55756c3..25bfb8c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/config.yaml b/config.yaml
index 6b06a1a..b9bc873 100644
--- a/config.yaml
+++ b/config.yaml
@@ -28,11 +28,11 @@ ALLOWED_EXTENSIONS : {'zip'}
RTSP_Check_Time : 600 #10分钟
#model
-model_platform : acl #acl gpu cpu
+model_platform : cpu #acl gpu cpu
weight_path: /model/weights
yolov5_path: D:/Project/FristProject/model/base_model/yolov5 #使用绝对路径,不同的部署环境需要修改!
cap_sleep_time: 300 #5分钟
-buffer_len: 300 #分析后画面缓冲区帧数 -- 可以与验证帧率结合确定缓冲区大小
+buffer_len: 100 #分析后画面缓冲区帧数 -- 可以与验证帧率结合确定缓冲区大小
RESET_INTERVAL : 100000 #帧数重置上限
frame_rate : 20 #帧率参考值 -- 后续作用主要基于verify_rate进行帧率控制
-verify_rate : 5 #验证帧率--- 也就是视频输出的帧率
+verify_rate : 10 #验证帧率--- 也就是视频输出的帧率
diff --git a/core/ModelManager.py b/core/ModelManager.py
index c9c9b0b..08fef10 100644
--- a/core/ModelManager.py
+++ b/core/ModelManager.py
@@ -175,32 +175,32 @@ class ModelManager:
# 使用 模型 进行目标检测
i_warn_count = 0 #报警标签
isverify = False
- # for i in range(len(myModle_list)): # 遍历通道关联的算法进行检测,若不控制模型数量,有可能需要考虑多线程执行。
- # model = myModle_list[i]
- # data = myModle_data[i]
- # schedule = schedule_list[i]
- # result = result_list[i]
- # #验证检测计划,是否在布防时间内
- # now = datetime.datetime.now() # 获取当前日期和时间
- # weekday = now.weekday() # 获取星期几,星期一是0,星期天是6
- # hour = now.hour
- # result.pop(0) # 保障结果数组定长 --先把最早的结果推出数组
- # if schedule[weekday][hour] == 1: #不在计划则不进行验证,直接返回图片
- # # 调用模型,进行检测,model是动态加载的,具体的判断标准由模型内执行 ---- *********
- # isverify = True
- # detections, bwarn, warntext = model.verify(img, data,isdraw) #****************重要
- # # 对识别结果要部要进行处理
- # if bwarn: # 整个识别有产生报警
- # #根据模型设定的时间和占比判断是否
- # # 绘制报警文本
- # cv2.putText(img, 'Intruder detected!', (50, (i_warn_count + 1) * 50),
- # cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
- # i_warn_count += 1
- # result.append(1) #要验证数组修改,是地址修改吗?
- # else: #没有产生报警也需要记录,统一计算占比
- # result.append(0)
- # else:
- # result.append(0)
+ for i in range(len(myModle_list)): # 遍历通道关联的算法进行检测,若不控制模型数量,有可能需要考虑多线程执行。
+ model = myModle_list[i]
+ data = myModle_data[i]
+ schedule = schedule_list[i]
+ result = result_list[i]
+ #验证检测计划,是否在布防时间内
+ now = datetime.datetime.now() # 获取当前日期和时间
+ weekday = now.weekday() # 获取星期几,星期一是0,星期天是6
+ hour = now.hour
+ result.pop(0) # 保障结果数组定长 --先把最早的结果推出数组
+ if schedule[weekday][hour] == 1: #不在计划则不进行验证,直接返回图片
+ # 调用模型,进行检测,model是动态加载的,具体的判断标准由模型内执行 ---- *********
+ isverify = True
+ detections, bwarn, warntext = model.verify(img, data,isdraw) #****************重要
+ # 对识别结果要部要进行处理
+ if bwarn: # 整个识别有产生报警
+ #根据模型设定的时间和占比判断是否
+ # 绘制报警文本
+ cv2.putText(img, 'Intruder detected!', (50, (i_warn_count + 1) * 50),
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
+ i_warn_count += 1
+ result.append(1) #要验证数组修改,是地址修改吗?
+ else: #没有产生报警也需要记录,统一计算占比
+ result.append(0)
+ else:
+ result.append(0)
if not isverify: #没做处理,直接返回的,需要控制下帧率,太快读取没有意义。
time.sleep(1.0/self.frame_rate) #给个默认帧率,不超过30帧,---若经过模型计算,CPU下单模型也就12帧这样
@@ -247,7 +247,8 @@ class ModelManager:
#获取视频通道的模型相关数据-list
for model in myModels:
#基于基类实例化模块类
- m = self._import_model("",model[5],model[8]) #动态加载模型处理文件py --需要验证模型文件是否能加载
+ #m = self._import_model("",model[5],model[8]) #动态加载模型处理文件py --需要验证模型文件是否能加载
+ m = None
if m:
myModle_list.append(m) #没有成功加载的模型原画输出
myModle_data.append(model)
@@ -326,9 +327,10 @@ class ModelManager:
# end_time = time.time() # 结束时间
# print(f"Processing time: {end_time - start_time} seconds")
# 本地显示---测试使用
- # cv2.imshow('Frame', img)
- # if cv2.waitKey(1) & 0xFF == ord('q'):
- # break
+ if channel_id == 2:
+ cv2.imshow(str(channel_id), img)
+ if cv2.waitKey(1) & 0xFF == ord('q'):
+ break
#结束线程
cap.release()
#cv2.destroyAllWindows()
diff --git a/model/plugins/ModelBase.py b/model/plugins/ModelBase.py
index 4c7e5d8..6bded86 100644
--- a/model/plugins/ModelBase.py
+++ b/model/plugins/ModelBase.py
@@ -5,7 +5,8 @@ import numpy as np
import cv2
import ast
import platform
-import acl
+if myCongif.get_data("model_platform") == "acl":
+ import acl
#-----acl相关------
SUCCESS = 0 # 成功状态值
diff --git a/run.py b/run.py
index 4c49222..b4e9534 100644
--- a/run.py
+++ b/run.py
@@ -25,6 +25,6 @@ if __name__ == '__main__':
raise NotImplementedError(f"Unsupported operating system: {system}")
print(free/(1024*1024))
mMM.start_work() # 启动所有通道的处理
- mVManager.start_check_rtsp() #线程更新视频在线情况
+ #mVManager.start_check_rtsp() #线程更新视频在线情况
web.run(debug=True,port=5001,host="0.0.0.0")
diff --git a/web/API/viedo.py b/web/API/viedo.py
index 2a293d3..aab604a 100644
--- a/web/API/viedo.py
+++ b/web/API/viedo.py
@@ -2,7 +2,7 @@ import cv2
import asyncio
import time
from . import api
-from quart import jsonify, request
+from quart import jsonify, request,websocket
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack,RTCConfiguration
from core.ModelManager import mMM
from core.DBManager import mDBM
@@ -10,6 +10,7 @@ from myutils.ConfigManager import myCongif
from fractions import Fraction
import threading
import logging
+from quart.helpers import Response
# 配置日志
logging.basicConfig(level=logging.INFO)
@@ -53,48 +54,48 @@ pcs = {}
'''
---------------------传输--------------------------
'''
-class VideoTransformTrack(VideoStreamTrack):
- kind = "video"
- def __init__(self,cid,itype=1): #0-usb,1-RTSP,2-海康SDK --itype已没有作用
- super().__init__()
- self.channel_id = cid
-
- self.frame_rate = myCongif.get_data("frame_rate")
- self.time_base = Fraction(1, self.frame_rate)
- self.frame_count = 0
- self.start_time = time.time()
-
- async def recv(self):
- new_frame = None
- time.sleep(1.0 / self.frame_rate)
- new_frame = mMM.verify_list[self.channel_id][4]
- # while True:
- # new_frame = mMM.verify_list[self.channel_id][4]
- # #new_frame = av.VideoFrame.from_ndarray(img, format="rgb24")
- # if new_frame is not None:
- # break
- # else:
- # time.sleep(1.0 / self.frame_rate)
- # 设置时间戳和时间基数 -- 根据耗时实时计算帧率
- # elapsed_time = time.time() - self.start_time
- # self.frame_count += 1
- # actual_frame_rate = self.frame_count / elapsed_time
- # self.time_base = Fraction(1, int(actual_frame_rate))
- #设置时间戳和帧率
- self.frame_count += 1
- if self.frame_count > myCongif.get_data("RESET_INTERVAL"):
- self.frame_count = 0
- new_frame.pts = self.frame_count
- new_frame.time_base = self.time_base
- print(f"{self.channel_id} -- Frame pts: {new_frame.pts}, time_base: {new_frame.time_base}")
-
- # 将检测结果图像转换为帧
- # new_frame.pts = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES))
- # new_frame.time_base = self.time_base
- # end_time = time.time() # 结束时间
- # print(f"Processing time: {end_time - start_time} seconds")
- #print("webRTC recv at:",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
- return new_frame
+# class VideoTransformTrack(VideoStreamTrack):
+# kind = "video"
+# def __init__(self,cid,itype=1): #0-usb,1-RTSP,2-海康SDK --itype已没有作用
+# super().__init__()
+# self.channel_id = cid
+#
+# self.frame_rate = myCongif.get_data("frame_rate")
+# self.time_base = Fraction(1, self.frame_rate)
+# self.frame_count = 0
+# self.start_time = time.time()
+#
+# async def recv(self):
+# new_frame = None
+# time.sleep(1.0 / self.frame_rate)
+# new_frame = mMM.verify_list[self.channel_id][4]
+# # while True:
+# # new_frame = mMM.verify_list[self.channel_id][4]
+# # #new_frame = av.VideoFrame.from_ndarray(img, format="rgb24")
+# # if new_frame is not None:
+# # break
+# # else:
+# # time.sleep(1.0 / self.frame_rate)
+# # 设置时间戳和时间基数 -- 根据耗时实时计算帧率
+# # elapsed_time = time.time() - self.start_time
+# # self.frame_count += 1
+# # actual_frame_rate = self.frame_count / elapsed_time
+# # self.time_base = Fraction(1, int(actual_frame_rate))
+# #设置时间戳和帧率
+# self.frame_count += 1
+# if self.frame_count > myCongif.get_data("RESET_INTERVAL"):
+# self.frame_count = 0
+# new_frame.pts = self.frame_count
+# new_frame.time_base = self.time_base
+# #print(f"{self.channel_id} -- Frame pts: {new_frame.pts}, time_base: {new_frame.time_base}")
+#
+# # 将检测结果图像转换为帧
+# # new_frame.pts = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES))
+# # new_frame.time_base = self.time_base
+# # end_time = time.time() # 结束时间
+# # print(f"Processing time: {end_time - start_time} seconds")
+# #print("webRTC recv at:",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
+# return new_frame
async def get_stats(peer_connection):
stats = await peer_connection.getStats()
@@ -104,89 +105,137 @@ async def get_stats(peer_connection):
print(f"Packets Sent: {report.packetsSent}")
print(f"Bytes Sent: {report.bytesSent}")
-@api.route('/offer', methods=['POST'])
-async def offer():
- #接收客户端的连接请求
- params = await request.json
- channel_id = params["cid"] #需要传参过来
- itype = params["mytype"]
- element_id = params["element_id"]
- reStatus = 0
- reMsg = ""
- ret = False
- if itype == 1:
- #添加通道和页面控件ID的关系
- strsql = f"select * from channel where element_id = {element_id}"
- data = mDBM.do_select(strsql,1)
- if data:
- reStatus = 0
- reMsg = '该控件已经关联某一视频通道,请确认数据是否正确!'
- return jsonify({'status': reStatus, 'msg': reMsg})
- strsql = f"update channel set element_id = {element_id} where ID = {channel_id};"
- ret = mDBM.do_sql(strsql)
- if ret == True:
- reStatus = 1
- reMsg = '播放画面成功,请稍等!'
- else:
- reStatus = 0
- reMsg = '播放画面失败,请联系技术支持!'
- #提取客户端发来的SDP,生成服务器端的SDP
- offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
- # # 配置 STUN 服务器
- #ice_servers = [{"urls": []}]# 禁用 STUN 服务器
- # pc = RTCPeerConnection(configuration={"iceServers": ice_servers})
- config = RTCConfiguration(iceServers=[])
- pc = RTCPeerConnection(configuration=config)
- # t = threading.Thread(target=get_stats,args=(pc,))
- # t.start()
-
- #pc = RTCPeerConnection() # 实例化一个rtcp对象
- pcs[channel_id] = pc # 集合中添加一个对象,若已存在则不添
-
- @pc.on("datachannel")
- def on_datachannel(channel):
- @channel.on("message")
- def on_message(message):
- print(f'Message received: {message}')
- # 处理接收到的数据
- if message == 'pause':
- # 执行暂停操作
- pass
- elif message == 'resume':
- # 执行继续操作
- pass
-
- #监听RTC连接状态
- @pc.on("iconnectionstatechange") #当ice连接状态发生变化时
- async def iconnectionstatechange():
- if pc.iceConnectionState == "failed":
- await pc.close()
- pcs.pop(channel_id, None) #移除对象
-
- # 添加视频轨道
- video_track = VideoTransformTrack(channel_id)
- pc.addTrack(video_track)
+# @api.route('/offer', methods=['POST'])
+# async def offer():
+# #接收客户端的连接请求
+# params = await request.json
+# channel_id = params["cid"] #需要传参过来
+# itype = params["mytype"]
+# element_id = params["element_id"]
+# reStatus = 0
+# reMsg = ""
+# ret = False
+# if itype == 1:
+# #添加通道和页面控件ID的关系
+# strsql = f"select * from channel where element_id = {element_id}"
+# data = mDBM.do_select(strsql,1)
+# if data:
+# reStatus = 0
+# reMsg = '该控件已经关联某一视频通道,请确认数据是否正确!'
+# return jsonify({'status': reStatus, 'msg': reMsg})
+# strsql = f"update channel set element_id = {element_id} where ID = {channel_id};"
+# ret = mDBM.do_sql(strsql)
+# if ret == True:
+# reStatus = 1
+# reMsg = '播放画面成功,请稍等!'
+# else:
+# reStatus = 0
+# reMsg = '播放画面失败,请联系技术支持!'
+# #提取客户端发来的SDP,生成服务器端的SDP
+# offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
+# # # 配置 STUN 服务器
+# #ice_servers = [{"urls": []}]# 禁用 STUN 服务器
+# # pc = RTCPeerConnection(configuration={"iceServers": ice_servers})
+# config = RTCConfiguration(iceServers=[])
+# pc = RTCPeerConnection(configuration=config)
+# # t = threading.Thread(target=get_stats,args=(pc,))
+# # t.start()
+#
+# #pc = RTCPeerConnection() # 实例化一个rtcp对象
+# pcs[channel_id] = pc # 集合中添加一个对象,若已存在则不添
+#
+# @pc.on("datachannel")
+# def on_datachannel(channel):
+# @channel.on("message")
+# def on_message(message):
+# print(f'Message received: {message}')
+# # 处理接收到的数据
+# if message == 'pause':
+# # 执行暂停操作
+# pass
+# elif message == 'resume':
+# # 执行继续操作
+# pass
+#
+# #监听RTC连接状态
+# @pc.on("iconnectionstatechange") #当ice连接状态发生变化时
+# async def iconnectionstatechange():
+# if pc.iceConnectionState == "failed":
+# await pc.close()
+# pcs.pop(channel_id, None) #移除对象
+#
+# # 添加视频轨道
+# video_track = VideoTransformTrack(channel_id)
+# pc.addTrack(video_track)
+#
+# # @pc.on('track') --- stream.getTracks().forEach(track => pc.addTrack(track, stream)); 猜测是这里触发的添加了摄像头通道
+# # def on_track(track):
+# # if track.kind == 'video':
+# # local_video = VideoTransformTrack(track)
+# # pc.addTrack(local_video)
+#
+# # 记录客户端 SDP
+# await pc.setRemoteDescription(offer)
+# # 生成本地 SDP
+# answer = await pc.createAnswer()
+# # 记录本地 SDP
+# await pc.setLocalDescription(answer)
+#
+# print("返回sdp")
+# return jsonify({
+# "sdp": pc.localDescription.sdp,
+# "type": pc.localDescription.type,
+# "status":reStatus, #itype =1 的时候才有意义
+# "msg":reMsg
+# })
- # @pc.on('track') --- stream.getTracks().forEach(track => pc.addTrack(track, stream)); 猜测是这里触发的添加了摄像头通道
- # def on_track(track):
- # if track.kind == 'video':
- # local_video = VideoTransformTrack(track)
- # pc.addTrack(local_video)
+async def generate_frames(stream_id):
+ #video_capture = streams[stream_id]
+ start_time = time.time()
+ icount = 0
+ frame_rate_time = 1.0 / myCongif.get_data("frame_rate")
+ while True:
+ try:
+ await asyncio.sleep(frame_rate_time)
+ frame = mMM.verify_list[int(stream_id)][4]
+ if frame is not None:
+ img = frame.to_ndarray(format="bgr24")
+ ret, buffer = cv2.imencode('.jpg', img)
+ if not ret:
+ continue
+ frame = buffer.tobytes()
+ yield (b'--frame\r\n'
+ b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
+ end_time = time.time() # 结束时间
+ icount += 1
+ print(f"icount:{icount},Processing time: {end_time - start_time} seconds")
+ except asyncio.CancelledError:
+ print("Task was cancelled")
+ break # 处理任务取消
+ except Exception as e:
+ print(f"Error: {e}")
+ break # 在出现异常时退出循环
+ print("退出循环")
- # 记录客户端 SDP
- await pc.setRemoteDescription(offer)
- # 生成本地 SDP
- answer = await pc.createAnswer()
- # 记录本地 SDP
- await pc.setLocalDescription(answer)
+@api.route('/video_feed/')
+async def video_feed(stream_id):
+ if int(stream_id) != 2:
+ return "None"
+ print("执行一次:video_feed")
+ return Response(generate_frames(stream_id),mimetype='multipart/x-mixed-replace; boundary=frame')
- print("返回sdp")
- return jsonify({
- "sdp": pc.localDescription.sdp,
- "type": pc.localDescription.type,
- "status":reStatus, #itype =1 的时候才有意义
- "msg":reMsg
- })
+@api.websocket('/ws/video_feed/')
+async def ws_video_feed(channel_id):
+ while True:
+ frame = mMM.verify_list[int(channel_id)][4]
+ if frame is not None:
+ img = frame.to_ndarray(format="bgr24")
+ ret, buffer = cv2.imencode('.jpg', img)
+ if not ret:
+ continue
+ frame = buffer.tobytes()
+ await websocket.send(frame)
+ await asyncio.sleep(1.0 / myCongif.get_data("frame_rate")) # Adjust based on frame rate
@api.route('/shutdown', methods=['POST'])
async def shutdown():#这是全关
diff --git a/web/__init__.py b/web/__init__.py
index c05247f..72d1d41 100644
--- a/web/__init__.py
+++ b/web/__init__.py
@@ -4,6 +4,7 @@ from pymemcache.client import base
from .main import main
from .API import api
from functools import wraps
+from myutils.ConfigManager import myCongif
from quart_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
@@ -34,8 +35,10 @@ def create_app():
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
# app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'zfxxkj_2024_!@#'
-
- app.config['SESSION_TYPE'] = 'memcached' # session类型
+ if myCongif.get_data("model_platform") == "acl":
+ app.config['SESSION_TYPE'] = 'memcached' # session类型
+ elif myCongif.get_data("model_platform") =="cpu":
+ app.config['SESSION_TYPE'] = 'redis' # session类型
#app.config['SESSION_FILE_DIR'] = './sessions' # session保存路径
#app.config['SESSION_MEMCACHED'] = base.Client(('localhost', 11211))
app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
@@ -45,12 +48,6 @@ def create_app():
app.session_interface = MemcachedSessionInterface(memcached_client)
Session(app)
- #ORM数据库管理
- # db = SQLAlchemy()
- # migrate = Migrate()
- # db.init_app(app)
- # migrate.init_app(app, db) # 创建对象,使用它来将ORM模型映射到数据库 -- 效果是及时更新数据的修改到文档?
-
# 注册main
app.register_blueprint(main)
#注册API模块
diff --git a/web/main/static/resources/scripts/aiortc-client-new.js b/web/main/static/resources/scripts/aiortc-client-new.js
index 84ec533..6efecca 100644
--- a/web/main/static/resources/scripts/aiortc-client-new.js
+++ b/web/main/static/resources/scripts/aiortc-client-new.js
@@ -20,7 +20,7 @@ document.addEventListener('DOMContentLoaded', async function() {
// console.log(`Type: ${channel.type}`);
// console.log(`Status: ${channel.status}`);
// console.log(`Element ID: ${channel.element_id}`);
- start(channel.element_id,channel.ID,0)
+ connectToStream(channel.element_id,channel.ID,channel.area_name,channel.channel_name)
}
});
} catch (error) {
@@ -28,86 +28,37 @@ document.addEventListener('DOMContentLoaded', async function() {
}
});
-function negotiate(pc,channel_id,itype,element_id) {
- pc.addTransceiver('video', { direction: 'recvonly' });
- return pc.createOffer().then((offer) => {
- return pc.setLocalDescription(offer);
- }).then(() => {
- // wait for ICE gathering to complete
- return new Promise((resolve) => {
- if (pc.iceGatheringState === 'complete') {
- resolve();
- } else {
- const checkState = () => {
- if (pc.iceGatheringState === 'complete') {
- pc.removeEventListener('icegatheringstatechange', checkState);
- resolve();
+function connectToStream(element_id,channel_id,area_name,channel_name) {
+// const videoContainer = document.getElementById('video-container');
+// const imgElement = document.createElement('img');
+// imgElement.id = `${element_id}`;
+// videoContainer.appendChild(imgElement);
+ const imgElement = document.getElementById(element_id);
+ imgElement.alt = `Stream ${area_name}--${channel_name}`;
+ const streamUrl = `ws://${window.location.host}/api/ws/video_feed/${channel_id}`;
+ console.log(streamUrl);
+ function connect() {
+ const socket = new WebSocket(streamUrl);
+
+ socket.onmessage = function(event) {
+ //const blob = new Blob([event.data], { type: 'image/jpeg' });
+ //imgElement.src = URL.createObjectURL(blob);
+ // 释放旧的对象URL
+ if (imgElement.src) {
+ URL.revokeObjectURL(imgElement.src);
}
+ imgElement.src = URL.createObjectURL(event.data);
};
- pc.addEventListener('icegatheringstatechange', checkState);
- }
- });
- }).then(() => {
- var offer = pc.localDescription;
- return fetch('/api/offer', {
- body: JSON.stringify({
- sdp: offer.sdp,
- type: offer.type,
- cid: channel_id,
- mytype: itype,
- element_id:element_id
- }),
- headers: {
- 'Content-Type': 'application/json'
- },
- method: 'POST'
- });
- }).then((response) => {
- return response.json();
- }).then((answer) => {
- return pc.setRemoteDescription(answer);
- }).catch((e) => {
- alert(e);
- });
-}
-function start(element_id,channel_id,itype) {// iytpe =0,不用修改数据库,1,需要添加数据库记录
- console.log(`Element ID: ${element_id}`);
- console.log(`Channel ID: ${channel_id}`);
- pc = new RTCPeerConnection();
- pc_list[channel_id] = pc; //保留pc
- pc.addEventListener('track', (evt) => {
- if (evt.track.kind == 'video') {
- //document.getElementById('video').srcObject = evt.streams[0];
- document.getElementById(element_id).srcObject = evt.streams[0];
- }
- });
- negotiate(pc,channel_id,itype,element_id);
-}
+ socket.onclose = function() {
+ setTimeout(connect, 1000); // 尝试在1秒后重新连接
+ };
-function showView(){
- channel_list.forEach(channel => {
- if(channel.element_id){ //""空为false,非空为true
- console.log('status:',document.getElementById(channel.element_id).paused)
- if (document.getElementById(channel.element_id).paused) {
- document.getElementById(channel.element_id).play().catch(error => {
- console.error('Error playing video:', error);
- });
+ socket.onerror = function() {
+ console.log(`WebSocket错误,尝试重新连接... Channel ID: ${channel_id}`);
+ socket.close();
+ };
}
- }
- });
-}
-async function closestream(channel_id){
- let response = await fetch('/close_stream', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ channel_id: channel_id })
- });
- let data = await response.json();
- console.log(data);
- if(pc_list[channel_id]){
- pc_list[channel_id].close();
- delete pc_list[channel_id];
- }
-}
\ No newline at end of file
+ connect();
+ }
\ No newline at end of file
diff --git a/web/main/templates/实时预览.html b/web/main/templates/实时预览.html
index d8c6c4d..82af553 100644
--- a/web/main/templates/实时预览.html
+++ b/web/main/templates/实时预览.html
@@ -171,58 +171,86 @@
+
-
+
-
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
diff --git a/zfbox.db b/zfbox.db
index 851002e..b971ee5 100644
Binary files a/zfbox.db and b/zfbox.db differ