录制视频
This commit is contained in:
parent
d179e541b6
commit
f550648cf4
|
@ -44,3 +44,4 @@ hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
|
|
||||||
logs/
|
logs/
|
||||||
|
.idea/
|
||||||
|
|
|
@ -12,7 +12,9 @@ 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.Java2DFrameConverter
|
import org.bytedeco.javacv.Java2DFrameConverter
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
@ -218,6 +220,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
fun recordAction(screenshot: JsonObject): JsonObject {
|
fun recordAction(screenshot: JsonObject): JsonObject {
|
||||||
taskCount.getAndAdd(1)
|
taskCount.getAndAdd(1)
|
||||||
val url = screenshot.getString("url")
|
val url = screenshot.getString("url")
|
||||||
|
val duration = screenshot.getLong("duration", 120) // 时长秒
|
||||||
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())
|
||||||
|
@ -231,39 +234,44 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
grabber.setOption("stimeout", "2000000")
|
grabber.setOption("stimeout", "2000000")
|
||||||
grabber.start()
|
grabber.start()
|
||||||
logger.info("grabber启动成功")
|
logger.info("grabber启动成功")
|
||||||
while (true) {
|
val videoWidth = grabber.imageWidth
|
||||||
when (val frame = grabber.grabImage()) {
|
val videoHeight = grabber.imageHeight
|
||||||
null -> {
|
val audioChannels = grabber.audioChannels
|
||||||
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 =
|
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(
|
||||||
"yyyyMMddHHmmss-HHmmss"
|
"yyyyMMddHHmmss-HHmmss"
|
||||||
)
|
)
|
||||||
.format(LocalDateTime.now()) + ".jpg"
|
.format(LocalDateTime.now()) + ".mp4"
|
||||||
logger.info("保存地址:$fileName")
|
val recorder = FFmpegFrameRecorder(fileName, videoWidth, videoHeight, audioChannels)
|
||||||
ImageIO.write(bufferedImage, "jpg", File(fileName))
|
recorder.videoBitrate = 4096
|
||||||
logger.info("图片保存成功:$fileName")
|
recorder.gopSize = 2
|
||||||
event!!.publish(
|
recorder.frameRate = 25.0
|
||||||
EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_PICTURE_SAVE.address,
|
recorder.videoCodecName = "copy"
|
||||||
JsonObject().put("fileSize", File(fileName).length())
|
recorder.format = "mp4"
|
||||||
.put("channelNo", screenshot.getString("channelId"))
|
try {
|
||||||
.put("filePath", fileName)
|
recorder.start(grabber.formatContext)
|
||||||
)
|
val startTime = System.currentTimeMillis() // 开始录制时间
|
||||||
|
while (true) { // 不断循环录制
|
||||||
|
val packet = grabber.grabPacket()
|
||||||
|
if (packet != null) {
|
||||||
|
recorder.recordPacket(packet)
|
||||||
|
}
|
||||||
|
var durationMS = System.currentTimeMillis() - startTime
|
||||||
|
if (durationMS > (duration * 1000)) { // 超过预设录制时长
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: FFmpegFrameRecorder.Exception) {
|
||||||
|
logger.info("recorder启动失败", e)
|
||||||
|
} finally {
|
||||||
|
logger.info("视频保存成功:$fileName")
|
||||||
|
recorder.stop()
|
||||||
|
recorder.close()
|
||||||
|
recorder.release()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("抓取截图·失败·", e)
|
logger.error("抓取录制短视频·失败·", e)
|
||||||
} finally {
|
} finally {
|
||||||
converter.close()
|
converter.close()
|
||||||
grabber.stop()
|
grabber.stop()
|
||||||
|
@ -275,5 +283,50 @@ class ScreenshotUtil(vertx: Vertx) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event!!.consumer<JsonObject>(EventBusAddress.SYS_DEVICE_CHANNEL_SCREENSHOT_VIDEO_SAVE.address)?.handler { replay ->
|
||||||
|
val message = replay.body()
|
||||||
|
val port = message.getString("monitorDomain").substringAfterLast(":").toInt()
|
||||||
|
var domain = message.getString("monitorDomain").substringBeforeLast(":").substringAfterLast("/")
|
||||||
|
val tokenString = message.getString("token")
|
||||||
|
|
||||||
|
val channelId = message.getString("channelId")
|
||||||
|
val subType = message.getString("subType")
|
||||||
|
val scheme = message.getString("scheme")
|
||||||
|
|
||||||
|
val duration = message.getLong("duration", 120) // 时长秒
|
||||||
|
|
||||||
|
val realmonitorUrl = "/videoService/realmonitor/uri?channelId=$channelId&subType=$subType&scheme=$scheme"
|
||||||
|
logger.info("获取设备通道预览url:$realmonitorUrl")
|
||||||
|
client!!.get(port, domain, realmonitorUrl)
|
||||||
|
.putHeader("X-Subject-Token", tokenString)
|
||||||
|
.send()
|
||||||
|
.onSuccess { res ->
|
||||||
|
val result = res.bodyAsJsonObject()
|
||||||
|
result.put("scheme", scheme)
|
||||||
|
logger.info("获取设备通道预览 statusCode:" + res.statusCode())
|
||||||
|
if (res.statusCode() == 200) {
|
||||||
|
result.put("channelId", channelId) // 通道id
|
||||||
|
result.put("duration", duration)
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
while (true) {
|
||||||
|
if (taskCount.get() < cpuNum * 2 - 1) {
|
||||||
|
replay.reply(recordAction(result))
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
logger.info("截图队列已满,尝试重试中:${result.encode()}")
|
||||||
|
val random = (3000..10000).random().toLong()
|
||||||
|
delay(random)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replay.reply(JsonObject().put("msg", "获取预览地址失败"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onFailure {
|
||||||
|
replay.reply(JsonObject().put("msg", "获取预览地址失败"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue