Browse Source

完成了网页端视频显示的优化,使用了websocket

master
张龙 10 months ago
parent
commit
575cc3c8ca
  1. 2
      .idea/FristProject.iml
  2. 2
      .idea/misc.xml
  3. 6
      config.yaml
  4. 62
      core/ModelManager.py
  5. 3
      model/plugins/ModelBase.py
  6. 2
      run.py
  7. 295
      web/API/viedo.py
  8. 13
      web/__init__.py
  9. 107
      web/main/static/resources/scripts/aiortc-client-new.js
  10. 56
      web/main/templates/实时预览.html
  11. BIN
      zfbox.db

2
.idea/FristProject.iml

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Remote Python 3.9.2 (sftp://root@192.168.3.48:22/usr/local/miniconda3/bin/python)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="PyTorch" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/misc.xml

@ -3,7 +3,7 @@
<component name="Black">
<option name="sdkName" value="PyTorch" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Remote Python 3.9.2 (sftp://root@192.168.3.48:22/usr/local/miniconda3/bin/python)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="PyTorch" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>

6
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 #验证帧率--- 也就是视频输出的帧率

62
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()

3
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 # 成功状态值

2
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")

295
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/<stream_id>')
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/<int:channel_id>')
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():#这是全关

13
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模块

107
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];
}
}
connect();
}

56
web/main/templates/实时预览.html

@ -171,58 +171,86 @@
<p></p>
</div>
</div>
<script src="/resources/scripts/aiortc-client-new.js">
console.log('当指定了src属性时,浏览器会加载并执行该文件中的脚本,而忽略标签内部的任何内容,所以不会执行');
</script>
<script>
window.onload = function() {
// 页面加载完成后执行的代码
var intervalId = setInterval(showView, 1000);
setTimeout(function() {
clearInterval(intervalId); // 停止定时循环
console.log("Interval stopped after 10 seconds");
}, 10000);
//var intervalId = setInterval(showView, 1000);
//setTimeout(function() {
// clearInterval(intervalId); // 停止定时循环
// console.log("Interval stopped after 10 seconds");
// }, 10000);
}
</script>
<!-- Unnamed (Image) -->
<div id="u32" class="ax_default image">
<video id="u32_video" autoplay="true" playsinline="true"></video>
<img id="u32_video" src="" alt="Video Stream">
</div>
<script>
const videoStream = document.getElementById('u32_video');
const streamUrl = 'ws://localhost:5001/api/ws/video_feed/2';
function connect() {
const socket = new WebSocket(streamUrl);
socket.onmessage = function(event) {
// 释放旧的对象URL
if (videoStream.src) {
URL.revokeObjectURL(videoStream.src);
}
videoStream.src = URL.createObjectURL(event.data);
};
socket.onclose = function() {
setTimeout(connect, 1000); // Attempt to reconnect after 1 second
};
}
window.onload = function() {
//connect();
}
</script>
<!-- Unnamed (Image) -->
<div id="u33" class="ax_default image">
<video id="u33_video" autoplay="true" playsinline="true"></video>
<img id="u33_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u34" class="ax_default image">
<video id="u34_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u34_video" autoplay="true" playsinline="true"></video>-->
<img id="u34_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u35" class="ax_default image">
<video id="u35_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u35_video" autoplay="true" playsinline="true"></video>-->
<img id="u35_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u36" class="ax_default image">
<video id="u36_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u36_video" autoplay="true" playsinline="true"></video>-->
<img id="u36_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u37" class="ax_default image">
<video id="u37_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u37_video" autoplay="true" playsinline="true"></video>-->
<img id="u37_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u38" class="ax_default image">
<video id="u38_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u38_video" autoplay="true" playsinline="true"></video>-->
<img id="u38_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->
<div id="u39" class="ax_default image">
<video id="u39_video" autoplay="true" playsinline="true"></video>
<!-- <video id="u39_video" autoplay="true" playsinline="true"></video>-->
<img id="u39_video" src="" alt="Video Stream">
</div>
<!-- Unnamed (Image) -->

BIN
zfbox.db

Binary file not shown.
Loading…
Cancel
Save