mirror of
https://github.com/pischule/memevizor.git
synced 2025-12-19 06:56:42 +00:00
Optimize transcoding
Skip transcoding if audio/video already has desired encoding
This commit is contained in:
@@ -46,6 +46,7 @@ class BotConfiguration(
|
||||
photos { handleMedia() }
|
||||
video { handleMedia() }
|
||||
videoNote { handleMedia() }
|
||||
document { handleMedia() }
|
||||
command("whoami") {
|
||||
handleMessage(message) {
|
||||
bot.sendMessage(
|
||||
|
||||
@@ -7,8 +7,18 @@ fun Message.getMedia(): MessageMedia? {
|
||||
return MessageMedia(fileId, MessageMedia.Type.PHOTO)
|
||||
}
|
||||
|
||||
(video?.fileId ?: videoNote?.fileId)?.let { fileId ->
|
||||
return MessageMedia(fileId, MessageMedia.Type.VIDEO)
|
||||
video?.let {
|
||||
return MessageMedia(it.fileId, MessageMedia.Type.VIDEO)
|
||||
}
|
||||
|
||||
videoNote?.let {
|
||||
return MessageMedia(it.fileId, MessageMedia.Type.VIDEO)
|
||||
}
|
||||
|
||||
document
|
||||
?.takeIf { it.mimeType?.startsWith("video/") == true }
|
||||
?.let {
|
||||
return MessageMedia(it.fileId, MessageMedia.Type.VIDEO)
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pischule.memevizor.video
|
||||
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.createTempFile
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.readBytes
|
||||
@@ -14,62 +15,32 @@ private val logger = KotlinLogging.logger {}
|
||||
class VideoTranscoderService {
|
||||
|
||||
fun transcode(inputVideo: ByteArray): ByteArray {
|
||||
logger.info { "Started transcoding a file" }
|
||||
logger.info { "Started transcoding a video" }
|
||||
|
||||
val inputFile = createTempFile()
|
||||
val outputFile = createTempFile()
|
||||
|
||||
val command: List<String> =
|
||||
listOf(
|
||||
"ffmpeg",
|
||||
"-nostdin",
|
||||
"-nostats",
|
||||
"-hide_banner",
|
||||
// input
|
||||
"-i",
|
||||
"$inputFile",
|
||||
// video
|
||||
"-map",
|
||||
"0:v",
|
||||
"-c:v",
|
||||
"libsvtav1",
|
||||
"-preset",
|
||||
"6",
|
||||
"-crf",
|
||||
"35",
|
||||
"-svtav1-params",
|
||||
"film-grain=10",
|
||||
// audio
|
||||
"-map",
|
||||
"0:a?",
|
||||
"-c:a",
|
||||
"libopus",
|
||||
"-b:a",
|
||||
"96k",
|
||||
"-vbr",
|
||||
"on",
|
||||
"-compression_level",
|
||||
"10",
|
||||
// output
|
||||
"-f",
|
||||
"webm",
|
||||
"-y",
|
||||
"$outputFile",
|
||||
)
|
||||
|
||||
try {
|
||||
inputFile.writeBytes(inputVideo)
|
||||
|
||||
val process = ProcessBuilder(command).redirectErrorStream(true).start()
|
||||
val exitCode: Int
|
||||
val timeTaken = measureTime { exitCode = process.waitFor() }
|
||||
val processOutput = process.inputStream.bufferedReader().use { it.readText() }
|
||||
if (exitCode != 0) {
|
||||
throw VideoTranscodingException(exitCode, processOutput)
|
||||
val copyVideo = hasDesiredVideoEncoding(inputFile)
|
||||
val copyAudio = hasDesiredAudioEncoding(inputFile)
|
||||
|
||||
val processOutput: String
|
||||
val timeTaken = measureTime {
|
||||
processOutput =
|
||||
launchCommand(
|
||||
transcodeCommand(
|
||||
inputFile = inputFile,
|
||||
outputFile = outputFile,
|
||||
copyVideo = copyVideo,
|
||||
copyAudio = copyAudio,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
logger.atInfo {
|
||||
message = "Finished transcoding a file"
|
||||
message = "Finished transcoding a video"
|
||||
payload =
|
||||
mapOf(
|
||||
"processOutput" to processOutput,
|
||||
@@ -82,4 +53,106 @@ class VideoTranscoderService {
|
||||
outputFile.deleteIfExists()
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchCommand(command: List<String>): String {
|
||||
val process = ProcessBuilder(command).redirectErrorStream(true).start()
|
||||
val exitCode = process.waitFor()
|
||||
val processOutput = process.inputStream.bufferedReader().use { it.readText() }
|
||||
if (exitCode != 0) {
|
||||
throw VideoTranscodingException(exitCode, processOutput)
|
||||
}
|
||||
return processOutput.trim()
|
||||
}
|
||||
|
||||
private fun hasDesiredVideoEncoding(inputFile: Path): Boolean {
|
||||
val videoCodec = launchCommand(getVideoCodecCommand(inputFile))
|
||||
return videoCodec == "av1"
|
||||
}
|
||||
|
||||
private fun hasDesiredAudioEncoding(inputFile: Path): Boolean {
|
||||
val audioCodec = launchCommand(getAudioCodecCommand(inputFile))
|
||||
return audioCodec == "opus" || audioCodec == ""
|
||||
}
|
||||
|
||||
private fun getVideoCodecCommand(inputFile: Path) =
|
||||
listOf(
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"v:0",
|
||||
"-show_entries",
|
||||
"stream=codec_name",
|
||||
"-of",
|
||||
"default=noprint_wrappers=1:nokey=1",
|
||||
"$inputFile",
|
||||
)
|
||||
|
||||
private fun getAudioCodecCommand(inputFile: Path) =
|
||||
listOf(
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"a:0",
|
||||
"-show_entries",
|
||||
"stream=codec_name",
|
||||
"-of",
|
||||
"default=noprint_wrappers=1:nokey=1",
|
||||
"$inputFile",
|
||||
)
|
||||
|
||||
private fun transcodeCommand(
|
||||
inputFile: Path,
|
||||
outputFile: Path,
|
||||
copyVideo: Boolean,
|
||||
copyAudio: Boolean,
|
||||
): List<String> = buildList {
|
||||
add("ffmpeg")
|
||||
add("-nostdin")
|
||||
add("-nostats")
|
||||
add("-hide_banner")
|
||||
|
||||
// input
|
||||
add("-i")
|
||||
add("$inputFile")
|
||||
|
||||
// video
|
||||
add("-map")
|
||||
add("0:v")
|
||||
add("-c:v")
|
||||
if (copyVideo) {
|
||||
add("copy")
|
||||
} else {
|
||||
add("libsvtav1")
|
||||
add("-preset")
|
||||
add("6")
|
||||
add("-crf")
|
||||
add("35")
|
||||
add("-svtav1-params")
|
||||
add("film-grain=10")
|
||||
}
|
||||
|
||||
// audio
|
||||
add("-map")
|
||||
add("0:a?")
|
||||
add("-c:a")
|
||||
if (copyAudio) {
|
||||
add("copy")
|
||||
} else {
|
||||
add("libopus")
|
||||
add("-b:a")
|
||||
add("96k")
|
||||
add("-vbr")
|
||||
add("on")
|
||||
add("-compression_level")
|
||||
add("10")
|
||||
}
|
||||
|
||||
// output
|
||||
add("-f")
|
||||
add("webm")
|
||||
add("-y")
|
||||
add("$outputFile")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user