diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt index f461cafe..e2289f5c 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt @@ -23,7 +23,7 @@ internal class CallVideoCodec(val format: CallVideoFormat, val bitrate: Int, nam try { val media = MediaFormat.createVideoFormat(type.mime, format.width, format.height).apply { 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) setInteger(MediaFormat.KEY_LEVEL, platformLevel(format.codec, format.level)) 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 { val f = CallVideoFormat(1, 1280, 720, codec = type.id, level = if (type.id == 1) 31 else 93) val caps = entry.getCapabilitiesForType(type.mime) - if (!caps.videoCapabilities.areSizeAndRateSupported(1280, 720, 30.0) - || !caps.encoderCapabilities.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) continue + if (caps.videoCapabilities?.areSizeAndRateSupported(1280, 720, 30.0) != true + || caps.encoderCapabilities?.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR) != true) continue CallVideoCodec(f, 1_000_000, entry.name).use { encoder -> val packets = mutableListOf(); 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) { if (input < 8 && encoder.submit(null, 1_000_000L + input * 33333, input == 0)) input++ 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()) if (f.codec == 1) { - check(bits.read(8) == 66 && bits.read(8) and 64 != 0) { "AVC is not Constrained Baseline" } - check(bits.read(8) <= f.level); bits.ue(); bits.ue() + val profile = bits.read(8); val constraints = bits.read(8); val level = bits.read(8) + 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") } check(bits.ue() <= 1) { "AVC uses more than one reference" } } else { 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) bits.ue(); check(bits.ue() == 1); bits.ue(); bits.ue() if (bits.read(1) != 0) repeat(4) { bits.ue() } check(bits.ue() == 0 && bits.ue() == 0); bits.ue(); bits.read(1) 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) }}" } } } }