Probe HEVC High tier on Android and desktop receivers
This commit is contained in:
1 parent
f2d67990f8
commit
08c74894ef
3 files changed
+37
-16
No files matched your search
@@ -43,7 +43,7 @@ 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))
|
||||
setInteger(MediaFormat.KEY_LEVEL, platformLevel(format.codec, format.level, format.profile))
|
||||
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,11 +192,14 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun platformLevel(codec: Int, level: Int): Int = if (codec == 1) when (level) {
|
||||
fun platformLevel(codec: Int, level: Int, profile: Int = 1): 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
|
||||
@@ -252,11 +255,20 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
val f = CallVideoFormat(1, w, h, codec = type.id,
|
||||
level = if (type.id == 1) { if (fullhd) 40 else 31 } else { if (fullhd) 120 else 93 },
|
||||
profile = if (type.id == 1) 2 else 1)
|
||||
CallVideoCodec(f, 1_000_000, entry.name).use { encoder ->
|
||||
encoder.prepare()
|
||||
check(foundProfile == 0 || foundProfile == encoder.format.profile) { "Geometry changes profile" }
|
||||
foundProfile = encoder.format.profile; foundLevel = maxOf(foundLevel, encoder.format.level)
|
||||
var verified: CallVideoFormat? = null
|
||||
for (level in (if (type.id == 2) listOf(f.level, 120) else listOf(f.level)).distinct()) {
|
||||
try {
|
||||
CallVideoCodec(f.copy(level = level), 1_000_000, entry.name).use { encoder ->
|
||||
encoder.prepare(); verified = encoder.format
|
||||
}
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("WARN", "call", "video encoder level probe rejected name=${entry.name} size=${w}x$h level=$level: ${e.message}")
|
||||
}
|
||||
}
|
||||
val actual = checkNotNull(verified) { "No valid encoder level: ${entry.name} ${w}x$h" }
|
||||
check(foundProfile == 0 || foundProfile == actual.profile) { "Geometry changes profile/tier" }
|
||||
foundProfile = actual.profile; foundLevel = maxOf(foundLevel, actual.level)
|
||||
}
|
||||
modes += EncoderMode(size, entry.name, foundProfile, foundLevel)
|
||||
LogManager.addLog("INFO", "call", "video codec probe passed type=${type.label} encoder=${entry.name} size=$size"); break
|
||||
@@ -277,21 +289,29 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
try {
|
||||
val caps = entry.getCapabilitiesForType(type.mime)
|
||||
if (caps.videoCapabilities?.areSizeAndRateSupported(size.width, size.height, 30.0) != true) continue
|
||||
var profiles = 0; var pipeline = 0
|
||||
val level = if (type.id == 1) { if (size.width > 1280) 40 else 31 } else { if (size.width > 1280) 120 else 93 }
|
||||
for (profile in if (type.id == 1) listOf(1, 2) else listOf(1)) {
|
||||
var profiles = 0; var pipeline = 0; var maximumLevel = 0
|
||||
for (profile in listOf(1, 2)) {
|
||||
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
|
||||
for (portrait in listOf(false, true)) {
|
||||
val w = if (portrait) size.height else size.width; val h = if (portrait) size.width else size.height
|
||||
check(caps.videoCapabilities?.areSizeAndRateSupported(w, h, 30.0) == true) { "Decoder geometry unsupported: ${w}x$h" }
|
||||
val f = CallVideoFormat(1, w, h, codec = type.id, level = level, profile = profile)
|
||||
val key = checkNotNull(CallVideoNative.probeKey(type.id, profile, w, h))
|
||||
validateKey(key, f)
|
||||
pipeline = maxOf(pipeline, verifyDecode(f, List(8) { Packet(1_000_000L + it * 33333, true, key) },
|
||||
profilePipeline = maxOf(profilePipeline, verifyDecode(f, List(8) { Packet(1_000_000L + it * 33333, true, key) },
|
||||
entry.name, true))
|
||||
}
|
||||
profiles = profiles or profile
|
||||
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}")
|
||||
}
|
||||
}
|
||||
modes += DecoderMode(size, entry.name, profiles, level, pipeline)
|
||||
check(profiles != 0) { "No verified decoder profiles" }
|
||||
modes += DecoderMode(size, entry.name, profiles, maximumLevel, pipeline)
|
||||
LogManager.addLog("INFO", "call", "video decoder verified codec=${type.id} name=${entry.name} size=$size profiles=$profiles pipeline=$pipeline")
|
||||
break
|
||||
} catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder probe rejected ${entry.name} size=$size: ${e.message}") }
|
||||
|
||||
@@ -271,7 +271,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
for (codec in if (settings.codec == 0) listOf(2, 1) else listOf(settings.codec)) {
|
||||
if (codec in excluded) continue
|
||||
val implementation = encoders[codec]?.modes?.firstOrNull { it.size == Size(settings.width, settings.height) } ?: continue
|
||||
val desired = CallVideoFormat(++generation, width, height, codec = codec)
|
||||
val desired = CallVideoFormat(++generation, width, height, codec = codec, profile = implementation.profile)
|
||||
val selected = CallVideoNative.choose(caps, peer, desired.array()) ?: continue
|
||||
var candidate: CallVideoCodec? = null
|
||||
try {
|
||||
|
||||
@@ -244,7 +244,7 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic<bool>* running)
|
||||
call_video_caps probed{};
|
||||
for (unsigned codec = 1; codec <= 2; ++codec) {
|
||||
unsigned profiles = 0, maximumTier = 0;
|
||||
for (unsigned profile = 1; profile <= (codec == 1 ? 2u : 1u); ++profile) {
|
||||
for (unsigned profile = 1; profile <= 2; ++profile) {
|
||||
for (unsigned tier = 0; tier < 2; ++tier) {
|
||||
const unsigned width = tier ? 1920 : 1280, height = tier ? 1080 : 720;
|
||||
for (Backend backend : {Backend::HardwareOnly, Backend::SoftwareOnly}) {
|
||||
@@ -260,7 +260,8 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic<bool>* running)
|
||||
call_video_frame input{}; input.call_id = 1; input.flags = CALL_VIDEO_KEY;
|
||||
input.data = bytes; input.size = input.length = size;
|
||||
input.format = {1, (uint16_t)w, (uint16_t)h, 30, 0,
|
||||
(uint8_t)codec, (uint8_t)(codec == 1 ? (tier ? 40 : 31) : (tier ? 120 : 93)), 4000000, (uint8_t)profile};
|
||||
(uint8_t)codec, (uint8_t)(codec == 1 ? (tier ? 40 : 31) : (tier || profile == 2 ? 120 : 93)),
|
||||
4000000, (uint8_t)profile};
|
||||
unsigned sent = 0, received = 0; bool valid = true, outputBeforeEos = false;
|
||||
const int64_t start = call_video_playout_now_us();
|
||||
while (valid && received < 8 && call_video_playout_now_us() - start < 1500000) {
|
||||
@@ -303,7 +304,7 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic<bool>* running)
|
||||
if (profiles) {
|
||||
const uint16_t width = maximumTier ? 1920 : 1280, height = maximumTier ? 1080 : 720;
|
||||
call_video_limit limit{width, height, 30, uint32_t(width) * height * 30, 4000000, (uint8_t)profiles,
|
||||
(uint8_t)(codec == 1 ? (maximumTier ? 40 : 31) : (maximumTier ? 120 : 93))};
|
||||
(uint8_t)(codec == 1 ? (maximumTier ? 40 : 31) : (maximumTier || (profiles & CALL_VIDEO_MAIN_HIGH_TIER) ? 120 : 93))};
|
||||
if (!call_video_limit_orientations(&limit, codec)) probed.decode[codec - 1] = limit;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user