|
|
|
|
@ -12,6 +12,7 @@ internal class StreamAudioOutput(
|
|
|
|
|
private val log: (String) -> Unit = {} |
|
|
|
|
) { |
|
|
|
|
internal interface Sink { |
|
|
|
|
val bufferFrames: Int |
|
|
|
|
val threshold: Int |
|
|
|
|
val head: Long |
|
|
|
|
val description: String |
|
|
|
|
@ -36,6 +37,7 @@ internal class StreamAudioOutput(
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 31) track.setStartThresholdInFrames(1) |
|
|
|
|
} catch (e: Exception) { track.release(); throw e } |
|
|
|
|
} |
|
|
|
|
override val bufferFrames: Int get() = track.bufferSizeInFrames |
|
|
|
|
override val threshold: Int get() = if (Build.VERSION.SDK_INT >= 31) |
|
|
|
|
minOf(track.startThresholdInFrames, track.bufferSizeInFrames) else track.bufferSizeInFrames |
|
|
|
|
override val head: Long get() = track.playbackHeadPosition.toLong() and 0xffffffffL |
|
|
|
|
@ -79,12 +81,13 @@ internal class StreamAudioOutput(
|
|
|
|
|
} |
|
|
|
|
previousHead = raw |
|
|
|
|
val head = headEpoch + raw |
|
|
|
|
val first = deviceHead == 0L && head > 0 |
|
|
|
|
if (head > deviceHead) { |
|
|
|
|
if (deviceHead == 0L) log("audio started delayMs=${(now - openedAt) / 1_000_000} $description") |
|
|
|
|
stalledSince = -1 |
|
|
|
|
} |
|
|
|
|
deviceHead = head |
|
|
|
|
played = head.coerceAtMost(written) |
|
|
|
|
if (first) log("audio started delayMs=${(now - openedAt) / 1_000_000} $description") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Ровно одна неблокирующая запись за итерацию; ожидание устройства не задерживает video pump. */ |
|
|
|
|
@ -96,7 +99,8 @@ internal class StreamAudioOutput(
|
|
|
|
|
log("audio ${if (hold) "paused" else "resumed"} $description") |
|
|
|
|
} |
|
|
|
|
if (hold) return |
|
|
|
|
if (pending == null && written - played < maxOf(960, sink?.threshold ?: 1)) { |
|
|
|
|
// Размер устройства уже учитывает его период обслуживания; 20ms могут быть меньше одного периода. |
|
|
|
|
if (pending == null && written - played < (sink?.bufferFrames ?: 1)) { |
|
|
|
|
val pcm = pull() |
|
|
|
|
if (pcm != null) { |
|
|
|
|
check(pcm.isNotEmpty() && pcm.size <= 960 && pcm.size % 2 == 0) { "Invalid stream PCM size=${pcm.size}" } |
|
|
|
|
|