录制视频
This commit is contained in:
parent
d179e541b6
commit
f550648cf4
|
@ -44,3 +44,4 @@ hs_err_pid*
|
|||
replay_pid*
|
||||
|
||||
logs/
|
||||
.idea/
|
||||
|
|
|
@ -12,7 +12,9 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.bytedeco.ffmpeg.avcodec.AVPacket
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber
|
||||
import org.bytedeco.javacv.FFmpegFrameRecorder
|
||||
import org.bytedeco.javacv.Java2DFrameConverter
|
||||
import java.io.File
|
||||
import java.time.LocalDateTime
|
||||
|
@ -218,6 +220,7 @@ class ScreenshotUtil(vertx: Vertx) {
|
|||
fun recordAction(screenshot: JsonObject): JsonObject {
|
||||
taskCount.getAndAdd(1)
|
||||
val url = screenshot.getString("url")
|
||||
val duration = screenshot.getLong("duration", 120) // 时长秒
|
||||
logger.info("抓取url:$url")
|
||||
val result = JsonObject()
|
||||
result.put("startTime", LocalDateTime.now().toString())
|
||||
|
@ -231,39 +234,44 @@ class ScreenshotUtil(vertx: Vertx) {
|
|||
grabber.setOption("stimeout", "2000000")
|
||||
grabber.start()
|
||||
logger.info("grabber启动成功")
|
||||
while (true) {
|
||||
when (val frame = grabber.grabImage()) {
|
||||
null -> {
|
||||
logger.info("截图取帧失败")
|
||||
val videoWidth = grabber.imageWidth
|
||||
val videoHeight = grabber.imageHeight
|
||||
val audioChannels = grabber.audioChannels
|
||||
val fileName =
|
||||
record + File.separator + screenshot.getString("channelId") +
|
||||
File.separator + screenshot.getString("channelId") + "_" + DateTimeFormatter.ofPattern(
|
||||
"yyyyMMddHHmmss-HHmmss"
|
||||
)
|
||||
.format(LocalDateTime.now()) + ".mp4"
|
||||
val recorder = FFmpegFrameRecorder(fileName, videoWidth, videoHeight, audioChannels)
|
||||
recorder.videoBitrate = 4096
|
||||
recorder.gopSize = 2
|
||||
recorder.frameRate = 25.0
|
||||
recorder.videoCodecName = "copy"
|
||||
recorder.format = "mp4"
|
||||
try {
|
||||
recorder.start(grabber.formatContext)
|
||||
val startTime = System.currentTimeMillis() // 开始录制时间
|
||||
while (true) { // 不断循环录制
|
||||
val packet = grabber.grabPacket()
|
||||
if (packet != null) {
|
||||
recorder.recordPacket(packet)
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
var durationMS = System.currentTimeMillis() - startTime
|
||||
if (durationMS > (duration * 1000)) { // 超过预设录制时长
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e: FFmpegFrameRecorder.Exception) {
|
||||
logger.info("recorder启动失败", e)
|
||||
} finally {
|
||||
logger.info("视频保存成功:$fileName")
|
||||
recorder.stop()
|
||||
recorder.close()
|
||||
recorder.release()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("抓取截图·失败·", e)
|
||||
logger.error("抓取录制短视频·失败·", e)
|
||||
} finally {
|
||||
converter.close()
|
||||
grabber.stop()
|
||||
|
@ -275,5 +283,50 @@ class ScreenshotUtil(vertx: Vertx) {
|
|||
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