Log Android verified camera modes and guard early camera events
This commit is contained in:
1 parent
fcdc098415
commit
c211e17af9
3 files changed
+59
-53
No files matched your search
@@ -238,66 +238,66 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
fun probe(): Map<Int, Encoder> {
|
||||
val result = mutableMapOf<Int, Encoder>()
|
||||
for (type in listOf(StreamVideoCodec.H265, StreamVideoCodec.H264)) {
|
||||
val modes = mutableListOf<EncoderMode>()
|
||||
for (size in listOf(Size(640, 480), Size(1280, 720), Size(1920, 1080))) {
|
||||
for (entry in candidates(true)) {
|
||||
if (entry.supportedTypes.none { it == type.mime }) continue
|
||||
try {
|
||||
val caps = entry.getCapabilitiesForType(type.mime)
|
||||
if (caps.encoderCapabilities?.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR) != true) continue
|
||||
var foundProfile = 0; var foundLevel = 0
|
||||
for ((w, h) in listOf(size.width to size.height, size.height to size.width)) {
|
||||
check(caps.videoCapabilities?.areSizeAndRateSupported(w, h, 30.0) == true) { "Geometry unsupported: ${w}x$h" }
|
||||
val fullhd = size.width > 1280
|
||||
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)
|
||||
}
|
||||
val modes = mutableListOf<EncoderMode>()
|
||||
for (size in listOf(Size(640, 480), Size(1280, 720), Size(1920, 1080))) {
|
||||
for (entry in candidates(true)) {
|
||||
if (entry.supportedTypes.none { it == type.mime }) continue
|
||||
try {
|
||||
val caps = entry.getCapabilitiesForType(type.mime)
|
||||
if (caps.encoderCapabilities?.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR) != true) continue
|
||||
var foundProfile = 0; var foundLevel = 0
|
||||
for ((w, h) in listOf(size.width to size.height, size.height to size.width)) {
|
||||
check(caps.videoCapabilities?.areSizeAndRateSupported(w, h, 30.0) == true) { "Geometry unsupported: ${w}x$h" }
|
||||
val fullhd = size.width > 1280
|
||||
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)
|
||||
}
|
||||
modes += EncoderMode(size, entry.name, foundProfile, foundLevel)
|
||||
LogManager.addLog("INFO", "call", "video codec probe passed type=${type.label} encoder=${entry.name} size=$size"); break
|
||||
} catch (e: Exception) { LogManager.addLog("WARN", "call", "video codec probe rejected ${entry.name} size=$size: ${e.message}") }
|
||||
}
|
||||
modes += EncoderMode(size, entry.name, foundProfile, foundLevel)
|
||||
LogManager.addLog("INFO", "call", "video codec probe passed type=${type.label} encoder=${entry.name} size=$size"); break
|
||||
} catch (e: Exception) { LogManager.addLog("WARN", "call", "video codec probe rejected ${entry.name} size=$size: ${e.message}") }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modes.isNotEmpty()) result[type.id] = Encoder(modes)
|
||||
if (modes.isNotEmpty()) result[type.id] = Encoder(modes)
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun probeDecoders(): Map<Int, Decoder> {
|
||||
val result = mutableMapOf<Int, Decoder>()
|
||||
for (type in listOf(StreamVideoCodec.H265, StreamVideoCodec.H264)) {
|
||||
val modes = mutableListOf<DecoderMode>()
|
||||
for (size in listOf(Size(1280, 720), Size(1920, 1080))) {
|
||||
for (entry in candidates(false)) {
|
||||
if (entry.supportedTypes.none { it == type.mime }) continue
|
||||
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)) {
|
||||
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) },
|
||||
entry.name, true))
|
||||
val modes = mutableListOf<DecoderMode>()
|
||||
for (size in listOf(Size(1280, 720), Size(1920, 1080))) {
|
||||
for (entry in candidates(false)) {
|
||||
if (entry.supportedTypes.none { it == type.mime }) continue
|
||||
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)) {
|
||||
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) },
|
||||
entry.name, true))
|
||||
}
|
||||
profiles = profiles or profile
|
||||
}
|
||||
profiles = profiles or profile
|
||||
}
|
||||
modes += DecoderMode(size, entry.name, profiles, level, 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}") }
|
||||
modes += DecoderMode(size, entry.name, profiles, level, 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}") }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modes.isNotEmpty()) result[type.id] = Decoder(modes)
|
||||
if (modes.isNotEmpty()) result[type.id] = Decoder(modes)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -137,14 +137,19 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
if (stopped) return@post
|
||||
val cameras = CameraLensManager.enumerate(manager).mapNotNull { lens ->
|
||||
val c = manager.getCameraCharacteristics(lens.cameraId)
|
||||
val map = c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
|
||||
val map = c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: run {
|
||||
LogManager.addLog("WARN", "call", "video camera has no stream configuration id=${lens.cameraId}")
|
||||
return@mapNotNull null
|
||||
}
|
||||
val rates = c.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES).orEmpty()
|
||||
val sizes = map?.getOutputSizes(ImageFormat.YUV_420_888).orEmpty().filter {
|
||||
val sizes = map.getOutputSizes(ImageFormat.YUV_420_888).orEmpty().filter {
|
||||
(it == Size(640, 480) || it == Size(1280, 720) || it == Size(1920, 1080)) &&
|
||||
rates.any { range -> range.upper == 30 } &&
|
||||
map!!.getOutputMinFrameDuration(ImageFormat.YUV_420_888, it) <= 33_366_700L
|
||||
map.getOutputMinFrameDuration(ImageFormat.YUV_420_888, it) <= 33_366_700L
|
||||
}
|
||||
val supported = sizes.filter { size -> encoders.values.any { size in it.sizes } }
|
||||
LogManager.addLog(if (supported.isEmpty()) "WARN" else "INFO", "call",
|
||||
"video camera id=${lens.cameraId} 30fpsYuv=$sizes verified=$supported")
|
||||
if (supported.isEmpty()) null else CallVideoCamera(lens.cameraId, lens.label, supported,
|
||||
c.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0,
|
||||
c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
|
||||
|
||||
@@ -117,6 +117,7 @@ class CameraWorker : public QObject {
|
||||
}
|
||||
void stage(const QString& name) { send(packet(Stage, generation, [&](QDataStream& out) { out << name; })); }
|
||||
void enumerate() {
|
||||
if (socket.state() != QLocalSocket::ConnectedState) return; // Первичный опрос повторит connected.
|
||||
stage(QStringLiteral("enumerate"));
|
||||
const auto devices = source->devices();
|
||||
send(packet(Devices, 0, [&](QDataStream& out) {
|
||||
|
||||
Reference in new issue
Block a user