summaryrefslogtreecommitdiffstats
path: root/video/out/vo.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vo.c')
-rw-r--r--video/out/vo.c88
1 files changed, 78 insertions, 10 deletions
diff --git a/video/out/vo.c b/video/out/vo.c
index 9960004e67..de79fb018e 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -139,6 +139,7 @@ struct vo_internal {
int64_t flip_queue_offset; // queue flip events at most this much in advance
+ int64_t missed_count;
int64_t drop_count;
bool dropped_frame; // the previous frame was dropped
@@ -418,9 +419,14 @@ static void forget_frames(struct vo *vo)
in->hasframe = false;
in->hasframe_rendered = false;
in->drop_count = 0;
+ in->missed_count = 0;
talloc_free(in->frame_queued);
in->frame_queued = NULL;
// don't unref current_frame; we always want to be able to redraw it
+ if (in->current_frame) {
+ in->current_frame->num_vsyncs = 0; // but reset future repeats
+ in->current_frame->display_synced = false; // mark discontinuity
+ }
}
#ifndef __MINGW32__
@@ -508,12 +514,15 @@ void vo_wakeup(struct vo *vo)
// next_pts is the exact time when the next frame should be displayed. If the
// VO is ready, but the time is too "early", return false, and call the wakeup
// callback once the time is right.
+// If next_pts is negative, disable any timing and draw the frame as fast as
+// possible.
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
{
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
- bool r = vo->config_ok && !in->frame_queued;
- if (r) {
+ bool r = vo->config_ok && !in->frame_queued &&
+ (!in->current_frame || in->current_frame->num_vsyncs < 1);
+ if (r && next_pts >= 0) {
// Don't show the frame too early - it would basically freeze the
// display by disallowing OSD redrawing or VO interaction.
// Actually render the frame at earliest 50ms before target time.
@@ -538,10 +547,12 @@ void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
{
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
- assert(vo->config_ok && !in->frame_queued);
+ assert(vo->config_ok && !in->frame_queued &&
+ (!in->current_frame || in->current_frame->num_vsyncs < 1));
in->hasframe = true;
in->frame_queued = frame;
- in->wakeup_pts = in->vsync_timed ? 0 : frame->pts + MPMAX(frame->duration, 0);
+ in->wakeup_pts = (frame->display_synced || in->vsync_timed)
+ ? 0 : frame->pts + MPMAX(frame->duration, 0);
wakeup_locked(vo);
pthread_mutex_unlock(&in->lock);
}
@@ -598,19 +609,29 @@ static bool render_frame(struct vo *vo)
vo->in->vsync_interval = in->display_fps > 0 ? 1e6 / in->display_fps : 0;
vo->in->vsync_interval = MPMAX(vo->in->vsync_interval, 1);
+ bool continuous = in->current_frame && in->current_frame->display_synced;
+
if (in->frame_queued) {
talloc_free(in->current_frame);
in->current_frame = in->frame_queued;
in->frame_queued = NULL;
} else if (in->paused || !in->current_frame || !in->hasframe ||
- !in->vsync_timed)
+ (!in->vsync_timed && !in->current_frame->display_synced))
{
goto done;
}
+ if (in->current_frame->display_synced && in->current_frame->num_vsyncs < 1)
+ goto done;
+
frame = vo_frame_ref(in->current_frame);
assert(frame);
+ if (frame->display_synced) {
+ frame->pts = 0;
+ frame->duration = -1;
+ }
+
int64_t pts = frame->pts;
int64_t duration = frame->duration;
int64_t end_time = pts + duration;
@@ -621,11 +642,10 @@ static bool render_frame(struct vo *vo)
frame->next_vsync = next_vsync;
frame->prev_vsync = prev_vsync;
-
- frame->vsync_offset = next_vsync - pts;
+ frame->num_vsyncs = 1;
// Time at which we should flip_page on the VO.
- int64_t target = pts - in->flip_queue_offset;
+ int64_t target = frame->display_synced ? 0 : pts - in->flip_queue_offset;
bool prev_dropped_frame = in->dropped_frame;
@@ -650,6 +670,7 @@ static bool render_frame(struct vo *vo)
in->dropped_frame |= end_time < prev_vsync;
}
+ in->dropped_frame &= !frame->display_synced;
in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP);
in->dropped_frame &= (vo->global->opts->frame_dropping & 1);
// Even if we're hopelessly behind, rather degrade to 10 FPS playback,
@@ -657,7 +678,7 @@ static bool render_frame(struct vo *vo)
in->dropped_frame &= mp_time_us() - in->last_flip < 100 * 1000;
in->dropped_frame &= in->hasframe_rendered;
- if (in->vsync_timed) {
+ if (in->vsync_timed && !frame->display_synced) {
// this is a heuristic that wakes the thread up some
// time before the next vsync
target = next_vsync - MPMIN(in->vsync_interval / 2, 8e3);
@@ -672,11 +693,17 @@ static bool render_frame(struct vo *vo)
in->dropped_frame = false;
goto done;
}
+
+ frame->vsync_offset = next_vsync - pts;
}
// Setup parameters for the next time this frame is drawn. ("frame" is the
// frame currently drawn, while in->current_frame is the potentially next.)
in->current_frame->repeat = true;
+ if (frame->display_synced)
+ in->current_frame->vsync_offset += in->vsync_interval;
+ if (in->current_frame->num_vsyncs > 0)
+ in->current_frame->num_vsyncs -= 1;
if (in->dropped_frame) {
in->drop_count += 1;
@@ -711,10 +738,15 @@ static bool render_frame(struct vo *vo)
in->vsync_interval_approx = in->last_flip - prev_flip;
MP_STATS(vo, "end video");
+ MP_STATS(vo, "video_end");
pthread_mutex_lock(&in->lock);
in->dropped_frame = prev_drop_count < vo->in->drop_count;
in->rendering = false;
+
+ if (in->current_frame && in->current_frame->display_synced &&
+ continuous && in->vsync_interval_approx > in->vsync_interval * 3 / 2)
+ in->missed_count += 1;
}
if (!in->dropped_frame) {
@@ -896,8 +928,11 @@ bool vo_still_displaying(struct vo *vo)
pthread_mutex_lock(&vo->in->lock);
int64_t now = mp_time_us();
int64_t frame_end = 0;
- if (in->current_frame)
+ if (in->current_frame) {
frame_end = in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
+ if (in->current_frame->num_vsyncs > 0)
+ frame_end = INT64_MAX;
+ }
bool working = now < frame_end || in->rendering || in->frame_queued;
pthread_mutex_unlock(&vo->in->lock);
return working && in->hasframe;
@@ -991,6 +1026,39 @@ int64_t vo_get_vsync_interval(struct vo *vo)
return res;
}
+// Get the mp_time_us() time at which the currently rendering frame will end
+// (i.e. time of the last flip call needed to display it).
+// This can only be called while no new frame is queued (after
+// vo_is_ready_for_frame). Returns 0 for non-display synced frames, or if the
+// deadline for continuous display was missed.
+int64_t vo_get_next_frame_start_time(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ pthread_mutex_lock(&in->lock);
+ assert (!in->frame_queued);
+ int64_t res = 0;
+ if (in->last_flip && in->vsync_interval > 1 && in->current_frame) {
+ res = in->last_flip;
+ int extra = !!in->rendering;
+ res += (in->current_frame->num_vsyncs + extra) * in->vsync_interval;
+ if (!in->current_frame->display_synced)
+ res = 0;
+ if (in->current_frame->num_vsyncs < 1 && !in->rendering)
+ res = 0;
+ }
+ pthread_mutex_unlock(&in->lock);
+ return res;
+}
+
+int64_t vo_get_missed_count(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ pthread_mutex_lock(&in->lock);
+ int64_t res = vo->in->missed_count;
+ pthread_mutex_unlock(&in->lock);
+ return res;
+}
+
double vo_get_display_fps(struct vo *vo)
{
struct vo_internal *in = vo->in;