Optimize transcoding

Skip transcoding if audio/video already has desired encoding
This commit is contained in:
2025-12-07 19:00:47 +03:00
parent 1266b0f440
commit 061743492e
3 changed files with 132 additions and 48 deletions

View File

@@ -46,6 +46,7 @@ class BotConfiguration(
photos { handleMedia() }
video { handleMedia() }
videoNote { handleMedia() }
document { handleMedia() }
command("whoami") {
handleMessage(message) {
bot.sendMessage(

View File

@@ -7,10 +7,20 @@ 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
}

View File

@@ -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")
}
}