From 1af9bfda80775485ae66440784270ce2004ae1a3 Mon Sep 17 00:00:00 2001 From: ua1zbe Date: Wed, 26 Aug 2026 08:11:04 +0300 Subject: [PATCH] Audio: reduce RX startup clipping --- .../ru/ua1zbe/svxremote/audio/OpusEngine.kt | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/ru/ua1zbe/svxremote/audio/OpusEngine.kt b/app/src/main/java/ru/ua1zbe/svxremote/audio/OpusEngine.kt index 35b46ac..8ebf2ae 100644 --- a/app/src/main/java/ru/ua1zbe/svxremote/audio/OpusEngine.kt +++ b/app/src/main/java/ru/ua1zbe/svxremote/audio/OpusEngine.kt @@ -31,6 +31,7 @@ class OpusEngine( const val CHANNELS = 1 const val FRAME_SIZE = 160 // 20 мс в 8 кГц const val BIT_RATE = 20000 + private const val GATE_MIN_WAIT_MS = 100L // Авто-адаптация битрейта TX (0 = авто включено в настройках) const val AUTO_BITRATE_MIN = 8 const val AUTO_BITRATE_MAX = 24 @@ -384,56 +385,26 @@ class OpusEngine( onRxStats?.invoke(lossPct, jitterMs) } - /** Джиттер-гейт открыт: накопили кадры или вышло 300 мс с первого пакета. */ + /** Джиттер-гейт открыт: накопили кадры или вышел короткий таймаут. */ private fun gateOpen(): Boolean { if (playStarted) return true if (rxQueue.size >= jitterFrames) return true - return gateStart != 0L && System.currentTimeMillis() - gateStart >= 300 + val waitMs = maxOf(GATE_MIN_WAIT_MS, jitterFrames * 20L) + return gateStart != 0L && System.currentTimeMillis() - gateStart >= waitMs } fun onAudioFlush() { try { - // MediaCodec.flush() возвращает входные буферы, но уже выданные индексы - // остаются у нас в freeInputs и дублируются колбэками «повторной выдачи» — - // queueInputBuffer падает (index not owned by client) и кадры теряются. - // Надёжнее пересоздать декодер: колбэки старого отсекаются проверкой - // codec !== decoder. player?.flush() rxQueue.clear() receivedAny = false playStarted = false gateStart = 0L onLevel(SRC_SPEAKER, 0f) - restartDecoder() } catch (_: Exception) { } } - /** Пересоздать только декодер (плеер и джиттер-буфер сохраняем). */ - private fun restartDecoder() { - val dec = decoder - decoder = null - if (dec != null) { - try { - dec.stop() - } catch (_: Exception) { - } - try { - dec.release() - } catch (_: Exception) { - } - } - val format = MediaFormat() - format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_OPUS) - format.setInteger(MediaFormat.KEY_SAMPLE_RATE, OUT_SAMPLE_RATE) - format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS) - if (startDecoderCodec(format)) { - concealLogs = 0 - dbgOut = 0 - ptsCounter = 20_000L - } - } - /** * Один кадр тишины (20 мс) для скрытия потерь вместо паузы в звуке. * Сгенерирован opuslib: 8 кГц, моно, 20 кбит/с, нули PCM. @@ -664,4 +635,4 @@ class OpusEngine( val norm = (db + 60.0) / 48.0 return norm.toFloat().coerceIn(0f, 1f) } -} \ No newline at end of file +}