|
|
|
|
@ -45,12 +45,13 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
|
|
|
|
val renderHandler = Handler(Looper.getMainLooper()) |
|
|
|
|
fun close() { |
|
|
|
|
try { decoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder stop: ${e.message}") } |
|
|
|
|
decoder?.release(); decoder = null; format = null; pictures.clear(); inputs.clear() |
|
|
|
|
try { decoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder release: ${e.message}") } |
|
|
|
|
decoder = null; format = null; pictures.clear(); inputs.clear() |
|
|
|
|
draining = false; endSeen = false; lastOutput = -1 |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
while (running) { |
|
|
|
|
var worked = false; var waitUs = 5000L |
|
|
|
|
var worked = false; var wakeUs = System.nanoTime() / 1000 + 5000 |
|
|
|
|
try { |
|
|
|
|
while (true) { |
|
|
|
|
val next = outputs.poll() ?: break |
|
|
|
|
@ -89,7 +90,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
|
|
|
|
val first = pictures.peekFirst(); val nextPicture = if (pictures.size > 1) pictures.peekLast() else null |
|
|
|
|
val decision = CallVideoNative.playoutStep(playout, first?.timing?.pts ?: -1, first?.timing?.received ?: 0, |
|
|
|
|
nextPicture?.timing?.pts ?: -1, nextPicture?.timing?.received ?: 0, now / 1000, valid, clock[4]) |
|
|
|
|
if (decision > 0) waitUs = decision |
|
|
|
|
if (decision > 0) wakeUs = now / 1000 + decision |
|
|
|
|
else if (first != null) { |
|
|
|
|
val render = decision == 0L |
|
|
|
|
if (first.timing.pts % 1_000_000 < 34_000) LogManager.addLog("DEBUG", "debug", |
|
|
|
|
@ -103,7 +104,11 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
|
|
|
|
} else late++ |
|
|
|
|
lastProgress = now |
|
|
|
|
} |
|
|
|
|
if (endSeen && pictures.isEmpty()) close() |
|
|
|
|
if (endSeen && pictures.isEmpty()) { |
|
|
|
|
check(inputs.isEmpty()) { "Video EOS before all output frames" } |
|
|
|
|
LogManager.addLog("DEBUG", "call", "video decoder drained call=$id generation=${format?.generation}") |
|
|
|
|
close() |
|
|
|
|
} |
|
|
|
|
// Ожидание срока output не останавливает подачу input; очередь остаётся ограниченной. |
|
|
|
|
if (pending == null && inputs.size + pictures.size < 8) { |
|
|
|
|
pending = CallVideoNative.pull(id, meta); if (pending != null) pulled++ |
|
|
|
|
@ -172,7 +177,8 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
|
|
|
|
close(); CallVideoNative.playoutReset(playout) |
|
|
|
|
pending = null; displayed.value = null; failure(e.message ?: "Video decoder failed") |
|
|
|
|
} |
|
|
|
|
if (!worked) Thread.sleep(waitUs / 1000, (waitUs % 1000 * 1000).toInt()) |
|
|
|
|
val waitUs = wakeUs - System.nanoTime() / 1000 |
|
|
|
|
if (!worked && waitUs > 0) Thread.sleep(waitUs / 1000, (waitUs % 1000 * 1000).toInt()) |
|
|
|
|
} |
|
|
|
|
} finally { |
|
|
|
|
close(); CallVideoNative.playoutDestroy(playout); displayed.value = null; output?.release?.invoke() |
|
|
|
|
|