|
|
|
|
@ -94,14 +94,23 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
|
|
|
|
resetAudio(); resetVideo(); videoBase = -1; prefillUntil = 0 |
|
|
|
|
connectDeadline = System.nanoTime() + 10_000_000_000 |
|
|
|
|
} |
|
|
|
|
// Отброшенный звук всё равно прогревает Opus; остающиеся пакеты будут декодированы один раз при воспроизведении. |
|
|
|
|
fun warmAudio(frame: Frame) { |
|
|
|
|
if (warmAudioPts >= 0 && frame.pts != warmAudioPts) { |
|
|
|
|
LogManager.addLog("WARN", "stream", "preroll audio discontinuity id=$id expected=$warmAudioPts pts=${frame.pts}") |
|
|
|
|
// Прогрев допускает джиттер PTS и короткий PLC; большой разрыв начинает новое состояние Opus. |
|
|
|
|
fun prepareAudio(pts: Long) { |
|
|
|
|
if (warmAudioPts >= 0 && (pts < warmAudioPts - 10000 || pts > warmAudioPts + 100000)) { |
|
|
|
|
LogManager.addLog("WARN", "stream", "preroll audio discontinuity id=$id expected=$warmAudioPts pts=$pts") |
|
|
|
|
if (opus != 0L) NativeLib.streamOpusClose(opus) |
|
|
|
|
opus = 0 |
|
|
|
|
opus = 0; warmAudioPts = -1 |
|
|
|
|
} |
|
|
|
|
if (opus == 0L) { opus = NativeLib.streamOpusCreate(false); check(opus != 0L) } |
|
|
|
|
val missing = if (warmAudioPts < 0) 0 else ((pts - warmAudioPts + 10000) / 20000).toInt() |
|
|
|
|
repeat(missing) { |
|
|
|
|
check(NativeLib.streamOpusDecode(opus, byteArrayOf()) != null) { "Preroll Opus PLC failed" } |
|
|
|
|
prerollFrames += 960 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Отброшенный звук всё равно прогревает Opus; остающиеся пакеты декодируются один раз при воспроизведении. |
|
|
|
|
fun warmAudio(frame: Frame) { |
|
|
|
|
prepareAudio(frame.pts) |
|
|
|
|
check(NativeLib.streamOpusDecode(opus, frame.data) != null) { "Preroll Opus decode failed" } |
|
|
|
|
warmAudioPts = frame.pts + 20000; prerollFrames += 960 |
|
|
|
|
} |
|
|
|
|
@ -237,12 +246,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
|
|
|
|
val now = System.nanoTime() |
|
|
|
|
if (prefillUntil != 0L && now >= prefillUntil) { |
|
|
|
|
if (audios.isNotEmpty() && track == null) { |
|
|
|
|
if (warmAudioPts >= 0 && audios.first.pts != warmAudioPts) { |
|
|
|
|
LogManager.addLog("WARN", "stream", "audio startup discontinuity id=$id expected=$warmAudioPts pts=${audios.first.pts}") |
|
|
|
|
if (opus != 0L) NativeLib.streamOpusClose(opus) |
|
|
|
|
opus = 0 |
|
|
|
|
} |
|
|
|
|
if (opus == 0L) { opus = NativeLib.streamOpusCreate(false); check(opus != 0L) } |
|
|
|
|
prepareAudio(audios.first.pts) |
|
|
|
|
track = AudioTrack.Builder().setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA) |
|
|
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build()).setAudioFormat(AudioFormat.Builder() |
|
|
|
|
.setSampleRate(48000).setChannelMask(AudioFormat.CHANNEL_OUT_STEREO).setEncoding(AudioFormat.ENCODING_PCM_16BIT).build()) |
|
|
|
|
|