录制视频

This commit is contained in:
DESKTOP-VA9NQUP\liwen 2022-11-27 17:09:12 +08:00
parent d179e541b6
commit f550648cf4
2 changed files with 81 additions and 27 deletions

1
.gitignore vendored
View File

@ -44,3 +44,4 @@ hs_err_pid*
replay_pid*
logs/
.idea/

View File

@ -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", "获取预览地址失败"))
}
}
}
}