截图保存 录制视频
This commit is contained in:
parent
ebb61c87f0
commit
d179e541b6
BIN
hisense_dh.db
BIN
hisense_dh.db
Binary file not shown.
|
@ -4,6 +4,8 @@ import com.hazelcast.config.*
|
||||||
import com.hisense.dahua_video.modules.device.Device
|
import com.hisense.dahua_video.modules.device.Device
|
||||||
import com.hisense.dahua_video.modules.device.DeviceChannel
|
import com.hisense.dahua_video.modules.device.DeviceChannel
|
||||||
import com.hisense.dahua_video.modules.device.Organization
|
import com.hisense.dahua_video.modules.device.Organization
|
||||||
|
import com.hisense.dahua_video.modules.media.ScreenshotPicture
|
||||||
|
import com.hisense.dahua_video.modules.media.ScreenshotVideo
|
||||||
import com.hisense.dahua_video.modules.monitor.MonitorUser
|
import com.hisense.dahua_video.modules.monitor.MonitorUser
|
||||||
import com.hisense.dahua_video.modules.monitor.MonitorUserToken
|
import com.hisense.dahua_video.modules.monitor.MonitorUserToken
|
||||||
import com.hisense.dahua_video.modules.sys.SysUser
|
import com.hisense.dahua_video.modules.sys.SysUser
|
||||||
|
@ -167,6 +169,8 @@ class MainVerticle : AbstractVerticle() {
|
||||||
val organization = Organization()
|
val organization = Organization()
|
||||||
val device = Device()
|
val device = Device()
|
||||||
val deviceChannel = DeviceChannel()
|
val deviceChannel = DeviceChannel()
|
||||||
|
val screenshotPicture = ScreenshotPicture()
|
||||||
|
val screenshotVideo = ScreenshotVideo()
|
||||||
logger.info("开始创表" + LocalDateTime.now().toString())
|
logger.info("开始创表" + LocalDateTime.now().toString())
|
||||||
Database.connect(
|
Database.connect(
|
||||||
HikariDataSource(dbConfig)
|
HikariDataSource(dbConfig)
|
||||||
|
@ -174,14 +178,25 @@ class MainVerticle : AbstractVerticle() {
|
||||||
logger.info("连接完成" + LocalDateTime.now().toString())
|
logger.info("连接完成" + LocalDateTime.now().toString())
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(StdOutSqlLogger)
|
addLogger(StdOutSqlLogger)
|
||||||
SchemaUtils.create(monitorUser, monitorUserToken, sysUser, organization, device, deviceChannel)
|
SchemaUtils.create(
|
||||||
|
monitorUser,
|
||||||
|
monitorUserToken,
|
||||||
|
sysUser,
|
||||||
|
organization,
|
||||||
|
device,
|
||||||
|
deviceChannel,
|
||||||
|
screenshotPicture,
|
||||||
|
screenshotVideo
|
||||||
|
)
|
||||||
SchemaUtils.createMissingTablesAndColumns(
|
SchemaUtils.createMissingTablesAndColumns(
|
||||||
monitorUser,
|
monitorUser,
|
||||||
monitorUserToken,
|
monitorUserToken,
|
||||||
sysUser,
|
sysUser,
|
||||||
organization,
|
organization,
|
||||||
device,
|
device,
|
||||||
deviceChannel
|
deviceChannel,
|
||||||
|
screenshotPicture,
|
||||||
|
screenshotVideo
|
||||||
)
|
)
|
||||||
logger.info("创表成功" + LocalDateTime.now().toString())
|
logger.info("创表成功" + LocalDateTime.now().toString())
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,16 @@ enum class EventBusAddress(val address: String) {
|
||||||
/**
|
/**
|
||||||
* 获取在线设备通道
|
* 获取在线设备通道
|
||||||
*/
|
*/
|
||||||
SYS_DEVICE_CHANNEL_ONLINE("MSG://EVENT/BUS/SQL/SYS/SYS_DEVICE_CHANNEL_ONLINE")
|
SYS_DEVICE_CHANNEL_ONLINE("MSG://EVENT/BUS/SQL/SYS/SYS_DEVICE_CHANNEL_ONLINE"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备通道截图保存
|
||||||
|
*/
|
||||||
|
SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE("MSG://EVENT/BUS/SQL/SYS/SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备通道截取视频
|
||||||
|
*/
|
||||||
|
SYS_DEVICE_CHANNEL_SCREENSHOT_VIDEO_SAVE("MSG://EVENT/BUS/SQL/SYS/SYS_DEVICE_CHANNEL_SCREENSHOT_VIDEO_SAVE")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.hisense.dahua_video.dao
|
||||||
|
|
||||||
|
import com.hisense.dahua_video.consts.EventBusAddress
|
||||||
|
import com.hisense.dahua_video.modules.device.DeviceChannel
|
||||||
|
import com.hisense.dahua_video.modules.media.ScreenshotPicture
|
||||||
|
import io.vertx.core.Vertx
|
||||||
|
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.JsonObject
|
||||||
|
import org.jetbrains.exposed.sql.StdOutSqlLogger
|
||||||
|
import org.jetbrains.exposed.sql.addLogger
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截图 dao
|
||||||
|
*/
|
||||||
|
class ScreenshotPictureDao(event_: EventBus?, vertx: Vertx) {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(ScreenshotPictureDao::class.java)
|
||||||
|
private val cpuNum = Runtime.getRuntime().availableProcessors()
|
||||||
|
private var event: EventBus? = null
|
||||||
|
private var vertx: Vertx? = null
|
||||||
|
private val screenshotPicture = ScreenshotPicture()
|
||||||
|
private val deviceChannel = DeviceChannel()
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.event = event_
|
||||||
|
this.vertx = vertx
|
||||||
|
savePicture()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存截图图片
|
||||||
|
*/
|
||||||
|
private fun savePicture() {
|
||||||
|
/**
|
||||||
|
* 只处理本地事件
|
||||||
|
*/
|
||||||
|
event!!.localConsumer<JsonObject>(EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address)
|
||||||
|
?.handler { replay ->
|
||||||
|
val message = replay.body()
|
||||||
|
logger.info("savePicture:" + message.encode())
|
||||||
|
val channelId = transaction {
|
||||||
|
addLogger(StdOutSqlLogger)
|
||||||
|
deviceChannel.slice(deviceChannel.id).select { deviceChannel.channelNo eq message.getString("channelNo") }
|
||||||
|
.withDistinct().map { it[deviceChannel.id] }.firstOrNull()?.value
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
addLogger(StdOutSqlLogger)
|
||||||
|
screenshotPicture.insert {
|
||||||
|
it[screenshotPicture.channelId] = channelId!!.toInt()
|
||||||
|
it[screenshotPicture.channelNo] = message.getString("channelNo")
|
||||||
|
it[screenshotPicture.createTime] = LocalDateTime.now()
|
||||||
|
it[screenshotPicture.filePath] = message.getString("filePath")
|
||||||
|
it[screenshotPicture.fileSize] = message.getLong("fileSize")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.hisense.dahua_video.dao
|
||||||
|
|
||||||
|
import io.vertx.core.Vertx
|
||||||
|
import io.vertx.core.eventbus.EventBus
|
||||||
|
import io.vertx.core.impl.logging.Logger
|
||||||
|
import io.vertx.core.impl.logging.LoggerFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截取短视频功能dao
|
||||||
|
*/
|
||||||
|
class ScreenshotVideoDao(event_: EventBus?, vertx: Vertx) {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(ScreenshotVideoDao::class.java)
|
||||||
|
private val cpuNum = Runtime.getRuntime().availableProcessors()
|
||||||
|
private var event: EventBus? = null
|
||||||
|
private var vertx: Vertx? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.event = event_
|
||||||
|
this.vertx = vertx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* 第三方设备、设备通道
|
||||||
|
*/
|
||||||
|
package com.hisense.dahua_video.modules.device;
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.hisense.dahua_video.modules.media
|
||||||
|
|
||||||
|
import com.hisense.dahua_video.modules.device.DeviceChannel
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备通道截图保存
|
||||||
|
*/
|
||||||
|
class ScreenshotPicture : IntIdTable("sys_device_channel_screenshot_picture") {
|
||||||
|
private val deviceChannel = DeviceChannel()
|
||||||
|
val channelId = integer("channel_id").references(deviceChannel.id).index() // 所属设备通道(外键)。
|
||||||
|
val channelNo = text("channel_no") // 通道编码
|
||||||
|
val createTime = datetime("create_time").nullable() // 创建时间
|
||||||
|
val filePath = text("file_path") // 文件本地路径
|
||||||
|
val fileSize = long("file_size") // 文件大小(单位.比特)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.hisense.dahua_video.modules.media
|
||||||
|
|
||||||
|
import com.hisense.dahua_video.modules.device.DeviceChannel
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频通道截取视频
|
||||||
|
*/
|
||||||
|
class ScreenshotVideo : IntIdTable("sys_device_channel_screenshot_video") {
|
||||||
|
private val deviceChannel = DeviceChannel()
|
||||||
|
val channelId = integer("channel_id").references(deviceChannel.id).index() // 所属设备通道(外键)。
|
||||||
|
val channelNo = text("channel_no") // 通道编码
|
||||||
|
val createTime = datetime("create_time").nullable() // 创建时间
|
||||||
|
val filePath = text("file_path") // 文件本地路径
|
||||||
|
val fileSize = long("file_size") // 文件大小(单位.比特)
|
||||||
|
val duration = long("duration") // 文件时长
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* 第三方通道截取媒体
|
||||||
|
*/
|
||||||
|
package com.hisense.dahua_video.modules.media;
|
|
@ -28,6 +28,8 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
private val logger = LoggerFactory.getLogger(DevicesManagerUtil::class.java)
|
private val logger = LoggerFactory.getLogger(DevicesManagerUtil::class.java)
|
||||||
private val cpuNum = Runtime.getRuntime().availableProcessors()
|
private val cpuNum = Runtime.getRuntime().availableProcessors()
|
||||||
|
|
||||||
|
var taskCount = AtomicInteger(0)
|
||||||
|
|
||||||
private var vertx: Vertx
|
private var vertx: Vertx
|
||||||
private var event: EventBus? = null
|
private var event: EventBus? = null
|
||||||
private var client: WebClient? = null
|
private var client: WebClient? = null
|
||||||
|
@ -40,6 +42,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
record =
|
record =
|
||||||
vertx.orCreateContext.config().getString("recroot", System.getProperty("user.dir") + File.separator + "record")
|
vertx.orCreateContext.config().getString("recroot", System.getProperty("user.dir") + File.separator + "record")
|
||||||
screenshot()
|
screenshot()
|
||||||
|
recordVideo()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,8 +101,6 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
* 截图通道画面
|
* 截图通道画面
|
||||||
*/
|
*/
|
||||||
private fun screenshot() {
|
private fun screenshot() {
|
||||||
var taskCount = AtomicInteger(0)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行截图
|
* 执行截图
|
||||||
*/
|
*/
|
||||||
|
@ -132,13 +133,20 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
File(filePath).mkdir()
|
File(filePath).mkdir()
|
||||||
}
|
}
|
||||||
val fileName =
|
val fileName =
|
||||||
record + File.separator + screenshot.getString("channelId") + File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
record + File.separator + screenshot.getString("channelId") +
|
||||||
|
File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
||||||
"yyyyMMddHHmmss-HHmmss"
|
"yyyyMMddHHmmss-HHmmss"
|
||||||
)
|
)
|
||||||
.format(LocalDateTime.now()) + ".jpg"
|
.format(LocalDateTime.now()) + ".jpg"
|
||||||
logger.info("保存地址:$fileName")
|
logger.info("保存地址:$fileName")
|
||||||
ImageIO.write(bufferedImage, "jpg", File(fileName))
|
ImageIO.write(bufferedImage, "jpg", File(fileName))
|
||||||
logger.info("图片保存成功:$fileName")
|
logger.info("图片保存成功:$fileName")
|
||||||
|
event!!.publish(
|
||||||
|
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address,
|
||||||
|
JsonObject().put("fileSize", File(fileName).length())
|
||||||
|
.put("channelNo", screenshot.getString("channelId"))
|
||||||
|
.put("filePath", fileName)
|
||||||
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,6 +158,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
grabber.stop()
|
grabber.stop()
|
||||||
grabber.release()
|
grabber.release()
|
||||||
taskCount.getAndAdd(-1)
|
taskCount.getAndAdd(-1)
|
||||||
|
logger.info("本地ffmpeg工作队列数量:${taskCount.get()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -198,4 +207,73 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*进行视频录制
|
||||||
|
*/
|
||||||
|
private fun recordVideo() {
|
||||||
|
/**
|
||||||
|
* 执行截图
|
||||||
|
*/
|
||||||
|
fun recordAction(screenshot: JsonObject): JsonObject {
|
||||||
|
taskCount.getAndAdd(1)
|
||||||
|
val url = screenshot.getString("url")
|
||||||
|
logger.info("抓取url:$url")
|
||||||
|
val result = JsonObject()
|
||||||
|
result.put("startTime", LocalDateTime.now().toString())
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
val grabber = FFmpegFrameGrabber(url)// 使用协程
|
||||||
|
val converter = Java2DFrameConverter()
|
||||||
|
try {
|
||||||
|
if (screenshot.getString("scheme") == MonitorScheme.RTSP.scheme) {
|
||||||
|
grabber.setOption("rtsp_transport", "tcp")
|
||||||
|
}
|
||||||
|
grabber.setOption("stimeout", "2000000")
|
||||||
|
grabber.start()
|
||||||
|
logger.info("grabber启动成功")
|
||||||
|
while (true) {
|
||||||
|
when (val frame = grabber.grabImage()) {
|
||||||
|
null -> {
|
||||||
|
logger.info("截图取帧失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val bufferedImage = converter.getBufferedImage(frame)
|
||||||
|
val filePath = record + File.separator + screenshot.getString("channelId")
|
||||||
|
if (!File(filePath).exists()) {
|
||||||
|
File(filePath).mkdir()
|
||||||
|
}
|
||||||
|
val fileName =
|
||||||
|
record + File.separator + screenshot.getString("channelId") +
|
||||||
|
File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
||||||
|
"yyyyMMddHHmmss-HHmmss"
|
||||||
|
)
|
||||||
|
.format(LocalDateTime.now()) + ".jpg"
|
||||||
|
logger.info("保存地址:$fileName")
|
||||||
|
ImageIO.write(bufferedImage, "jpg", File(fileName))
|
||||||
|
logger.info("图片保存成功:$fileName")
|
||||||
|
event!!.publish(
|
||||||
|
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address,
|
||||||
|
JsonObject().put("fileSize", File(fileName).length())
|
||||||
|
.put("channelNo", screenshot.getString("channelId"))
|
||||||
|
.put("filePath", fileName)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("抓取截图·失败·", e)
|
||||||
|
} finally {
|
||||||
|
converter.close()
|
||||||
|
grabber.stop()
|
||||||
|
grabber.release()
|
||||||
|
taskCount.getAndAdd(-1)
|
||||||
|
logger.info("本地ffmpeg工作队列数量:${taskCount.get()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ class SqlFactoryVerticle : CoroutineVerticle() {
|
||||||
shareDate.getLocalLock(EventBusAddress.SYS_MONITOR_USER_TOKEN_SAVE_NEW.address)
|
shareDate.getLocalLock(EventBusAddress.SYS_MONITOR_USER_TOKEN_SAVE_NEW.address)
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
val monitorUserTokenDao = MonitorUserTokenDao(this.event)
|
val monitorUserTokenDao = MonitorUserTokenDao(this.event)
|
||||||
val cleanfailtokenTaskId = vertx.setPeriodic(5 * 60 * 1000) { // 每间隔5分钟进行一次失效token清理
|
|
||||||
|
val cleanfailtokenTaskId = vertx.setPeriodic(30 * 60 * 1000) { // 每间隔30分钟进行一次失效token清理
|
||||||
logger.info("--------------------------定时清理失效token----------------------")
|
logger.info("--------------------------定时清理失效token----------------------")
|
||||||
event!!.publish(EventBusAddress.SYS_MONITOR_CLEANFAILTOKEN.address, JsonObject())
|
event!!.publish(EventBusAddress.SYS_MONITOR_CLEANFAILTOKEN.address, JsonObject())
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,8 @@ class SqlFactoryVerticle : CoroutineVerticle() {
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
val deviceDao = DeviceDao(this.event, this.vertx)
|
val deviceDao = DeviceDao(this.event, this.vertx)
|
||||||
val deviceChannelDao = DeviceChannelDao(this.event)
|
val deviceChannelDao = DeviceChannelDao(this.event)
|
||||||
|
val screenshotPictureDao = ScreenshotPictureDao(this.event, this.vertx)
|
||||||
|
val screenshotVideoDao = ScreenshotVideoDao(this.event, this.vertx)
|
||||||
}
|
}
|
||||||
startFuture?.complete()
|
startFuture?.complete()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue