diff --git a/hisense_dh.db b/hisense_dh.db index 9d241e3..245b7ed 100644 Binary files a/hisense_dh.db and b/hisense_dh.db differ diff --git a/src/main/kotlin/com/hisense/dahua_video/MainVerticle.kt b/src/main/kotlin/com/hisense/dahua_video/MainVerticle.kt index 3a23e2e..baa3999 100644 --- a/src/main/kotlin/com/hisense/dahua_video/MainVerticle.kt +++ b/src/main/kotlin/com/hisense/dahua_video/MainVerticle.kt @@ -4,6 +4,8 @@ import com.hazelcast.config.* 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.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.MonitorUserToken import com.hisense.dahua_video.modules.sys.SysUser @@ -167,6 +169,8 @@ class MainVerticle : AbstractVerticle() { val organization = Organization() val device = Device() val deviceChannel = DeviceChannel() + val screenshotPicture = ScreenshotPicture() + val screenshotVideo = ScreenshotVideo() logger.info("开始创表" + LocalDateTime.now().toString()) Database.connect( HikariDataSource(dbConfig) @@ -174,14 +178,25 @@ class MainVerticle : AbstractVerticle() { logger.info("连接完成" + LocalDateTime.now().toString()) transaction { addLogger(StdOutSqlLogger) - SchemaUtils.create(monitorUser, monitorUserToken, sysUser, organization, device, deviceChannel) + SchemaUtils.create( + monitorUser, + monitorUserToken, + sysUser, + organization, + device, + deviceChannel, + screenshotPicture, + screenshotVideo + ) SchemaUtils.createMissingTablesAndColumns( monitorUser, monitorUserToken, sysUser, organization, device, - deviceChannel + deviceChannel, + screenshotPicture, + screenshotVideo ) logger.info("创表成功" + LocalDateTime.now().toString()) } diff --git a/src/main/kotlin/com/hisense/dahua_video/consts/EventBusAddress.kt b/src/main/kotlin/com/hisense/dahua_video/consts/EventBusAddress.kt index 8844114..ec2ad63 100644 --- a/src/main/kotlin/com/hisense/dahua_video/consts/EventBusAddress.kt +++ b/src/main/kotlin/com/hisense/dahua_video/consts/EventBusAddress.kt @@ -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") } diff --git a/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotPictureDao.kt b/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotPictureDao.kt new file mode 100644 index 0000000..f053e20 --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotPictureDao.kt @@ -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(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") + } + } + } + } + +} diff --git a/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotVideoDao.kt b/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotVideoDao.kt new file mode 100644 index 0000000..c86536b --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/dao/ScreenshotVideoDao.kt @@ -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 + } + + +} diff --git a/src/main/kotlin/com/hisense/dahua_video/modules/device/package-info.java b/src/main/kotlin/com/hisense/dahua_video/modules/device/package-info.java new file mode 100644 index 0000000..687290b --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/modules/device/package-info.java @@ -0,0 +1,4 @@ +/** + * 第三方设备、设备通道 + */ +package com.hisense.dahua_video.modules.device; diff --git a/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotPicture.kt b/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotPicture.kt new file mode 100644 index 0000000..af41591 --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotPicture.kt @@ -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") // 文件大小(单位.比特) +} diff --git a/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotVideo.kt b/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotVideo.kt new file mode 100644 index 0000000..06681fc --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/modules/media/ScreenshotVideo.kt @@ -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") // 文件时长 +} diff --git a/src/main/kotlin/com/hisense/dahua_video/modules/media/package-info.java b/src/main/kotlin/com/hisense/dahua_video/modules/media/package-info.java new file mode 100644 index 0000000..c4c52cf --- /dev/null +++ b/src/main/kotlin/com/hisense/dahua_video/modules/media/package-info.java @@ -0,0 +1,4 @@ +/** + * 第三方通道截取媒体 + */ +package com.hisense.dahua_video.modules.media; diff --git a/src/main/kotlin/com/hisense/dahua_video/util/ScreenshotUtil.kt b/src/main/kotlin/com/hisense/dahua_video/util/ScreenshotUtil.kt index 6164135..fa7653a 100644 --- a/src/main/kotlin/com/hisense/dahua_video/util/ScreenshotUtil.kt +++ b/src/main/kotlin/com/hisense/dahua_video/util/ScreenshotUtil.kt @@ -28,6 +28,8 @@ class ScreenshotUtil(vertx: Vertx) { private val logger = LoggerFactory.getLogger(DevicesManagerUtil::class.java) private val cpuNum = Runtime.getRuntime().availableProcessors() + var taskCount = AtomicInteger(0) + private var vertx: Vertx private var event: EventBus? = null private var client: WebClient? = null @@ -40,6 +42,7 @@ class ScreenshotUtil(vertx: Vertx) { record = vertx.orCreateContext.config().getString("recroot", System.getProperty("user.dir") + File.separator + "record") screenshot() + recordVideo() } /** @@ -98,8 +101,6 @@ class ScreenshotUtil(vertx: Vertx) { * 截图通道画面 */ private fun screenshot() { - var taskCount = AtomicInteger(0) - /** * 执行截图 */ @@ -132,13 +133,20 @@ class ScreenshotUtil(vertx: Vertx) { File(filePath).mkdir() } 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" ) .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 } } @@ -150,6 +158,7 @@ class ScreenshotUtil(vertx: Vertx) { grabber.stop() grabber.release() taskCount.getAndAdd(-1) + logger.info("本地ffmpeg工作队列数量:${taskCount.get()}") } } 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 + } + + } } diff --git a/src/main/kotlin/com/hisense/dahua_video/verticle/SqlFactoryVerticle.kt b/src/main/kotlin/com/hisense/dahua_video/verticle/SqlFactoryVerticle.kt index 52be134..e80b2c6 100644 --- a/src/main/kotlin/com/hisense/dahua_video/verticle/SqlFactoryVerticle.kt +++ b/src/main/kotlin/com/hisense/dahua_video/verticle/SqlFactoryVerticle.kt @@ -31,7 +31,8 @@ class SqlFactoryVerticle : CoroutineVerticle() { shareDate.getLocalLock(EventBusAddress.SYS_MONITOR_USER_TOKEN_SAVE_NEW.address) .onSuccess { 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----------------------") event!!.publish(EventBusAddress.SYS_MONITOR_CLEANFAILTOKEN.address, JsonObject()) } @@ -42,6 +43,8 @@ class SqlFactoryVerticle : CoroutineVerticle() { .onSuccess { val deviceDao = DeviceDao(this.event, this.vertx) val deviceChannelDao = DeviceChannelDao(this.event) + val screenshotPictureDao = ScreenshotPictureDao(this.event, this.vertx) + val screenshotVideoDao = ScreenshotVideoDao(this.event, this.vertx) } startFuture?.complete() }