优化文件
This commit is contained in:
@@ -9,7 +9,6 @@ import ink.snowflake.server.route.ImageAnalytics
|
||||
import ink.snowflake.server.route.RemoteDebug
|
||||
import ink.snowflake.server.route.VideoAnalytics
|
||||
import ink.snowflake.server.route.VideoAnalyticsJetson
|
||||
import ink.snowflake.server.route.mainFunc
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.tomcat.jakarta.*
|
||||
@@ -54,8 +53,6 @@ fun Application.module() {
|
||||
// 设置-WebSocket
|
||||
configureSockets()
|
||||
|
||||
// 业务-首页导航
|
||||
mainFunc()
|
||||
// 业务-用户信息相关操作
|
||||
User(appConfig)
|
||||
// 业务-聊天
|
||||
|
||||
@@ -32,7 +32,6 @@ object ImageDao {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getImageList(name: String): List<ImageAnalyticsRequest> {
|
||||
return transaction {
|
||||
ImageTable.selectAll()
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ktor.server.auth.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import kotlin.text.isNullOrEmpty
|
||||
|
||||
fun Application.ImageAnalytics() {
|
||||
routing {
|
||||
@@ -18,16 +19,6 @@ fun Application.ImageAnalytics() {
|
||||
call.respond(BaseResponse(data = ImageDao.insertImageAnalyticsData(request)))
|
||||
}
|
||||
authenticate {
|
||||
// 拍照保存为图片 并且调用Python程序进行分析
|
||||
get("/takePhoto") {
|
||||
val camera = call.request.queryParameters["cameraId"]
|
||||
if (camera.isNullOrEmpty()) {
|
||||
call.respond(BaseResponse(status = false, message = "摄像头名称不能为空", data = null))
|
||||
return@get
|
||||
}
|
||||
stopHLSStream(camera)
|
||||
call.respond(BaseResponse(message = "摄像头流已停止", data = null))
|
||||
}
|
||||
// 获取已分析图片列表
|
||||
get("/getImageList") {
|
||||
val name = call.parameters["name"]
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package ink.snowflake.server.route
|
||||
|
||||
import com.google.gson.Gson
|
||||
import ink.snowflake.server.model.response.BaseResponse
|
||||
import ink.snowflake.server.model.response.DeviceItem
|
||||
import ink.snowflake.server.utils.runCommand
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import io.ktor.client.plugins.auth.*
|
||||
import io.ktor.client.plugins.auth.providers.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.http.content.*
|
||||
import java.io.File
|
||||
|
||||
fun Application.mainFunc() {
|
||||
routing {
|
||||
get("/") {
|
||||
// call.respondFile(File("src/main/resources/page/html/login.html"))
|
||||
// call.respondRedirect("html/login.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,244 +34,211 @@ import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
|
||||
val clients = Collections.synchronizedList<WebSocketServerSession>(ArrayList()) // 线程安全的客户端列表
|
||||
var aiState = "等待分析任务中"
|
||||
|
||||
fun Application.VideoAnalytics() {
|
||||
routing {
|
||||
// 实时发送AI状态
|
||||
webSocket("/handleState") { // WebSocket 路由
|
||||
clients.add(this) // 添加当前连接的客户端
|
||||
send(aiState) // 向客户端发送连接成功消息
|
||||
try {
|
||||
incoming.consumeEach { frame -> // 持续接收消息
|
||||
when (frame) {
|
||||
is Frame.Text -> {
|
||||
aiState = frame.readText() // 更新状态
|
||||
broadcastMessage(aiState) // 使用封装的方法广播消息
|
||||
}
|
||||
|
||||
is Frame.Close -> {
|
||||
println("Closed")
|
||||
clients.remove(this)
|
||||
close() // 确保关闭 WebSocket 连接
|
||||
return@consumeEach
|
||||
}
|
||||
// 其他消息类型的处理
|
||||
is Frame.Binary -> TODO() // 处理二进制消息
|
||||
is Frame.Ping -> TODO() // 处理 Ping 消息
|
||||
is Frame.Pong -> TODO() // 处理 Pong 消息
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 处理接收消息时的异常
|
||||
close(CloseReason(CloseReason.Codes.NORMAL, "Client disconnected"))
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
clients.remove(this) // 确保在连接关闭时移除客户端
|
||||
}
|
||||
}
|
||||
route("/api/iva") {
|
||||
// 上传分析结果
|
||||
post("/saveVideoAnalyticsData") {
|
||||
val request = call.receive<VideoAnalyticsRequest>()
|
||||
// todo 上传这里未做测试
|
||||
call.respond(BaseResponse(data = VideoDao.insertVideoAnalyticsData(request)))
|
||||
}
|
||||
authenticate {
|
||||
post("/createVideoTask") {
|
||||
val multipart = call.receiveMultipart() //1G
|
||||
// 确保 uploads 目录存在
|
||||
val uploadDir = File(VIDEO_INPUT_PATH)
|
||||
if (!uploadDir.exists()) {
|
||||
if (!uploadDir.mkdirs()) {
|
||||
println("无法创建目录")
|
||||
throw IOException("Failed to create upload directory.")
|
||||
}
|
||||
// 上传分析结果
|
||||
post("/saveVideoAnalyticsData") {
|
||||
val request = call.receive<VideoAnalyticsRequest>()
|
||||
// todo 上传这里未做测试
|
||||
call.respond(BaseResponse(data = VideoDao.insertVideoAnalyticsData(request)))
|
||||
}
|
||||
authenticate {
|
||||
post("/createVideoTask") {
|
||||
val multipart = call.receiveMultipart() //1G
|
||||
// 确保 uploads 目录存在
|
||||
val uploadDir = File(VIDEO_INPUT_PATH)
|
||||
if (!uploadDir.exists()) {
|
||||
if (!uploadDir.mkdirs()) {
|
||||
println("无法创建目录")
|
||||
throw IOException("Failed to create upload directory.")
|
||||
}
|
||||
var fileName = ""
|
||||
var name = ""
|
||||
var datetime = ""
|
||||
broadcastMessage("正在上传数据")
|
||||
}
|
||||
var fileName = ""
|
||||
var name = ""
|
||||
var datetime = ""
|
||||
broadcastMessage("正在上传数据")
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
multipart.forEachPart { part ->
|
||||
when (part) {
|
||||
is PartData.FileItem -> {
|
||||
fileName = part.originalFileName ?: "unknown"
|
||||
val file = File("$VIDEO_INPUT_PATH$fileName") // 保存路径
|
||||
//ktor3
|
||||
withContext(Dispatchers.IO) {
|
||||
multipart.forEachPart { part ->
|
||||
when (part) {
|
||||
is PartData.FileItem -> {
|
||||
fileName = part.originalFileName ?: "unknown"
|
||||
val file = File("$VIDEO_INPUT_PATH$fileName") // 保存路径
|
||||
//ktor3
|
||||
// file.outputStream().use { outputStream ->
|
||||
// val writableChannel = Channels.newChannel(outputStream)
|
||||
// part.provider().copyTo(writableChannel) // 复制到 WritableByteChannel
|
||||
// }
|
||||
//ktor2
|
||||
part.streamProvider().use { inputStream ->
|
||||
file.outputStream().buffered().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
//ktor2
|
||||
part.streamProvider().use { inputStream ->
|
||||
file.outputStream().buffered().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
is PartData.FormItem -> {
|
||||
when (part.name) {
|
||||
"projectName" -> name = part.value
|
||||
"projectDatetime" -> datetime = part.value
|
||||
}
|
||||
}
|
||||
|
||||
else -> part.dispose()
|
||||
}
|
||||
|
||||
is PartData.FormItem -> {
|
||||
when (part.name) {
|
||||
"projectName" -> name = part.value
|
||||
"projectDatetime" -> datetime = part.value
|
||||
}
|
||||
}
|
||||
|
||||
else -> part.dispose()
|
||||
}
|
||||
}
|
||||
call.respond(BaseResponse(message = "上传成功", data = null))
|
||||
broadcastMessage("上传完成,开始启动AI引擎")
|
||||
val command = listOf(
|
||||
"/usr/bin/python3",
|
||||
"/home/xhcp/mine/IntelligentVideoAnalytics/AI_Project/DeepStream_Action_Recognition/core/final.py",
|
||||
"$VIDEO_INPUT_PATH$fileName",
|
||||
datetime,
|
||||
name
|
||||
}
|
||||
call.respond(BaseResponse(message = "上传成功", data = null))
|
||||
broadcastMessage("上传完成,开始启动AI引擎")
|
||||
val command = listOf(
|
||||
"/usr/bin/python3",
|
||||
"/home/xhcp/mine/IntelligentVideoAnalytics/AI_Project/DeepStream_Action_Recognition/core/final.py",
|
||||
"$VIDEO_INPUT_PATH$fileName",
|
||||
datetime,
|
||||
name
|
||||
)
|
||||
println("-----------------" + command.joinToString(" "))
|
||||
runCommand(command) {
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
// 获取已分析视频列表
|
||||
get("/getVideoList") {
|
||||
val name = call.parameters["name"]
|
||||
val res = VideoDao.getVideoList(name)
|
||||
call.respond(BaseResponse(data = res))
|
||||
}
|
||||
// 获取某视频分析详情
|
||||
get("/getAnalyticsDetailByVideoId") {
|
||||
// 获取 vId 参数
|
||||
val vIdParam = call.parameters["vId"]
|
||||
val vId = vIdParam?.toIntOrNull() // 将 vId 转换为 Int,确保安全
|
||||
if (vId == null) {
|
||||
call.respond(BaseResponse(status = true, message = "Invalid vId", data = null))
|
||||
return@get
|
||||
}
|
||||
// 查询视频信息
|
||||
val video = VideoDao.getAnalyticsDetailByVideoId(vId)
|
||||
if (video == null) {
|
||||
call.respond(BaseResponse(status = false, message = "不存在该视频", data = null))
|
||||
} else {
|
||||
// 查询相关的分析详情
|
||||
val details = VideoDao.selectVideoDetailByVid(vId)
|
||||
|
||||
// 用于返回的数据
|
||||
val yTotalData = mutableListOf<Pair<String, Int>>() // (时间, 总人数)
|
||||
val yMaskedData = mutableListOf<Pair<String, Int>>() // (时间, 佩戴口罩人数)
|
||||
val areaData = mutableListOf<List<Area>>()
|
||||
val detailList = mutableListOf<DetailItem>()
|
||||
// 颜色映射
|
||||
val colors = mapOf(
|
||||
"feed" to "rgba(0, 255, 0, 0.4)", // 淡绿色
|
||||
"disinfection" to "rgba(0, 0, 255, 0.4)", // 淡蓝色
|
||||
"other" to "rgba(255, 0, 0, 0.4)" // 淡红色
|
||||
)
|
||||
println("-----------------" + command.joinToString(" "))
|
||||
runCommand(command) {
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
// 获取已分析视频列表
|
||||
get("/getVideoList") {
|
||||
val name = call.parameters["name"]
|
||||
val res = VideoDao.getVideoList(name)
|
||||
call.respond(BaseResponse(data = res))
|
||||
}
|
||||
// 获取某视频分析详情
|
||||
get("/getAnalyticsDetailByVideoId") {
|
||||
// 获取 vId 参数
|
||||
val vIdParam = call.parameters["vId"]
|
||||
val vId = vIdParam?.toIntOrNull() // 将 vId 转换为 Int,确保安全
|
||||
if (vId == null) {
|
||||
call.respond(BaseResponse(status = true, message = "Invalid vId", data = null))
|
||||
return@get
|
||||
}
|
||||
// 查询视频信息
|
||||
val video = VideoDao.getAnalyticsDetailByVideoId(vId)
|
||||
if (video == null) {
|
||||
call.respond(BaseResponse(status = false, message = "不存在该视频", data = null))
|
||||
} else {
|
||||
// 查询相关的分析详情
|
||||
val details = VideoDao.selectVideoDetailByVid(vId)
|
||||
// 生成 yTotalData 和 yMaskedData,同时计算 areaData
|
||||
var lastAction: String? = null
|
||||
var areaStartTime: String? = null
|
||||
var currentColor: String? = null
|
||||
|
||||
// 用于返回的数据
|
||||
val yTotalData = mutableListOf<Pair<String, Int>>() // (时间, 总人数)
|
||||
val yMaskedData = mutableListOf<Pair<String, Int>>() // (时间, 佩戴口罩人数)
|
||||
val areaData = mutableListOf<List<Area>>()
|
||||
val detailList = mutableListOf<DetailItem>()
|
||||
// 颜色映射
|
||||
val colors = mapOf(
|
||||
"feed" to "rgba(0, 255, 0, 0.4)", // 淡绿色
|
||||
"disinfection" to "rgba(0, 0, 255, 0.4)", // 淡蓝色
|
||||
"other" to "rgba(255, 0, 0, 0.4)" // 淡红色
|
||||
)
|
||||
// 生成 yTotalData 和 yMaskedData,同时计算 areaData
|
||||
var lastAction: String? = null
|
||||
var areaStartTime: String? = null
|
||||
var currentColor: String? = null
|
||||
for (detail in details) {
|
||||
// 获取视频开始时间
|
||||
val vStartDatetime: LocalDateTime = video[vStartDateTime]!!
|
||||
val timeStr = vStartDatetime.toJavaLocalDateTime()
|
||||
.plusSeconds(detail.a_time_stamp.toLong())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
|
||||
for (detail in details) {
|
||||
// 获取视频开始时间
|
||||
val vStartDatetime: LocalDateTime = video[vStartDateTime]!!
|
||||
val timeStr = vStartDatetime.toJavaLocalDateTime()
|
||||
.plusSeconds(detail.a_time_stamp.toLong())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
// 添加总人数和口罩佩戴人数
|
||||
yTotalData.add(timeStr to detail.a_total_people)
|
||||
yMaskedData.add(timeStr to detail.a_total_people_masked)
|
||||
|
||||
// 添加总人数和口罩佩戴人数
|
||||
yTotalData.add(timeStr to detail.a_total_people)
|
||||
yMaskedData.add(timeStr to detail.a_total_people_masked)
|
||||
|
||||
// 处理 areaData,根据 a_action 判断是否需要创建新的区域
|
||||
if (detail.a_action != "--") { // 只处理非 "--" 动作
|
||||
if (lastAction == null || lastAction != detail.a_action) {
|
||||
// 如果上一个动作和当前动作不同,并且 areaStartTime 已经存在,创建新的区域
|
||||
if (areaStartTime != null) {
|
||||
areaData.add(
|
||||
listOf(
|
||||
Area(areaStartTime, ItemStyle(currentColor ?: "#FF0000")),
|
||||
Area(timeStr, ItemStyle(currentColor ?: "#FF0000"))
|
||||
)
|
||||
)
|
||||
}
|
||||
// 添加到 detailList,记录动作开始的时刻
|
||||
detailList.add(
|
||||
DetailItem(getFriendlyActionName(detail.a_action), time = timeStr)
|
||||
)
|
||||
|
||||
// 更新为新的动作
|
||||
lastAction = detail.a_action
|
||||
areaStartTime = timeStr
|
||||
currentColor = colors[detail.a_action] ?: "#FF0000" // 默认红色
|
||||
}
|
||||
} else {
|
||||
// 如果当前动作为 "--",则结束当前区域,并重置状态
|
||||
// 处理 areaData,根据 a_action 判断是否需要创建新的区域
|
||||
if (detail.a_action != "--") { // 只处理非 "--" 动作
|
||||
if (lastAction == null || lastAction != detail.a_action) {
|
||||
// 如果上一个动作和当前动作不同,并且 areaStartTime 已经存在,创建新的区域
|
||||
if (areaStartTime != null) {
|
||||
// 添加到 detailList,记录动作开始的时刻
|
||||
detailList.add(
|
||||
DetailItem(getFriendlyActionName(detail.a_action), timeStr)
|
||||
)
|
||||
// 结束当前区域
|
||||
areaData.add(
|
||||
listOf(
|
||||
Area(areaStartTime, ItemStyle(currentColor ?: "#FF0000")),
|
||||
Area(timeStr, ItemStyle(currentColor ?: "#FF0000"))
|
||||
)
|
||||
)
|
||||
// 重置状态
|
||||
lastAction = null
|
||||
areaStartTime = null
|
||||
currentColor = null
|
||||
}
|
||||
// 添加到 detailList,记录动作开始的时刻
|
||||
detailList.add(
|
||||
DetailItem(getFriendlyActionName(detail.a_action), time = timeStr)
|
||||
)
|
||||
|
||||
// 更新为新的动作
|
||||
lastAction = detail.a_action
|
||||
areaStartTime = timeStr
|
||||
currentColor = colors[detail.a_action] ?: "#FF0000" // 默认红色
|
||||
}
|
||||
} else {
|
||||
// 如果当前动作为 "--",则结束当前区域,并重置状态
|
||||
if (areaStartTime != null) {
|
||||
// 添加到 detailList,记录动作开始的时刻
|
||||
detailList.add(
|
||||
DetailItem(getFriendlyActionName(detail.a_action), timeStr)
|
||||
)
|
||||
// 结束当前区域
|
||||
areaData.add(
|
||||
listOf(
|
||||
Area(areaStartTime, ItemStyle(currentColor ?: "#FF0000")),
|
||||
Area(timeStr, ItemStyle(currentColor ?: "#FF0000"))
|
||||
)
|
||||
)
|
||||
// 重置状态
|
||||
lastAction = null
|
||||
areaStartTime = null
|
||||
currentColor = null
|
||||
}
|
||||
}
|
||||
// 处理最后一个区域的结束时间
|
||||
if (areaStartTime != null && lastAction != null) {
|
||||
areaData.add(
|
||||
listOf(
|
||||
Area(areaStartTime, ItemStyle(currentColor ?: "#FF0000")),
|
||||
Area(yTotalData.last().first, ItemStyle(currentColor ?: "#FF0000"))
|
||||
)
|
||||
)
|
||||
}
|
||||
val analyticsData = VideoAnalyticsData(
|
||||
yTotalData = yTotalData,
|
||||
yMaskedData = yMaskedData,
|
||||
areaData = areaData
|
||||
)
|
||||
// 返回数据
|
||||
call.respond(
|
||||
BaseResponse(
|
||||
data = VideoAnalyticsDetail(
|
||||
v_id = vId,
|
||||
v_name = video[VideoTable.vName],
|
||||
v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideoTable.vObjectName],
|
||||
v_file_name = video[VideoTable.vFileName],
|
||||
v_duration = video[VideoTable.vDuration],
|
||||
v_size = video[VideoTable.vSize],
|
||||
v_start_datetime = video[vStartDateTime]?.toString() ?: "",
|
||||
v_video_codec = video[VideoTable.vVideoCodec],
|
||||
v_audio_codec = video[VideoTable.vAudioCodec],
|
||||
v_overall_bit_rate = video[VideoTable.vOverallBitRate],
|
||||
v_resolution = video[VideoTable.vResolution],
|
||||
v_a_time = video[VideoTable.vATime].toString(),
|
||||
v_a_total_people = video[vATotalPeople], // 总人数
|
||||
v_a_count_people = video[vACountPeople], // 佩戴口罩人数
|
||||
v_a_max_stay_time = video[vAMaxStayTime], // 最大停留时间
|
||||
v_a_max_action = getFriendlyActionName(video[vAMaxAction]), // 最大动作
|
||||
v_a_average_masked_ratio = video[vAAverageMaskedRatio], // 平均佩戴口罩比例
|
||||
v_a_details = analyticsData, // 这里是计算得来的 VideoAnalyticsData
|
||||
v_details_list = detailList
|
||||
)
|
||||
}
|
||||
// 处理最后一个区域的结束时间
|
||||
if (areaStartTime != null && lastAction != null) {
|
||||
areaData.add(
|
||||
listOf(
|
||||
Area(areaStartTime, ItemStyle(currentColor ?: "#FF0000")),
|
||||
Area(yTotalData.last().first, ItemStyle(currentColor ?: "#FF0000"))
|
||||
)
|
||||
)
|
||||
}
|
||||
val analyticsData = VideoAnalyticsData(
|
||||
yTotalData = yTotalData,
|
||||
yMaskedData = yMaskedData,
|
||||
areaData = areaData
|
||||
)
|
||||
// 返回数据
|
||||
call.respond(
|
||||
BaseResponse(
|
||||
data = VideoAnalyticsDetail(
|
||||
v_id = vId,
|
||||
v_name = video[VideoTable.vName],
|
||||
v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideoTable.vObjectName],
|
||||
v_file_name = video[VideoTable.vFileName],
|
||||
v_duration = video[VideoTable.vDuration],
|
||||
v_size = video[VideoTable.vSize],
|
||||
v_start_datetime = video[vStartDateTime]?.toString() ?: "",
|
||||
v_video_codec = video[VideoTable.vVideoCodec],
|
||||
v_audio_codec = video[VideoTable.vAudioCodec],
|
||||
v_overall_bit_rate = video[VideoTable.vOverallBitRate],
|
||||
v_resolution = video[VideoTable.vResolution],
|
||||
v_a_time = video[VideoTable.vATime].toString(),
|
||||
v_a_total_people = video[vATotalPeople], // 总人数
|
||||
v_a_count_people = video[vACountPeople], // 佩戴口罩人数
|
||||
v_a_max_stay_time = video[vAMaxStayTime], // 最大停留时间
|
||||
v_a_max_action = getFriendlyActionName(video[vAMaxAction]), // 最大动作
|
||||
v_a_average_masked_ratio = video[vAAverageMaskedRatio], // 平均佩戴口罩比例
|
||||
v_a_details = analyticsData, // 这里是计算得来的 VideoAnalyticsData
|
||||
v_details_list = detailList
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
var aiState = "等待分析任务中"
|
||||
// 实时发送AI状态
|
||||
webSocket("/handleState") { // WebSocket 路由
|
||||
clients.add(this) // 添加当前连接的客户端
|
||||
send(aiState) // 向客户端发送连接成功消息
|
||||
try {
|
||||
incoming.consumeEach { frame -> // 持续接收消息
|
||||
when (frame) {
|
||||
is Frame.Text -> {
|
||||
aiState = frame.readText() // 更新状态
|
||||
broadcastMessage(aiState) // 使用封装的方法广播消息
|
||||
}
|
||||
|
||||
is Frame.Close -> {
|
||||
println("Closed")
|
||||
clients.remove(this)
|
||||
close() // 确保关闭 WebSocket 连接
|
||||
return@consumeEach
|
||||
}
|
||||
// 其他消息类型的处理
|
||||
is Frame.Binary -> TODO() // 处理二进制消息
|
||||
is Frame.Ping -> TODO() // 处理 Ping 消息
|
||||
is Frame.Pong -> TODO() // 处理 Pong 消息
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 处理接收消息时的异常
|
||||
close(CloseReason(CloseReason.Codes.NORMAL, "Client disconnected"))
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
clients.remove(this) // 确保在连接关闭时移除客户端
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 拍照保存为图片 并且调用Python程序进行分析
|
||||
//get("/takePhoto") {
|
||||
// val camera = call.request.queryParameters["cameraId"]
|
||||
// if (camera.isNullOrEmpty()) {
|
||||
// call.respond(BaseResponse(status = false, message = "摄像头名称不能为空", data = null))
|
||||
// return@get
|
||||
// }
|
||||
// stopHLSStream(camera)
|
||||
// call.respond(BaseResponse(message = "摄像头流已停止", data = null))
|
||||
//}
|
||||
Reference in New Issue
Block a user