录制视频

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* replay_pid*
logs/ logs/
.idea/

View File

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