Browse Source

12344

master
lichong 9 months ago
parent
commit
f47a3bdbf7
  1. 114
      api/main.py
  2. 2
      front/package.json
  3. 127
      front/src/views/student.vue
  4. 82
      front/src/views/teacher.vue
  5. 22
      test.py

114
api/main.py

@ -4,7 +4,7 @@ from bson import ObjectId
import hashlib import hashlib
import json import json
from flask import Flask, render_template, request from flask import Flask, render_template, request
from flask_socketio import SocketIO, Namespace, emit from flask_socketio import SocketIO, emit, join_room, leave_room
import time import time
app = Flask(__name__) app = Flask(__name__)
@ -12,95 +12,46 @@ client = MongoClient("mongodb://localhost:27019/")
db = client["back"] db = client["back"]
collection = db["users"] collection = db["users"]
# sockitIo解决跨域问题 # sockitIo解决跨域问题
socketMsg = SocketIO(app, cors_allowed_origins="*") app.config["SECRET_KEY"] = "secret!"
socketVedio = SocketIO(app, cors_allowed_origins="*") socketio = SocketIO(app, cors_allowed_origins="*")
# 用来存放客户端的 sid,即 session id # 存储连接的客户端的房间号
# 可以不单独定义字典存放 sid与namespace,flask-socketio 默认将 sid 存放在 room 中 clients = {}
socket_pool = {}
# Websocket 通过namespace 和 sid 标识具体客户端 @socketio.on("connect", namespace="/ws/video")
# 第一个 Websocket 类用于聊天 def on_connect():
class MyCustomNamespace(Namespace): print("Client connected")
name_space = "/msg"
# 连接成功调用的方法
def on_connect(self):
global socket_pool
socket_pool[request.sid] = self.name_space
print(1, socket_pool)
print(2, "Client connected")
print(3, "-----------------")
# 断开连接调用的方法 @socketio.on("disconnect", namespace="/ws/video")
def on_disconnect(self): def on_disconnect():
global socket_pool print("Client disconnected")
print(4, "disconnect...") # 移除客户端的房间关联
del socket_pool[request.sid] room = clients.pop(request.sid, None)
print(5, socket_pool) if room:
leave_room(room)
# 往 接收客户端标消息识为 'message' 的方法
def on_message(self, data):
print(6, data)
# 把消息发送到客户端的 'response' 标识的方法中, 一般是 on_response()
emit("response", "123", to=request.sid)
print(7, "--------------")
print(8, request.sid)
print(9, "--------------")
print(10, socketio.server.manager.rooms)
# 往 接收客户端标消息识为 'hello' 的方法 @socketio.on("join", namespace="/ws/video")
def on_hello(self, data): def on_join(data):
print(11, "hello world") room = data["room"]
join_room(room)
clients[request.sid] = room
# 发送消息
def send(self, data):
# 向 namespace中的所有 Websocket 连接广播消息, namespace参数不能少, to缺省是广播模式
emit("response", data, namespace=self.name_space)
# 向 sid 所标识的客户端 单播
emit("response", data, namespace=self.name_space, to=request.sid)
@socketio.on("leave", namespace="/ws/video")
def on_leave(data):
room = data["room"]
leave_room(room)
clients.pop(request.sid, None)
# 为了详细展示不同类,这里就不做继承
# 第二个 Websocket 类用于视频
class MyCustomNamespace1(Namespace):
name_space = "/vedio"
def on_connect(self): @socketio.on("message", namespace="/ws/video")
global socket_pool def handle_video_frame(message):
# 广播视频帧给房间内的所有客户端,除了发送该帧的客户端
print(12, "connect..") room = clients.get(request.sid)
print(13, request.namespace) if room:
socket_pool[request.sid] = self.name_space emit("message", message, room=room, include_self=False)
print(14, socket_pool)
def on_disconnect(self):
global socket_pool
print(15, "disconnect...")
del socket_pool[request.sid]
print(16, socket_pool)
def on_message(self, data):
print(17, data)
emit("response", "123")
self.send("asd")
# 发送消息
def send(self, data):
emit("response", data, namespace=self.name_space)
socketMsg.on_namespace(MyCustomNamespace("/msg"))
socketVedio.on_namespace(MyCustomNamespace("/vedio"))
@app.route("/sendMsg")
def sendMsg():
for sid, namespace_value in socket_pool.items():
print(18, sid)
emit("response", "123456", namespace=namespace_value, to=sid)
print(19, "ok")
return "ok"
# md5加密 # md5加密
@ -332,5 +283,4 @@ def updatePassword():
if __name__ == "__main__": if __name__ == "__main__":
initData() initData()
# app.run(debug=True) # app.run(debug=True)
socketMsg.run(app, debug=True) socketio.run(app, debug=True)
# socketVedio.run(app, debug=True)

