Request constrained AVC explicitly and diagnose hardware SPS limits

This commit is contained in:
evgeny committed 2026-10-14 22:49:31 +02:00
1 parent 156b250432
commit b01d54a431
1 file changed
+10 -8
@@ -23,7 +23,7 @@ internal class CallVideoCodec(val format: CallVideoFormat, val bitrate: Int, nam
try { try {
val media = MediaFormat.createVideoFormat(type.mime, format.width, format.height).apply { val media = MediaFormat.createVideoFormat(type.mime, format.width, format.height).apply {
setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible) setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible)
setInteger(MediaFormat.KEY_PROFILE, if (format.codec == 1) MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline setInteger(MediaFormat.KEY_PROFILE, if (format.codec == 1) MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline
else MediaCodecInfo.CodecProfileLevel.HEVCProfileMain) else MediaCodecInfo.CodecProfileLevel.HEVCProfileMain)
setInteger(MediaFormat.KEY_LEVEL, platformLevel(format.codec, format.level)) 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_BIT_RATE, bitrate); setInteger(MediaFormat.KEY_FRAME_RATE, format.fps)
@@ -122,11 +122,11 @@ internal class CallVideoCodec(val format: CallVideoFormat, val bitrate: Int, nam
try { try {
val f = CallVideoFormat(1, 1280, 720, codec = type.id, level = if (type.id == 1) 31 else 93) val f = CallVideoFormat(1, 1280, 720, codec = type.id, level = if (type.id == 1) 31 else 93)
val caps = entry.getCapabilitiesForType(type.mime) val caps = entry.getCapabilitiesForType(type.mime)
if (!caps.videoCapabilities.areSizeAndRateSupported(1280, 720, 30.0) if (caps.videoCapabilities?.areSizeAndRateSupported(1280, 720, 30.0) != true
|| !caps.encoderCapabilities.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) continue || caps.encoderCapabilities?.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR) != true) continue
CallVideoCodec(f, 1_000_000, entry.name).use { encoder -> CallVideoCodec(f, 1_000_000, entry.name).use { encoder ->
val packets = mutableListOf<Packet>(); var input = 0 val packets = mutableListOf<Packet>(); var input = 0
val deadline = System.nanoTime() + 2_000_000_000 val deadline = System.nanoTime() + 5_000_000_000
while (packets.size < 4 && System.nanoTime() < deadline) { while (packets.size < 4 && System.nanoTime() < deadline) {
if (input < 8 && encoder.submit(null, 1_000_000L + input * 33333, input == 0)) input++ if (input < 8 && encoder.submit(null, 1_000_000L + input * 33333, input == 0)) input++
packets += encoder.drain(); Thread.sleep(2) packets += encoder.drain(); Thread.sleep(2)
@@ -182,19 +182,21 @@ internal class CallVideoCodec(val format: CallVideoFormat, val bitrate: Int, nam
} }
val bits = Bits(raw.toByteArray()) val bits = Bits(raw.toByteArray())
if (f.codec == 1) { if (f.codec == 1) {
check(bits.read(8) == 66 && bits.read(8) and 64 != 0) { "AVC is not Constrained Baseline" } val profile = bits.read(8); val constraints = bits.read(8); val level = bits.read(8)
check(bits.read(8) <= f.level); bits.ue(); bits.ue() check(profile == 66 && constraints and 64 != 0) { "AVC profile=$profile constraints=$constraints level=$level SPS=${sps.joinToString { "%02x".format(it) }}" }
check(level <= f.level); bits.ue(); bits.ue()
when (bits.ue()) { 0 -> bits.ue(); 2 -> Unit; else -> error("Unsupported AVC POC") } when (bits.ue()) { 0 -> bits.ue(); 2 -> Unit; else -> error("Unsupported AVC POC") }
check(bits.ue() <= 1) { "AVC uses more than one reference" } check(bits.ue() <= 1) { "AVC uses more than one reference" }
} else { } else {
bits.read(4); check(bits.read(3) == 0) { "HEVC temporal layers" }; bits.read(1) bits.read(4); check(bits.read(3) == 0) { "HEVC temporal layers" }; bits.read(1)
bits.read(2); check(bits.read(1) == 0 && bits.read(5) == 1) { "HEVC is not Main/Main tier" } bits.read(2); val tier = bits.read(1); val profile = bits.read(5)
check(tier == 0 && profile == 1) { "HEVC profile=$profile tier=$tier SPS=${sps.joinToString { "%02x".format(it) }}" }
repeat(10) { bits.read(8) }; check(bits.read(8) <= f.level) repeat(10) { bits.read(8) }; check(bits.read(8) <= f.level)
bits.ue(); check(bits.ue() == 1); bits.ue(); bits.ue() bits.ue(); check(bits.ue() == 1); bits.ue(); bits.ue()
if (bits.read(1) != 0) repeat(4) { bits.ue() } if (bits.read(1) != 0) repeat(4) { bits.ue() }
check(bits.ue() == 0 && bits.ue() == 0); bits.ue(); bits.read(1) check(bits.ue() == 0 && bits.ue() == 0); bits.ue(); bits.read(1)
val dpb = bits.ue(); check(bits.ue() == 0) { "HEVC reordered frames" } val dpb = bits.ue(); check(bits.ue() == 0) { "HEVC reordered frames" }
check(dpb <= 1) { "HEVC requires more than two decoded pictures" } check(dpb <= 1) { "HEVC decoded pictures=${dpb + 1} SPS=${sps.joinToString { "%02x".format(it) }}" }
} }
} }
} }