Browse Source

Keep Android wake deadlines absolute and report causes of the largest video gaps

master
evgeny 4 days ago
parent
commit
717d965fec
  1. 12
      src/call/call_video_playout.c
  2. 1
      src/call/call_video_playout.h
  3. 16
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoPlayer.kt

12
src/call/call_video_playout.c

@ -42,14 +42,17 @@ static void report(struct call_video_playout *p, int64_t now) {
if (now - p->report_us < 1000000) return;
DEBUG_DEBUG(DEBUG_CATEGORY_CALL,
"video playout call=%llu interval_us=%lld shown=%u drop_late=%u drop_schedule=%u drop_order=%u "
"cadence_us=%lld..%lld av_us=%lld..%lld dispatch_max_us=%lld data_shift_us=%lld audio_wait=%u clock_wait=%u",
"cadence_us=%lld..%lld gap_pts_us=%lld gap_rx_us=%lld gap_dispatch_us=%lld "
"av_us=%lld..%lld dispatch_max_us=%lld data_shift_us=%lld audio_wait=%u clock_wait=%u",
(unsigned long long)p->call_id, (long long)(now - p->report_us), p->shown, p->late, p->skipped, p->order,
(long long)(p->interval_min == INT64_MAX ? 0 : p->interval_min), (long long)p->interval_max,
(long long)p->gap_pts, (long long)p->gap_received, (long long)p->gap_dispatch,
(long long)(p->shown ? p->av_min : 0), (long long)(p->shown ? p->av_max : 0),
(long long)p->dispatch_max, (long long)p->data_shift, p->audio_wait, p->clock_wait);
p->report_us = now; p->shown = p->late = p->skipped = p->order = p->audio_wait = p->clock_wait = 0;
p->interval_min = p->av_min = INT64_MAX; p->av_max = INT64_MIN;
p->interval_max = p->dispatch_max = p->data_shift = 0;
p->gap_pts = p->gap_received = p->gap_dispatch = 0;
}
enum call_video_playout_action call_video_playout_poll(struct call_video_playout *p,
@ -94,9 +97,12 @@ enum call_video_playout_action call_video_playout_poll(struct call_video_playout
if (p->last_show_us) {
int64_t gap = now - p->last_show_us;
if (gap < p->interval_min) p->interval_min = gap;
if (gap > p->interval_max) p->interval_max = gap;
if (gap > p->interval_max) {
p->interval_max = gap; p->gap_pts = f->pts_us - p->last_show_pts;
p->gap_received = f->received_us - p->last_received; p->gap_dispatch = now - deadline;
}
}
p->last_show_us = now; p->shown++;
p->last_show_us = now; p->last_show_pts = f->pts_us; p->last_received = f->received_us; p->shown++;
}
return action;
}

1
src/call/call_video_playout.h

@ -21,6 +21,7 @@ struct call_video_playout {
int anchored, audio_valid;
int64_t last_pts, last_deadline, deadline_us;
int64_t report_us, last_show_us, interval_min, interval_max, av_min, av_max, dispatch_max, data_shift;
int64_t last_show_pts, last_received, gap_pts, gap_received, gap_dispatch;
unsigned shown, late, skipped, order, audio_wait, clock_wait;
};

16
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoPlayer.kt

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

Loading…
Cancel
Save