2
front/package.json

@ -28,4 +28,4 @@
"@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue": "^4.6.2",
"vite": "^5.0.8" "vite": "^5.0.8"
} }
} }

127
front/src/views/student.vue

@ -34,21 +34,25 @@
<span>实时作弊信息</span> <span>实时作弊信息</span>
</div> </div>
</template> </template>
<p>实时作弊信息</p> <p>
<span>
实时作弊信息
</span>
<el-button type="primary" @click="startExam">开始考试</el-button>
</p>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
视频 <video ref="videoEL" class="canvasClass" playsinline></video>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
<script> <script>
import _ from "lodash"; import _ from "lodash";
import { io } from "socket.io-client";
import { import {
getUser, //user getUser, //user
} from "@/api/student"; } from "@/api/student";
import { ElMessage, ElMessageBox } from 'element-plus'
export default { export default {
name: "student", name: "student",
components: {}, components: {},
@ -115,8 +119,8 @@ export default {
}, },
], ],
userData: {}, userData: {},
socketMsg: io("http://localhost:3000/msg"), ws: null,
socketVedio: io("http://localhost:3000/vedio") videoStream: null,
}; };
}, },
watch: {}, watch: {},
@ -124,7 +128,7 @@ export default {
async mounted() { async mounted() {
this.userId = _.get(this.$route, ["params", "id"], "") this.userId = _.get(this.$route, ["params", "id"], "")
await this.getUser() await this.getUser()
this.initMsg() await this.initWebSocket();
}, },
methods: { methods: {
async getUser() { async getUser() {
@ -133,39 +137,86 @@ export default {
}) })
this.userData = res.list[0] this.userData = res.list[0]
}, },
initMsg() { async initWebSocket() {
this.socketMsg.on("connect", (data) => { this.ws = new WebSocket('ws://127.0.0.1:5000/ws/video');
console.log(data); this.ws.binaryType = 'arraybuffer';
}); this.ws.onopen = () => {
this.socketMsg.on("data", (data) => { console.log(1, 'WebSocket connected');
console.log(data); };
}); this.ws.onerror = (err) => {
this.socketMsg.on("disconnect", (data) => { console.error(2, 'WebSocket error:', err);
console.log(data); };
}); this.ws.onclose = () => {
this.socketMsg.on('response', (data) => { console.log(3, 'WebSocket disconnected');
this.messages.push({ id: Date.now(), text: data.data }); };
}); },
async connectTeacher() {
console.log(78744, this.socketMsg)
this.socketMsg.emit('sendMsg', { sid: this.socketMsg.id, id: this.userId, msg: "我有问题" });
},
async startExam() {
try {
let device = {}
let devices = await navigator.mediaDevices.enumerateDevices()
for (let key in devices) {
if (devices[key].kind === 'videoinput') {
device = devices[key]
break
}
}
let stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
sourceId: device.deviceId, // ID
width: 600,
height: 600,
}
})
//
this.$refs['videoEL'].srcObject = stream
this.$refs['videoEL'].play()
this.sendVideoFrames();
} catch (error) {
ElMessage.error(`摄像头开启失败,请检查摄像头是否可用!${error}`)
}
}, },
initVedio() { sendVideoFrames() {
this.socketVedio.on("connect", (data) => { let that = this
console.log(data); let video = this.$refs['videoEL'];
}); let canvas = document.createElement('canvas');
this.socketVedio.on("data", (data) => { let ctx = canvas.getContext('2d');
console.log(data); canvas.width = video.videoWidth;
}); canvas.height = video.videoHeight;
this.socketVedio.on("disconnect", (data) => { let sendFrame = () => {
console.log(data); ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
}); canvas.toBlob((blob) => {
this.socketVedio.on('response', (data) => { let arrayBufferView = new Uint8Array(blob);
this.messages.push({ id: Date.now(), text: data.data }); that.ws.send(arrayBufferView.buffer);
}); }, 'image/jpeg');
};
setInterval(sendFrame, 1000 / 30); // 30 FPS
}, },
connectTeacher() { stopVideoStream() {
this.socketMsg.emit('message', "this.message"); if (this.videoStream) {
} this.videoStream.getTracks().forEach(track => track.stop());
}
},
closeWebSocket() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.close();
}
},
},
beforeUnmount() {
this.stopVideoStream();
this.closeWebSocket();
}, },
beforeUnmount() { },
}; };
</script> </script>
<style scoped></style> <style scoped>
.canvasClass {
position: relative;
width: 600px;
height: 600px;
}
</style>

82
front/src/views/teacher.vue

