Audio: reduce RX startup clipping

This commit is contained in:
ua1zbe
2026-08-26 08:11:04 +03:00
parent f6c37d4fc0
commit 1af9bfda80

View File

@@ -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)
}
}
}