截图保存 录制视频

This commit is contained in:
wangliwen 2022-11-25 16:30:48 +08:00
parent ebb61c87f0
commit d179e541b6
11 changed files with 246 additions and 7 deletions

Binary file not shown.

View File

@ -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())
}

View File

@ -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")
}

View File

@ -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")
}
}
}
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,4 @@
/**
* 第三方设备设备通道
*/
package com.hisense.dahua_video.modules.device;

View File

@ -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") // 文件大小(单位.比特)
}

View File

@ -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") // 文件时长
}

View File

@ -0,0 +1,4 @@
/**
* 第三方通道截取媒体
*/
package com.hisense.dahua_video.modules.media;

View File

@ -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
}
}
}

View File

@ -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()
}