diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCameraModes.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCameraModes.kt index 0b2284ca..4c6ffc4a 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCameraModes.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCameraModes.kt @@ -5,10 +5,9 @@ import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager import android.media.MediaCodecInfo import android.media.MediaCodecList -import android.media.MediaFormat import android.os.Build -/** Режим является пересечением возможностей камеры и конкретного Surface AVC encoder. */ +/** Режим является пересечением возможностей камеры и выбранного Surface encoder. */ data class StreamCaptureMode(val camera: String, val width: Int, val height: Int, val fps: Int, val minimumFps: Int, val highSpeed: Boolean, val codec: String, val bitrate: Int, val videoCodec: StreamVideoCodec = StreamVideoCodec.H264, @@ -34,23 +33,24 @@ object StreamCameraModes { val modes = mutableListOf() fun add(width: Int, height: Int, fps: Int, lower: Int, highSpeed: Boolean) { for (type in StreamVideoCodec.values()) { - for (codec in codecs) { - try { - if (codec.supportedTypes.none { it.equals(type.mime, true) }) continue - if (highSpeed && Build.VERSION.SDK_INT >= 29 && !codec.isHardwareAccelerated) continue - val caps = codec.getCapabilitiesForType(type.mime) - val video = caps.videoCapabilities - if (!caps.colorFormats.contains(MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) || - (type.profile != null && caps.profileLevels.none { it.profile == type.profile }) || - !video.areSizeAndRateSupported(width, height, fps.toDouble())) continue - val bitrate = (width.toLong() * height * fps / 14).coerceIn(512_000L, 16_000_000L).toInt() - modes.add(StreamCaptureMode(lens.cameraId, width, height, fps, lower, highSpeed, codec.name, - video.bitrateRange.clamp(bitrate), type, video.bitrateRange.lower, video.bitrateRange.upper)) - LogManager.addLog("INFO", "video", "camera mode camera=${lens.cameraId} ${width}x$height fps=$lower..$fps " + - "highSpeed=$highSpeed codec=${codec.name} bitrate=${modes.last().bitrate} achievable=${video.getAchievableFrameRatesFor(width, height)}") - break - } catch (e: Exception) { LogManager.addLog("WARN", "video", "codec probe ${codec.name}: ${e.message}") } - } + for (codec in codecs) { + try { + if (codec.supportedTypes.none { it.equals(type.mime, true) }) continue + if (highSpeed && Build.VERSION.SDK_INT >= 29 && !codec.isHardwareAccelerated) continue + val caps = codec.getCapabilitiesForType(type.mime) + val video = caps.videoCapabilities + if (!caps.colorFormats.contains(MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) || + (type.profile != null && caps.profileLevels.none { it.profile == type.profile }) || + !video.areSizeAndRateSupported(width, height, fps.toDouble())) continue + val bitrate = (width.toLong() * height * fps / 14).coerceIn(512_000L, 16_000_000L).toInt() + modes.add(StreamCaptureMode(lens.cameraId, width, height, fps, lower, highSpeed, codec.name, + video.bitrateRange.clamp(bitrate), type, video.bitrateRange.lower, video.bitrateRange.upper)) + LogManager.addLog("INFO", "video", "camera mode camera=${lens.cameraId} ${width}x$height fps=$lower..$fps " + + "highSpeed=$highSpeed type=${type.label} encoder=${codec.name} bitrate=${modes.last().bitrate} " + + "range=${video.bitrateRange} achievable=${video.getAchievableFrameRatesFor(width, height)}") + break + } catch (e: Exception) { LogManager.addLog("WARN", "video", "codec probe ${codec.name}: ${e.message}") } + } } } val rates = c.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES).orEmpty() diff --git a/tools/chatgui-android/libutun_lite/stream_bridge.h b/tools/chatgui-android/libutun_lite/stream_bridge.h index cb4b09f6..395480d5 100644 --- a/tools/chatgui-android/libutun_lite/stream_bridge.h +++ b/tools/chatgui-android/libutun_lite/stream_bridge.h @@ -1,8 +1,8 @@ /* Android-очереди live media: attach/detach только в uasync, вне callbacks. * UI/кодировщики используют только session_id, никогда не сохраняют inst/ua. * Один publisher и один viewer, TX <=2MB/128 records, RX <=4MB/256 records. - * Data принимает полные H.264 Annex B кадры (KEY содержит SPS/PPS/IDR) и Opus 48k mono/20ms. - * pull передаёт владение u_malloc-буфером: kind:1,flags:1,width:2,height:2,rotation:2,PTS_us:8,length:4,revision:4,data. + * Data принимает полные access units пяти кодеков uAV2 (KEY: config_length:4/config/frame) и Opus 48k mono/stereo/20ms. + * pull передаёт u_malloc-буфер: kind:1,flags:1,width:2,height:2,rotation:2,PTS_us:8,length:4,revision:4,codec:1,reserved:3,data. * kinds: video=1,audio=2,gap=3,opened=4,ended=5,error=6; error.flags = stream_close_reason. Числа big-endian. * Медиаданные передаются через pull, не через общий чатовый event bus. */ #ifndef ANDROID_STREAM_BRIDGE_H