|
|
|
|
@ -3,15 +3,34 @@ package com.utun.chat.data
|
|
|
|
|
/** Только числа в playback-потоке; форматирование и вывод выполняет отдельный reporter. */ |
|
|
|
|
internal class CallAudioTrace(private val id: Long, private val emit: (String, String) -> Unit) { |
|
|
|
|
companion object { |
|
|
|
|
const val WIDTH = 25 |
|
|
|
|
const val WIDTH = 35 |
|
|
|
|
const val CAPACITY = 512 |
|
|
|
|
// monoUs, routeId, routeType, written, played, pullUs, writeUs, wakeLateUs, zeroWrites, |
|
|
|
|
// monoUs, routeId, routeType, written, played, pullUs, writeCallUs, wakeLateUs, zeroWrites, |
|
|
|
|
// deviceUnderruns, bufferFrames, startFrames, stampFrame, stampNs, stampReadUs, stampOk, |
|
|
|
|
// encodedMs, processingMs, reserveMs, spreadMs, pcmSamples, tempo100, jbUnderruns, waiting, accepted |
|
|
|
|
// headCallUs, progressCallUs, writeSpanUs, jbSnapshotUs, blockUs, |
|
|
|
|
// deviceBeginUs, deviceEndUs, stampQueryUs, routeQueryUs, propertiesQueryUs |
|
|
|
|
private const val PRE_US = 2_000_000L |
|
|
|
|
private const val POST_US = 3_000_000L |
|
|
|
|
} |
|
|
|
|
@Volatile var enabled = false |
|
|
|
|
/** Неизменяемый снимок reporter; playback только читает ссылку, не вызывает Android API. */ |
|
|
|
|
data class DeviceSample( |
|
|
|
|
val routeId: Long = -1, val routeType: Long = -1, val underruns: Long = -1, |
|
|
|
|
val bufferFrames: Long = -1, val startFrames: Long = -1, |
|
|
|
|
val stampFrame: Long = -1, val stampNs: Long = -1, val stampOk: Boolean = false, |
|
|
|
|
val beginUs: Long = -1, val endUs: Long = -1, |
|
|
|
|
val stampQueryUs: Long = 0, val routeQueryUs: Long = 0, val propertiesQueryUs: Long = 0 |
|
|
|
|
) { |
|
|
|
|
fun copyTo(row: LongArray) { |
|
|
|
|
row[1] = routeId; row[2] = routeType; row[9] = underruns |
|
|
|
|
row[10] = bufferFrames; row[11] = startFrames |
|
|
|
|
row[12] = stampFrame; row[13] = stampNs; row[14] = beginUs; row[15] = if (stampOk) 1 else 0 |
|
|
|
|
row[30] = beginUs; row[31] = endUs |
|
|
|
|
row[32] = stampQueryUs; row[33] = routeQueryUs; row[34] = propertiesQueryUs |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@Volatile var device = DeviceSample() |
|
|
|
|
private val ring = Array(CAPACITY) { LongArray(WIDTH) } |
|
|
|
|
private var head = 0 |
|
|
|
|
private var count = 0 |
|
|
|
|
@ -66,13 +85,13 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
|
|
|
|
|
history.addLast(row) |
|
|
|
|
while (history.size > 400 || (eventAt < 0 && history.first()[0] < now - PRE_US)) history.removeFirst() |
|
|
|
|
var events = if (old == null) 1 else 0 |
|
|
|
|
if (old != null && (row[1] != old[1] || row[2] != old[2])) events = events or 2 |
|
|
|
|
if (old != null && old[1] >= 0 && row[1] >= 0 && (row[1] != old[1] || row[2] != old[2])) events = events or 2 |
|
|
|
|
val queueUs = queued(row) |
|
|
|
|
if (queueUs > 100_000) { if (highSince < 0) highSince = now } else highSince = -1 |
|
|
|
|
if (old != null && row[4] == old[4] && queueUs > 0) { |
|
|
|
|
if (stoppedSince < 0) stoppedSince = old[0] |
|
|
|
|
} else stoppedSince = -1 |
|
|
|
|
if (old != null && row[9] > old[9]) underrunAt = now |
|
|
|
|
if (old != null && old[9] >= 0 && row[9] > old[9]) underrunAt = now |
|
|
|
|
val current = (if (highSince >= 0 && now - highSince >= 1_000_000) 4 else 0) or |
|
|
|
|
(if (stoppedSince >= 0 && now - stoppedSince >= 100_000) 8 else 0) or |
|
|
|
|
(if (now - underrunAt < 3_000_000) 16 else 0) |
|
|
|
|
@ -111,11 +130,15 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
|
|
|
|
|
emit("DEBUG", "audio summary call=$id monoUs=${last[0]} spanUs=${last[0] - first[0]} n=${summary.size} " + |
|
|
|
|
"route=${last[1]}/${last[2]} min/avg/max queueUs=${range(::queued)} " + |
|
|
|
|
"jbMs=${range { it[16] + it[17] }} reserveMs=${range { it[18] }} spreadMs=${range { it[19] }} " + |
|
|
|
|
"pullUs=${range { it[5] }} writeUs=${range { it[6] }} wakeLateUs=${range { it[7] }} " + |
|
|
|
|
"pullUs=${range { it[5] }} writeCallUs=${range { it[6] }} headCallUs=${range { it[25] }} " + |
|
|
|
|
"progressCallUs=${range { it[26] }} writeSpanUs=${range { it[27] }} jbSnapshotUs=${range { it[28] }} " + |
|
|
|
|
"blockUs=${range { it[29] }} wakeLateUs=${range { it[7] }} " + |
|
|
|
|
"tempo=${last[21]} deviceUnderruns=${last[9]} deviceUnderDelta=${last[9] - reportedDeviceUnderruns} " + |
|
|
|
|
"jbUnderruns=${last[22]} jbUnderDelta=${last[22] - reportedJbUnderruns} " + |
|
|
|
|
"zeroWrites=${summary.sumOf { it[8] }} missingPcm=${summary.sumOf { (960 - it[20]).coerceAtLeast(0) }} " + |
|
|
|
|
"stamp=${last[12]}@${last[13]} readUs=${last[14]} ok=${last[15]} " + |
|
|
|
|
"deviceAgeUs=${if (last[31] >= 0) last[0] - last[31] else -1} " + |
|
|
|
|
"stampQueryUs=${range { it[32] }} routeQueryUs=${range { it[33] }} propertiesQueryUs=${range { it[34] }} " + |
|
|
|
|
"buffer=${last[10]} start=${last[11]} fault=$faults suppressed=$suppressed") |
|
|
|
|
reportedDeviceUnderruns = last[9]; reportedJbUnderruns = last[22] |
|
|
|
|
summary.clear() |
|
|
|
|
@ -124,9 +147,11 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
|
|
|
|
|
private fun dump(partial: Boolean) { |
|
|
|
|
emit("DEBUG", "audio detail call=$id triggerUs=$eventAt reasons=$eventBits events=$eventCount partial=$partial " + |
|
|
|
|
"bits=start:1,route:2,queue:4,stalled:8,underrun:16 " + |
|
|
|
|
"columns=monoUs,routeId,routeType,written,played,pullUs,writeUs,wakeLateUs,zeroWrites,deviceUnderruns," + |
|
|
|
|
"columns=monoUs,routeId,routeType,written,played,pullUs,writeCallUs,wakeLateUs,zeroWrites,deviceUnderruns," + |
|
|
|
|
"bufferFrames,startFrames,stampFrame,stampNs,stampReadUs,stampOk," + |
|
|
|
|
"encodedMs,processingMs,reserveMs,spreadMs,pcmSamples,tempo100,jbUnderruns,waiting,accepted") |
|
|
|
|
"encodedMs,processingMs,reserveMs,spreadMs,pcmSamples,tempo100,jbUnderruns,waiting,accepted," + |
|
|
|
|
"headCallUs,progressCallUs,writeSpanUs,jbSnapshotUs,blockUs," + |
|
|
|
|
"deviceBeginUs,deviceEndUs,stampQueryUs,routeQueryUs,propertiesQueryUs") |
|
|
|
|
// До пяти секунд, по четыре снимка в строке, с явным временем каждого. |
|
|
|
|
history.filter { it[0] in (eventAt - PRE_US)..(eventAt + POST_US) }.chunked(4).forEach { group -> |
|
|
|
|
emit("DEBUG", "audio detail call=$id rows=" + group.joinToString(";") { it.joinToString(",") }) |
|
|
|
|
|