Smooth video presentation phase across render latency changes

This commit is contained in:
evgeny committed 2026-10-19 21:15:18 +02:00
1 parent 6f542bfae9
commit d0232e4c6e
3 files changed
+37 -3

No files matched your search

+14 -2
View File
@@ -122,7 +122,11 @@ enum call_video_playout_action call_video_playout_poll(struct call_video_playout
int64_t step = (int64_t)f->interval_us * 1000000 / p->speed_ppm;
if (p->head_pts != f->pts_us) {
p->head_pts = f->pts_us;
p->head_due_us = p->anchored ? p->last_show_us + step - p->lead_us : now;
p->head_show_us = p->anchored ? p->last_deadline + step : now;
// После опоздания сохраняем все кадры, но не устраиваем мгновенный burst.
int64_t earliest = p->last_show_us + (int64_t)f->interval_us * 1000000 / VIDEO_MAX_SPEED_PPM;
if (p->anchored && p->head_show_us < earliest) p->head_show_us = earliest;
p->head_due_us = p->head_show_us - (p->anchored ? p->lead_us : 0);
}
int64_t deadline = p->head_due_us;
p->deadline_us = deadline;
@@ -151,7 +155,15 @@ void call_video_playout_presented(struct call_video_playout *p, int64_t pts, int
int64_t av = p->pending_av, lead = now - p->selected_us;
// GUI freeze не становится постоянным опережением dispatch для последующих кадров.
if (lead <= f->interval_us / 2) p->lead_us = (p->lead_us * 7 + lead) / 8;
p->awaiting = 0; p->anchored = 1; p->last_deadline = now; p->head_pts = -1;
int64_t error = now - p->head_show_us;
if (!p->anchored || error > f->interval_us / 2 || error < -(int64_t)f->interval_us / 2) p->last_deadline = now;
else {
int64_t correction = error / 8;
if (correction > 1000) correction = 1000;
if (correction < -1000) correction = -1000;
p->last_deadline = p->head_show_us + correction;
}
p->awaiting = 0; p->anchored = 1; p->head_pts = -1;
{
if (av < p->av_min) p->av_min = av;
if (av > p->av_max) p->av_max = av;
+1
View File
@@ -34,6 +34,7 @@ struct call_video_playout {
struct call_video_playout_frame pending; // Единственный выбранный кадр до подтверждения потребителем.
int awaiting;
int64_t selected_us, pending_av, head_pts, head_due_us, lead_us, rate_us;
int64_t head_show_us; // Плановая презентация головы; GUI ACK корректирует фазу плавно.
unsigned queued, reserve; // reserve — будущие кадры сверх головы, включая необходимый pipeline backend.
// NULL пишет DEBUG напрямую; timer устанавливает неблокирующий callback с копированием события.
void (*log_event)(void *, const struct call_video_playout_event *);
+22 -1
View File
@@ -108,6 +108,27 @@ static void slowing_with_queue(void) {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS queue governor: speed_ppm=%u -> %u as total frames fall 8 -> 2", fast, p.speed_ppm);
}
static void render_latency_change(void) {
struct call_video_playout p; call_video_playout_init(&p, 13);
struct call_video_playout_frame f = {origin, origin, 33333};
struct call_clock_snapshot audio = {.valid = 1, .rate_ppm = 1000000};
int64_t now = origin;
for (unsigned i = 0; i < 240; ++i) {
f.pts_us = origin + i * 33333ll; audio.pts_us = f.pts_us + 100000;
int action = call_video_playout_poll(&p, &f, now, &audio, 2, 1);
if (action == CALL_VIDEO_PLAYOUT_WAIT) {
now = p.deadline_us;
CHECK(call_video_playout_poll(&p, &f, now, &audio, 2, 1) == CALL_VIDEO_PLAYOUT_SHOW);
} else CHECK(action == CALL_VIDEO_PLAYOUT_SHOW);
now += i < 120 ? 3000 : 9000;
call_video_playout_presented(&p, f.pts_us, now);
++now;
}
int64_t phase = p.last_show_us - (origin + 239 * 33333ll);
CHECK(phase >= 0 && phase < 15000);
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS render latency 3->9ms: phase_us=%lld; no accumulated per-frame GUI delay", (long long)phase);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
const unsigned pauses[] = {50, 100, 200, 500};
@@ -116,5 +137,5 @@ int main(void) {
simulate(pauses[i], cpu, 1000, 0);
simulate(pauses[i], cpu, 15625, 1);
}
presentation_deadline(); queued_capture_gap(); slowing_with_queue(); return 0;
presentation_deadline(); queued_capture_gap(); slowing_with_queue(); render_latency_change(); return 0;
}