Verify production HEVC modes and cross-platform reception
This commit is contained in:
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));
|
||||
|
||||
Reference in new issue
Block a user