@ -1,6 +1,6 @@
<template> <template>
<el-row> <el-row>
<el-col :span="16"> <el-col :span="14">
<div style="margin-bottom: 4px;"> <div style="margin-bottom: 4px;">
<el-button icon="Plus" type="primary" @click="addUserData"> <el-button icon="Plus" type="primary" @click="addUserData">
add User add User
@ -18,8 +18,21 @@
</vueTable> </vueTable>
</div> </div>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="10">
监控画面 <el-row>
<el-col :span="12">
<video ref="videoEL1" class="canvasClass"></video>
</el-col>
<el-col :span="12">
<video ref="videoEL2" class="canvasClass"></video>
</el-col>
<el-col :span="12">
<video ref="videoEL3" class="canvasClass"></video>
</el-col>
<el-col :span="12">
<video ref="videoEL4" class="canvasClass"></video>
</el-col>
</el-row>
</el-col> </el-col>
</el-row> </el-row>
<el-dialog v-model="addDialog.show" :title="addDialog.title" width="80%" draggable <el-dialog v-model="addDialog.show" :title="addDialog.title" width="80%" draggable
@ -37,7 +50,6 @@
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import md5 from "md5";
import vueTable from "../component/table.vue"; import vueTable from "../component/table.vue";
import changeItem from "../component/changeItem.vue"; import changeItem from "../component/changeItem.vue";
import _ from "lodash"; import _ from "lodash";
@ -48,6 +60,8 @@ import {
getUser, //user getUser, //user
updatePassword,// updatePassword,//
} from "@/api/teacher"; } from "@/api/teacher";
import io from "socket.io-client";
import { ElMessage, ElMessageBox } from 'element-plus'
export default { export default {
name: "teacher", name: "teacher",
components: { vueTable, changeItem }, components: { vueTable, changeItem },
@ -188,6 +202,9 @@ export default {
formHeader: [], formHeader: [],
formHeaderLocal: [], formHeaderLocal: [],
formDisabled: false, formDisabled: false,
ws: null,
videoStream: null,
room: 'defaultRoom', //
}; };
}, },
watch: {}, watch: {},
@ -196,8 +213,9 @@ export default {
this.dealTableHeader() this.dealTableHeader()
this.dealFormHeader() this.dealFormHeader()
await this.init(); await this.init();
await this.initWebSocket()
}, },
beforeUnmount() { },
methods: { methods: {
dealTableHeader() { dealTableHeader() {
this.tableHeader = _.filter(this.localComponent, o => { this.tableHeader = _.filter(this.localComponent, o => {
@ -332,7 +350,59 @@ export default {
}); });
}) })
}, },
//
async initWebSocket() {
this.ws = new WebSocket(`ws://127.0.0.1:5000/ws/video`);
console.log(787844, this.ws)
this.ws.binaryType = 'arraybuffer';
this.ws.onopen = () => {
console.log(4, 'WebSocket connected');
this.ws.send(JSON.stringify({ type: 'join', room: this.room }));
};
this.ws.onerror = (err) => {
console.error(5, 'WebSocket error:', err);
};
this.ws.onclose = () => {
console.log(6, 'WebSocket disconnected');
};
this.ws.onmessage = (event) => {
//
const arrayBuffer = event.data;
const blob = new Blob([arrayBuffer], { type: 'image/jpeg' });
const urlCreator = window.URL || window.webkitURL;
const imageUrl = urlCreator.createObjectURL(blob);
const img = new Image();
img.onload = function () {
urlCreator.revokeObjectURL(this.src);
};
img.src = imageUrl;
this.$refs.videoEL1.srcObject = null; //
this.$refs.videoEL1.src = imageUrl; //
this.$refs.videoEL1.play();
};
},
stopVideoStream() {
if (this.videoStream) {
this.videoStream.getTracks().forEach(track => track.stop());
}
},
closeWebSocket() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.close();
}
},
},
beforeUnmount() {
this.stopVideoStream();
this.closeWebSocket();
}, },
}; };
</script> </script>
<style scoped></style> <style scoped>
.canvasClass {
position: relative;
width: 390px;
height: 390px;
background-color: blueviolet
}
</style>

22
test.py

@ -1,22 +0,0 @@
from flask import Flask, render_template
from flask_socketio import SocketIO, emit, Namespace
from threading import Thread, Event
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, cors_allowed_origins="*")
class MyCustomNamespace(Namespace):
def on_connect(self):
print('Client connected')
def on_disconnect(self):
print('Client disconnected')
def on_message(self, message):
emit('response', {'data': 'Got it!'}, broadcast=True)
socketio.on_namespace(MyCustomNamespace('/test'))
if __name__ == '__main__':
socketio.run(app, debug=True)
Loading…
Cancel
Save