通道界面与预览 初始化
This commit is contained in:
parent
e765a67fae
commit
64c9f6ca69
|
@ -0,0 +1,177 @@
|
||||||
|
package com.hisense.dahua_video.controller
|
||||||
|
|
||||||
|
import com.hisense.dahua_video.consts.EventBusAddress
|
||||||
|
import com.hisense.dahua_video.consts.MonitorScheme
|
||||||
|
import com.hisense.dahua_video.modules.device.Device
|
||||||
|
import com.hisense.dahua_video.modules.device.DeviceChannel
|
||||||
|
import com.hisense.dahua_video.modules.device.Organization
|
||||||
|
import com.hisense.dahua_video.modules.monitor.MonitorUser
|
||||||
|
import io.vertx.core.Vertx
|
||||||
|
import io.vertx.core.buffer.Buffer
|
||||||
|
import io.vertx.core.eventbus.EventBus
|
||||||
|
import io.vertx.core.impl.logging.Logger
|
||||||
|
import io.vertx.core.impl.logging.LoggerFactory
|
||||||
|
import io.vertx.core.json.JsonArray
|
||||||
|
import io.vertx.core.json.JsonObject
|
||||||
|
import io.vertx.ext.web.RoutingContext
|
||||||
|
import io.vertx.ext.web.templ.thymeleaf.ThymeleafTemplateEngine
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通道控制器
|
||||||
|
*/
|
||||||
|
class DeviceChannelController(
|
||||||
|
vertx: Vertx,
|
||||||
|
templateEngine: ThymeleafTemplateEngine?
|
||||||
|
) {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(DeviceChannelController::class.java)
|
||||||
|
var templateEngine: ThymeleafTemplateEngine? = null
|
||||||
|
private var event: EventBus? = null
|
||||||
|
private var vertx: Vertx
|
||||||
|
|
||||||
|
private val deviceChannel = DeviceChannel()
|
||||||
|
private val device = Device()
|
||||||
|
private val organization = Organization()
|
||||||
|
private val monitorUser = MonitorUser()
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.vertx = vertx
|
||||||
|
this.event = this.vertx.eventBus()
|
||||||
|
this.templateEngine = templateEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (设备通道)首页
|
||||||
|
*/
|
||||||
|
fun index(requestHandler: RoutingContext) {
|
||||||
|
val userId = requestHandler.get<Int>("userId")
|
||||||
|
val data = JsonObject()
|
||||||
|
templateEngine!!.render(
|
||||||
|
data, "templates/device_channel/index.html"
|
||||||
|
).onSuccess { success: Buffer? ->
|
||||||
|
requestHandler.response().putHeader("Content-Type", "text/html").end(success)
|
||||||
|
}.onFailure { fail: Throwable ->
|
||||||
|
requestHandler.fail(fail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* flv预览页面
|
||||||
|
*/
|
||||||
|
fun flv(requestHandler: RoutingContext) {
|
||||||
|
val userId = requestHandler.get<Int>("userId") // 用户id
|
||||||
|
val queryParams = requestHandler.queryParams()
|
||||||
|
val channelId = queryParams.get("channelId")
|
||||||
|
val subType = queryParams.get("subType")
|
||||||
|
val scheme = queryParams.get("scheme")
|
||||||
|
event!!.request<JsonArray>(EventBusAddress.SYS_MONITOR_USER_ALLMONITORUSER_TOKEN.address, JsonObject()) {
|
||||||
|
if (it.succeeded()) {
|
||||||
|
val token = it.result().body().stream().filter { index ->
|
||||||
|
index as JsonObject
|
||||||
|
index.getInteger("id") == userId
|
||||||
|
}.findFirst()
|
||||||
|
token.ifPresent { tokenInfo ->
|
||||||
|
tokenInfo as JsonObject
|
||||||
|
tokenInfo.put("channelId", channelId)
|
||||||
|
tokenInfo.put("subType", subType)
|
||||||
|
tokenInfo.put("scheme", MonitorScheme.values()[scheme.toInt()].scheme)
|
||||||
|
event!!.request<JsonObject>(EventBusAddress.SYS_DEVICE_CHANNEL_PREVIEW_URL.address, tokenInfo) { previewUrl ->
|
||||||
|
if (previewUrl.succeeded()) {
|
||||||
|
requestHandler
|
||||||
|
.response()
|
||||||
|
.putHeader("content-type", "application/json")
|
||||||
|
.end(previewUrl.result().body().encode())
|
||||||
|
} else {
|
||||||
|
requestHandler
|
||||||
|
.response()
|
||||||
|
.putHeader("content-type", "application/json")
|
||||||
|
.end(JsonObject().encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hls预览页面
|
||||||
|
*/
|
||||||
|
fun hls(requestHandler: RoutingContext) {
|
||||||
|
val queryParams = requestHandler.queryParams()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备通道分页
|
||||||
|
*/
|
||||||
|
fun deviceChannelPage(requestHandler: RoutingContext) {
|
||||||
|
val body = requestHandler.body().asJsonObject()
|
||||||
|
val size = body.getInteger("size", 10)
|
||||||
|
val page = body.getLong("page", 1)
|
||||||
|
val key = body.getString("key", "")
|
||||||
|
val userId = requestHandler.get<Int>("userId")
|
||||||
|
runBlocking(Dispatchers.IO) {
|
||||||
|
transaction {
|
||||||
|
addLogger(StdOutSqlLogger)
|
||||||
|
val query = if (key.isNullOrEmpty()) {
|
||||||
|
deviceChannel
|
||||||
|
.join(device, JoinType.LEFT, additionalConstraint = { deviceChannel.deviceId eq device.id })
|
||||||
|
.join(organization, JoinType.LEFT, additionalConstraint = { organization.id eq device.organizationId })
|
||||||
|
.join(monitorUser, JoinType.LEFT, additionalConstraint = { monitorUser.id eq organization.userId })
|
||||||
|
.slice(
|
||||||
|
deviceChannel.id,
|
||||||
|
deviceChannel.channelId,
|
||||||
|
deviceChannel.name,
|
||||||
|
deviceChannel.subType,
|
||||||
|
deviceChannel.online,
|
||||||
|
deviceChannel.sort
|
||||||
|
).select { monitorUser.id eq userId }
|
||||||
|
.orderBy(deviceChannel.sort to SortOrder.DESC)
|
||||||
|
} else {
|
||||||
|
deviceChannel
|
||||||
|
.join(device, JoinType.LEFT, additionalConstraint = { deviceChannel.deviceId eq device.id })
|
||||||
|
.join(organization, JoinType.LEFT, additionalConstraint = { organization.id eq device.organizationId })
|
||||||
|
.join(monitorUser, JoinType.LEFT, additionalConstraint = { monitorUser.id eq organization.userId })
|
||||||
|
.slice(
|
||||||
|
deviceChannel.id,
|
||||||
|
deviceChannel.channelId,
|
||||||
|
deviceChannel.name,
|
||||||
|
deviceChannel.subType,
|
||||||
|
deviceChannel.online,
|
||||||
|
deviceChannel.sort
|
||||||
|
).select { monitorUser.id eq userId }
|
||||||
|
.andWhere { deviceChannel.name like "%${key}%" }
|
||||||
|
.orderBy(deviceChannel.sort to SortOrder.DESC)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapToJson(it: ResultRow): JsonObject? {
|
||||||
|
return JsonObject()
|
||||||
|
.put("id", it[deviceChannel.id].value)
|
||||||
|
.put("channelId", it[deviceChannel.channelId])
|
||||||
|
.put("name", it[deviceChannel.name])
|
||||||
|
.put("subType", it[deviceChannel.subType])
|
||||||
|
.put("online", it[deviceChannel.online])
|
||||||
|
.put("sort", it[deviceChannel.sort])
|
||||||
|
}
|
||||||
|
|
||||||
|
val count = query.count()
|
||||||
|
val pageList = query.limit(size, (page - 1) * size).map { mapToJson(it) }
|
||||||
|
val result = JsonObject()
|
||||||
|
.put("code", 0)
|
||||||
|
.put("msg", LocalDateTime.now().toString())
|
||||||
|
.put("count", count)
|
||||||
|
.put("data", pageList)
|
||||||
|
requestHandler.response().putHeader("content-type", "application/json")
|
||||||
|
.end(result.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ class DeviceChannel : IntIdTable("sys_device_channel") {
|
||||||
val deviceId = integer("device_id").references(device.id) // 所属设备(外键)。
|
val deviceId = integer("device_id").references(device.id) // 所属设备(外键)。
|
||||||
val channelNo = text("channel_no").uniqueIndex() // 通道编码
|
val channelNo = text("channel_no").uniqueIndex() // 通道编码
|
||||||
val channelId = text("channel_id").uniqueIndex() // 通道编码 同channelNo
|
val channelId = text("channel_id").uniqueIndex() // 通道编码 同channelNo
|
||||||
val name = text("name").nullable() // 通道名称
|
val name = text("name").nullable().index() // 通道名称
|
||||||
val orgCode = text("org_code").nullable() // 组织编码,根组织为""
|
val orgCode = text("org_code").nullable() // 组织编码,根组织为""
|
||||||
val orgType = text("org_type").nullable() // 组织类型,"1"为基本组织
|
val orgType = text("org_type").nullable() // 组织类型,"1"为基本组织
|
||||||
val online = text("online").nullable() // 通道是否在线。"1":在线;"0":离线
|
val online = text("online").nullable() // 通道是否在线。"1":在线;"0":离线
|
||||||
|
|
|
@ -12,7 +12,6 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.bytedeco.ffmpeg.avcodec.AVPacket
|
|
||||||
import org.bytedeco.javacv.FFmpegFrameGrabber
|
import org.bytedeco.javacv.FFmpegFrameGrabber
|
||||||
import org.bytedeco.javacv.FFmpegFrameRecorder
|
import org.bytedeco.javacv.FFmpegFrameRecorder
|
||||||
import org.bytedeco.javacv.Java2DFrameConverter
|
import org.bytedeco.javacv.Java2DFrameConverter
|
||||||
|
@ -145,7 +144,8 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
logger.info("图片保存成功:$fileName")
|
logger.info("图片保存成功:$fileName")
|
||||||
event!!.publish(
|
event!!.publish(
|
||||||
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address,
|
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address,
|
||||||
JsonObject().put("fileSize", File(fileName).length())
|
JsonObject()
|
||||||
|
.put("fileSize", File(fileName).length())
|
||||||
.put("channelNo", screenshot.getString("channelId"))
|
.put("channelNo", screenshot.getString("channelId"))
|
||||||
.put("filePath", fileName)
|
.put("filePath", fileName)
|
||||||
)
|
)
|
||||||
|
@ -193,7 +193,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
replay.reply(screenshotAction(result))
|
replay.reply(screenshotAction(result))
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
logger.info("截图队列已满,尝试重试中:${result.encode()}")
|
logger.info("ffmpeg队列已满,尝试重试中:${result.encode()}")
|
||||||
val random = (3000..10000).random().toLong()
|
val random = (3000..10000).random().toLong()
|
||||||
delay(random)
|
delay(random)
|
||||||
}
|
}
|
||||||
|
@ -224,12 +224,27 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
logger.info("抓取url:$url")
|
logger.info("抓取url:$url")
|
||||||
val result = JsonObject()
|
val result = JsonObject()
|
||||||
result.put("startTime", LocalDateTime.now().toString())
|
result.put("startTime", LocalDateTime.now().toString())
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {// 使用协程
|
||||||
val grabber = FFmpegFrameGrabber(url)// 使用协程
|
val grabber = FFmpegFrameGrabber(url)
|
||||||
val converter = Java2DFrameConverter()
|
val converter = Java2DFrameConverter()
|
||||||
try {
|
try {
|
||||||
if (screenshot.getString("scheme") == MonitorScheme.RTSP.scheme) {
|
when (screenshot.getString("scheme")) {
|
||||||
grabber.setOption("rtsp_transport", "tcp")
|
MonitorScheme.RTSP.scheme -> {
|
||||||
|
grabber.setOption("rtsp_transport", "tcp")
|
||||||
|
grabber.format = MonitorScheme.RTSP.scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorScheme.FLV_HTTP.scheme -> {
|
||||||
|
grabber.format = "flv"
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorScheme.HLS.scheme -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorScheme.RTMP.scheme -> {
|
||||||
|
grabber.format = MonitorScheme.RTMP.scheme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
grabber.setOption("stimeout", "2000000")
|
grabber.setOption("stimeout", "2000000")
|
||||||
grabber.start()
|
grabber.start()
|
||||||
|
@ -237,6 +252,11 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
val videoWidth = grabber.imageWidth
|
val videoWidth = grabber.imageWidth
|
||||||
val videoHeight = grabber.imageHeight
|
val videoHeight = grabber.imageHeight
|
||||||
val audioChannels = grabber.audioChannels
|
val audioChannels = grabber.audioChannels
|
||||||
|
val videoCodecName = grabber.videoCodecName
|
||||||
|
val videoCodec = grabber.videoCodec
|
||||||
|
val formatContext = grabber.formatContext
|
||||||
|
logger.info("获取到预览流信息--> videoWidth:$videoWidth videoHeight:$videoHeight audioChannels:$audioChannels videoCodecName:$videoCodecName videoCodec:$videoCodec")
|
||||||
|
logger.info("formatContext--> $formatContext")
|
||||||
val fileName =
|
val fileName =
|
||||||
record + File.separator + screenshot.getString("channelId") +
|
record + File.separator + screenshot.getString("channelId") +
|
||||||
File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
||||||
|
@ -244,13 +264,15 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
)
|
)
|
||||||
.format(LocalDateTime.now()) + ".mp4"
|
.format(LocalDateTime.now()) + ".mp4"
|
||||||
val recorder = FFmpegFrameRecorder(fileName, videoWidth, videoHeight, audioChannels)
|
val recorder = FFmpegFrameRecorder(fileName, videoWidth, videoHeight, audioChannels)
|
||||||
recorder.videoBitrate = 4096
|
recorder.videoBitrate = grabber.videoBitrate
|
||||||
recorder.gopSize = 2
|
recorder.gopSize = 2
|
||||||
recorder.frameRate = 25.0
|
recorder.frameRate = 25.0
|
||||||
recorder.videoCodecName = "copy"
|
recorder.videoCodecName = "copy"
|
||||||
recorder.format = "mp4"
|
recorder.format = "mp4"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
recorder.start(grabber.formatContext)
|
recorder.start(grabber.formatContext)
|
||||||
|
logger.info("recorder 启动成功,开始进行录制")
|
||||||
val startTime = System.currentTimeMillis() // 开始录制时间
|
val startTime = System.currentTimeMillis() // 开始录制时间
|
||||||
while (true) { // 不断循环录制
|
while (true) { // 不断循环录制
|
||||||
val packet = grabber.grabPacket()
|
val packet = grabber.grabPacket()
|
||||||
|
@ -258,7 +280,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
recorder.recordPacket(packet)
|
recorder.recordPacket(packet)
|
||||||
}
|
}
|
||||||
var durationMS = System.currentTimeMillis() - startTime
|
var durationMS = System.currentTimeMillis() - startTime
|
||||||
if (durationMS > (duration * 1000)) { // 超过预设录制时长
|
if (durationMS >= (duration * 1000)) { // 超过预设录制时长
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,6 +291,24 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
recorder.stop()
|
recorder.stop()
|
||||||
recorder.close()
|
recorder.close()
|
||||||
recorder.release()
|
recorder.release()
|
||||||
|
delay(100) // 延迟100ms
|
||||||
|
/**
|
||||||
|
* 尝试获取视频时长
|
||||||
|
*/
|
||||||
|
val grabberFile = FFmpegFrameGrabber(fileName)
|
||||||
|
grabberFile.start()
|
||||||
|
val fileDuration = grabberFile.lengthInTime / 1000000 // 获取视频时长 秒
|
||||||
|
grabberFile.stop()
|
||||||
|
grabberFile.close()
|
||||||
|
grabberFile.release()
|
||||||
|
event!!.publish(
|
||||||
|
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_VIDEO_SAVE.address,
|
||||||
|
JsonObject()
|
||||||
|
.put("fileSize", File(fileName).length())
|
||||||
|
.put("channelNo", screenshot.getString("channelId"))
|
||||||
|
.put("filePath", fileName)
|
||||||
|
.put("duration", fileDuration)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("抓取录制短视频·失败·", e)
|
logger.error("抓取录制短视频·失败·", e)
|
||||||
|
@ -313,7 +353,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
replay.reply(recordAction(result))
|
replay.reply(recordAction(result))
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
logger.info("截图队列已满,尝试重试中:${result.encode()}")
|
logger.info("ffmpeg队列已满,尝试重试中:${result.encode()}")
|
||||||
val random = (3000..10000).random().toLong()
|
val random = (3000..10000).random().toLong()
|
||||||
delay(random)
|
delay(random)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ class WebAPIVerticle : CoroutineVerticle() {
|
||||||
val monitorUserController = MonitorUserController(this.vertx, templateEngine)
|
val monitorUserController = MonitorUserController(this.vertx, templateEngine)
|
||||||
val monitorController = MonitorController(this.vertx, templateEngine)
|
val monitorController = MonitorController(this.vertx, templateEngine)
|
||||||
val organizationController = OrganizationController(this.vertx, templateEngine)
|
val organizationController = OrganizationController(this.vertx, templateEngine)
|
||||||
|
val deviceChannelController = DeviceChannelController(this.vertx, templateEngine)
|
||||||
router.route().method(HttpMethod.POST).handler(BodyHandler.create())
|
router.route().method(HttpMethod.POST).handler(BodyHandler.create())
|
||||||
router.route("/*").handler(StaticHandler.create()) //静态资源
|
router.route("/*").handler(StaticHandler.create()) //静态资源
|
||||||
router.route().handler(
|
router.route().handler(
|
||||||
|
@ -80,7 +81,7 @@ class WebAPIVerticle : CoroutineVerticle() {
|
||||||
router.route().method(HttpMethod.GET).handler(commonController::getCros)
|
router.route().method(HttpMethod.GET).handler(commonController::getCros)
|
||||||
|
|
||||||
logger.info("开放管理员后台")
|
logger.info("开放管理员后台")
|
||||||
router.route("/admin/*").order(0).handler(commonController::checkLogin) // 管理首页
|
router.route("/admin/*").order(0).handler(commonController::checkLogin) // 管理首页(会先期校验登录情况)
|
||||||
router.route(HttpMethod.GET, "/admin/").handler(indexControl::index) // 首页
|
router.route(HttpMethod.GET, "/admin/").handler(indexControl::index) // 首页
|
||||||
router.route(HttpMethod.POST, "/admin/check").handler(indexControl::check) // 注册校验
|
router.route(HttpMethod.POST, "/admin/check").handler(indexControl::check) // 注册校验
|
||||||
router.route(HttpMethod.POST, "/admin/sign_up").handler(indexControl::sign_up) // 注册表单
|
router.route(HttpMethod.POST, "/admin/sign_up").handler(indexControl::sign_up) // 注册表单
|
||||||
|
@ -90,5 +91,8 @@ class WebAPIVerticle : CoroutineVerticle() {
|
||||||
router.route(HttpMethod.GET, "/admin/realmonitorUrl").handler(monitorController::realmonitorUrl) // 获取预览地址
|
router.route(HttpMethod.GET, "/admin/realmonitorUrl").handler(monitorController::realmonitorUrl) // 获取预览地址
|
||||||
router.route(HttpMethod.GET, "/admin/screenshot").handler(monitorController::screenshot) // 截图
|
router.route(HttpMethod.GET, "/admin/screenshot").handler(monitorController::screenshot) // 截图
|
||||||
router.route(HttpMethod.GET, "/admin/record").handler(monitorController::recordVideo) // 截取短视频
|
router.route(HttpMethod.GET, "/admin/record").handler(monitorController::recordVideo) // 截取短视频
|
||||||
|
|
||||||
|
router.route(HttpMethod.GET, "/admin/channel/index").handler(deviceChannelController::index) // 通道首页
|
||||||
|
router.route(HttpMethod.POST, "/admin/channel/page").handler(deviceChannelController::deviceChannelPage) // 通道分页
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,145 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>设备通道页面</title>
|
||||||
|
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content" style="margin-top: 15px;margin-left: 15px;">
|
||||||
|
<div class="container">
|
||||||
|
搜索通道名称:
|
||||||
|
<div class="layui-inline">
|
||||||
|
<input class="layui-input" name="id" id="demoReload" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<button class="layui-btn" data-type="reload">搜索</button>
|
||||||
|
<table lay-filter="channel_table" id="channel_table"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/html" id="barDemo">
|
||||||
|
<a href="#" title="通道信息" style="font-size: 18px"><i class="layui-icon layui-icon-template-1"
|
||||||
|
style="font-size: 18px; color: #5FB878;"
|
||||||
|
lay-event="info"></i></a>
|
||||||
|
<a href="#" title="预览flv" style="font-size: 18px"><i class="layui-icon layui-icon-play"
|
||||||
|
style="font-size: 18px; color: #5FB878;"
|
||||||
|
lay-event="flv"></i></a>
|
||||||
|
<a href="#" title="预览hls" style="font-size: 18px"><i class="layui-icon layui-icon-play"
|
||||||
|
style="font-size: 18px; color: #5FB878;"
|
||||||
|
lay-event="hls"></i></a>
|
||||||
|
<a href="#" title="截图" style="font-size: 18px"><i class="layui-icon layui-icon-picture"
|
||||||
|
style="font-size: 18px; color: #FF5722;" lay-event="download"></i></a>
|
||||||
|
<a href="#" title="录制短视频" style="font-size: 18px"><i class="layui-icon layui-icon-video"
|
||||||
|
style="font-size: 18px; color: #66CCFF;"
|
||||||
|
lay-event="record"></i></a>
|
||||||
|
</script>
|
||||||
|
<script type="text/html" id="onlineTpl">
|
||||||
|
{{# if(d.online == 0){ }}
|
||||||
|
<span style="color: #FF5722;">离线</span>
|
||||||
|
{{# } else { }}
|
||||||
|
<span style="color: #5FB878;">在线</span>
|
||||||
|
{{# } }}
|
||||||
|
</script>
|
||||||
|
<script th:src="@{/layui/layui.js}"></script>
|
||||||
|
<script language='javascript' th:inline="javascript">
|
||||||
|
layui.use('table', function(){
|
||||||
|
var table = layui.table;
|
||||||
|
var layer = layui.layer;
|
||||||
|
var col = [
|
||||||
|
{
|
||||||
|
field: 'channelId',
|
||||||
|
title: '通道编号',
|
||||||
|
width: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '通道名称',
|
||||||
|
width: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'subType',
|
||||||
|
title: '设备子类',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'online',
|
||||||
|
title: '是否在线',
|
||||||
|
width: 150,
|
||||||
|
templet: '#onlineTpl'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fixed: 'right',
|
||||||
|
width: 140,
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
toolbar: '#barDemo'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
var tableIns = table.render({
|
||||||
|
elem: '#channel_table'
|
||||||
|
, height: 500
|
||||||
|
, width: 930
|
||||||
|
, page: true
|
||||||
|
, limit: 10
|
||||||
|
, limits: [10, 20]
|
||||||
|
, title: '设备通道列表'
|
||||||
|
, loading: true
|
||||||
|
, cols: [
|
||||||
|
col
|
||||||
|
],
|
||||||
|
url: '/admin/channel/page',
|
||||||
|
method: 'post',
|
||||||
|
request: {
|
||||||
|
pageName: 'page',
|
||||||
|
limitName: 'size'
|
||||||
|
},
|
||||||
|
contentType: 'application/json',
|
||||||
|
id: 'channel_table'
|
||||||
|
});
|
||||||
|
table.on('tool(channel_table)', function (obj) {
|
||||||
|
var data = obj.data;
|
||||||
|
var layEvent = obj.event;
|
||||||
|
var tr = obj.tr;
|
||||||
|
if (layEvent === 'flv') { // flv 预览
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: false,
|
||||||
|
area: ['1280px', '720px'],
|
||||||
|
shade: 0.8,
|
||||||
|
closeBtn: 0,
|
||||||
|
scrollbar: false,
|
||||||
|
shadeClose: true,
|
||||||
|
content: '/vod?stream=' + data.stream + '&clientId=' + data.clientId + '&liveApp=' + data.liveApp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var $ = layui.$, active = {
|
||||||
|
reload: function(){
|
||||||
|
var demoReload = $('#demoReload');
|
||||||
|
|
||||||
|
//执行重载
|
||||||
|
table.reload('channel_table', {
|
||||||
|
page: {
|
||||||
|
curr: 1 //重新从第 1 页开始
|
||||||
|
}
|
||||||
|
,where: {
|
||||||
|
key: demoReload.val()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$('.container .layui-btn').on('click', function(){
|
||||||
|
var type = $(this).data('type');
|
||||||
|
active[type] ? active[type].call(this) : '';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue