Verify production HEVC modes and cross-platform reception

This commit is contained in:
evgeny committed 2026-10-19 23:36:58 +02:00
1 parent 08c74894ef
commit 7be4b8884c
3 files changed
+45 -12

No files matched your search

@@ -25,7 +25,9 @@ object CallVideoHevcProbe {
}
@JvmStatic fun main(args: Array<String>) {
require(args.size in 1..2 && (args.size == 1 || args[1] == "parameters")) { "Expected output directory [parameters]" }
require(args.size in 1..2 && (args.size == 1 || args[1] in listOf("parameters", "call"))) {
"Expected output directory [parameters|call]"
}
check(Build.VERSION.SDK_INT >= 29) { "Hardware codec enumeration requires API 29" }
val directory = File(args[0]); check(directory.isDirectory || directory.mkdirs())
val journal = File(directory, "results.log")
@@ -37,7 +39,39 @@ object CallVideoHevcProbe {
!it.isEncoder && it.isHardwareAccelerated && "video/hevc" in it.supportedTypes
}
report("start encoder=${encoder.name} decoder=${decoder.name} frames=$FRAMES")
if (args.size == 2) {
if (args.getOrNull(1) == "call") {
val encoders = CallVideoCodec.probe()
val decoders = CallVideoCodec.probeDecoders()
val modes = checkNotNull(encoders[2]) { "No verified HEVC encoder" }.modes
val receiving = checkNotNull(decoders[2]) { "No verified HEVC decoder" }
report("production encoders=$modes decoders=$receiving")
for (mode in modes) for (portrait in listOf(false, true)) {
val format = CallVideoFormat(1, if (portrait) mode.size.height else mode.size.width,
if (portrait) mode.size.width else mode.size.height, codec = 2, level = mode.level, profile = mode.profile)
val implementation = checkNotNull(receiving.modes.firstOrNull { it.supports(format) })
CallVideoCodec(format, 1_000_000, mode.name).use { codec ->
codec.prepare()
check(codec.format == format) { "Production mode differs from probe: ${codec.format}/$format" }
for ((phase, rate) in listOf(1_000_000, 4_000_000, 300_000, 4_000_000).withIndex()) {
check(codec.setBitrate(rate)) { "Bitrate update refused $rate" }
val packets = mutableListOf<CallVideoCodec.Packet>(); var input = 0
val base = 2_000_000L + phase * 1_000_000L
val deadline = System.nanoTime() + 5_000_000_000L
while (packets.size < FRAMES && System.nanoTime() < deadline) {
if (input < FRAMES && codec.submit(null, base + input * 33333, input == 0)) ++input
packets += codec.drain(); Thread.sleep(2)
}
check(packets.size == FRAMES && packets.first().key) { "Incomplete production encode" }
CallVideoCodec.verifyDecode(codec.format, packets, implementation.name)
report("production PASSED format=${codec.format} rate=$rate frames=${packets.size} decoder=${implementation.name}")
File(directory, "production-${format.width}x${format.height}-$phase.key").writeBytes(packets.first().bytes)
}
}
}
report("production finished modes=${modes.size}")
return
}
if (args.getOrNull(1) == "parameters") {
check(Build.VERSION.SDK_INT >= 31) { "Vendor parameter enumeration requires API 31" }
val codec = MediaCodec.createByCodecName(encoder.name)
try {
@@ -43,7 +43,8 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
else MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline
}
else MediaCodecInfo.CodecProfileLevel.HEVCProfileMain)
setInteger(MediaFormat.KEY_LEVEL, platformLevel(format.codec, format.level, format.profile))
// Запрос Main tier одинаков в probe/live; Qualcomm может выдать High, фактический режим проверяет prepare().
setInteger(MediaFormat.KEY_LEVEL, platformLevel(format.codec, format.level))
setInteger(MediaFormat.KEY_BIT_RATE, bitrate); setInteger(MediaFormat.KEY_FRAME_RATE, format.fps)
setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)
@@ -192,14 +193,11 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
}
companion object {
fun platformLevel(codec: Int, level: Int, profile: Int = 1): Int = if (codec == 1) when (level) {
fun platformLevel(codec: Int, level: Int): Int = if (codec == 1) when (level) {
30 -> MediaCodecInfo.CodecProfileLevel.AVCLevel3
31 -> MediaCodecInfo.CodecProfileLevel.AVCLevel31
40 -> MediaCodecInfo.CodecProfileLevel.AVCLevel4
else -> error("Unsupported AVC level $level")
} else if (profile == 2) {
check(level == 120) { "Unsupported HEVC High tier level $level" }
MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel4
} else when (level) {
90 -> MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel3
93 -> MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel31
@@ -291,7 +289,7 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
if (caps.videoCapabilities?.areSizeAndRateSupported(size.width, size.height, 30.0) != true) continue
var profiles = 0; var pipeline = 0; var maximumLevel = 0
for (profile in listOf(1, 2)) {
try {
try {
val level = if (type.id == 1) { if (size.width > 1280) 40 else 31 }
else if (size.width > 1280 || profile == 2) 120 else 93
var profilePipeline = 0
@@ -306,9 +304,10 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
}
profiles = profiles or profile; maximumLevel = maxOf(maximumLevel, level)
pipeline = maxOf(pipeline, profilePipeline)
} catch (e: Exception) {
LogManager.addLog("WARN", "call", "video decoder profile probe rejected name=${entry.name} codec=${type.id} profile=$profile size=$size: ${e.message}")
}
} catch (e: Exception) {
LogManager.addLog("WARN", "call", "video decoder profile probe rejected name=${entry.name} " +
"codec=${type.id} profile=$profile size=$size: ${e.message}")
}
}
check(profiles != 0) { "No verified decoder profiles" }
modes += DecoderMode(size, entry.name, profiles, maximumLevel, pipeline)
@@ -128,7 +128,7 @@ private slots:
}
void independentDecoders() {
auto caps = CallVideoDecoder::capabilities();
QCOMPARE(caps.decode[0].profiles, uint8_t(3)); QCOMPARE(caps.decode[1].profiles, uint8_t(1));
QCOMPARE(caps.decode[0].profiles, uint8_t(3)); QCOMPARE(caps.decode[1].profiles, uint8_t(3));
for (const auto& limit : caps.decode) {
QCOMPARE(limit.width, uint16_t(1920)); QCOMPARE(limit.height, uint16_t(1920));
QCOMPARE(limit.pixels_per_second, uint32_t(1920 * 1080 * 30));