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.c119
1 files changed, 102 insertions, 17 deletions
diff --git a/video/out/vo.c b/video/out/vo.c
index e0c8d3ebed..72f98e8859 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -130,9 +130,16 @@ struct vo_internal {
bool hasframe;
bool request_redraw;
+ bool paused;
int64_t flip_queue_offset; // queue flip events at most this much in advance
+ int64_t last_flip;
+ int64_t vsync_interval;
+ int64_t drop_count;
+ bool dropped_frame; // the previous frame was dropped
+ struct mp_image *dropped_image; // used to possibly redraw the dropped frame
+
int64_t wakeup_pts; // time at which to pull frame from decoder
bool rendering; // true if an image is being rendered
@@ -323,6 +330,11 @@ static void run_reconfig(void *p)
vo->params = NULL;
}
forget_frames(vo); // implicitly synchronized
+
+ double display_fps = 1000.0; // assume infinite if unset
+ vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &display_fps);
+ vo->in->vsync_interval = 1e6 / display_fps;
+ MP_VERBOSE(vo, "Assuming %f FPS for framedrop.\n", display_fps);
}
int vo_reconfig(struct vo *vo, struct mp_image_params *params, int flags)
@@ -358,7 +370,9 @@ static void forget_frames(struct vo *vo)
{
struct vo_internal *in = vo->in;
in->hasframe = false;
+ in->drop_count = 0;
mp_image_unrefp(&in->frame_queued);
+ mp_image_unrefp(&in->dropped_image);
}
#ifndef __MINGW32__
@@ -496,6 +510,17 @@ void vo_wait_frame(struct vo *vo)
pthread_mutex_unlock(&in->lock);
}
+static int64_t prev_sync(struct vo *vo, int64_t ts)
+{
+ struct vo_internal *in = vo->in;
+
+ int64_t diff = (int64_t)(ts - in->last_flip);
+ int64_t offset = diff % MPMAX(in->vsync_interval, 1);
+ if (offset < 0)
+ offset += in->vsync_interval;
+ return ts - offset;
+}
+
static bool render_frame(struct vo *vo)
{
struct vo_internal *in = vo->in;
@@ -510,33 +535,59 @@ static bool render_frame(struct vo *vo)
return false;
}
+ mp_image_unrefp(&in->dropped_image);
+
in->rendering = true;
in->frame_queued = NULL;
- pthread_mutex_unlock(&in->lock);
+ // The next time a flip (probably) happens.
+ int64_t next_vsync = prev_sync(vo, mp_time_us()) + in->vsync_interval;
+ int64_t end_time = pts + duration;
+
+ in->dropped_frame = end_time < next_vsync;
+ in->dropped_frame &= !!(vo->global->opts->frame_dropping & 1);
+ in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP) &&
+ !vo->driver->untimed && !vo->driver->encode;
+ // Even if we're hopelessly behind, rather degrade to 10 FPS playback,
+ // instead of just freezing the display forever.
+ in->dropped_frame &= mp_time_us() - in->last_flip < 100 * 1000;
+
+ if (in->dropped_frame) {
+ in->drop_count += 1;
+ in->dropped_image = img;
+ } else {
+ pthread_mutex_unlock(&in->lock);
- vo->driver->draw_image(vo, img);
+ vo->driver->draw_image(vo, img);
- int64_t target = pts - in->flip_queue_offset;
- while (1) {
- int64_t now = mp_time_us();
- if (target <= now)
- break;
- mp_sleep_us(target - now);
- }
+ int64_t target = pts - in->flip_queue_offset;
+ while (1) {
+ int64_t now = mp_time_us();
+ if (target <= now)
+ break;
+ mp_sleep_us(target - now);
+ }
- if (vo->driver->flip_page_timed)
- vo->driver->flip_page_timed(vo, pts, duration);
- else
- vo->driver->flip_page(vo);
+ if (vo->driver->flip_page_timed)
+ vo->driver->flip_page_timed(vo, pts, duration);
+ else
+ vo->driver->flip_page(vo);
+
+ in->last_flip = mp_time_us();
+
+ MP_DBG(vo, "phase: %ld\n", (long)(in->last_flip % in->vsync_interval));
+
+ pthread_mutex_lock(&in->lock);
+ }
vo->want_redraw = false;
- pthread_mutex_lock(&in->lock);
in->request_redraw = false;
in->rendering = false;
+
pthread_cond_signal(&in->wakeup); // for vo_wait_frame()
mp_input_wakeup(vo->input_ctx);
+
pthread_mutex_unlock(&in->lock);
return true;
@@ -546,15 +597,28 @@ static void do_redraw(struct vo *vo)
{
struct vo_internal *in = vo->in;
+ vo->want_redraw = false;
+
pthread_mutex_lock(&in->lock);
in->request_redraw = false;
+ bool skip = !in->paused && in->dropped_frame;
+ struct mp_image *img = in->dropped_image;
+ if (!skip) {
+ in->dropped_image = NULL;
+ in->dropped_frame = false;
+ }
pthread_mutex_unlock(&in->lock);
- vo->want_redraw = false;
-
- if (!vo->config_ok || vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1)
+ if (!vo->config_ok || skip)
return;
+ if (img) {
+ vo->driver->draw_image(vo, img);
+ } else {
+ if (vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1)
+ return;
+ }
+
if (vo->driver->flip_page_timed)
vo->driver->flip_page_timed(vo, 0, -1);
else
@@ -601,6 +665,27 @@ static void *vo_thread(void *ptr)
return NULL;
}
+void vo_set_paused(struct vo *vo, bool paused)
+{
+ struct vo_internal *in = vo->in;
+ pthread_mutex_lock(&in->lock);
+ if (in->paused != paused) {
+ in->paused = true;
+ if (in->paused && in->dropped_frame)
+ in->request_redraw = true;
+ }
+ pthread_mutex_unlock(&in->lock);
+ vo_control(vo, paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
+}
+
+int64_t vo_get_drop_count(struct vo *vo)
+{
+ pthread_mutex_lock(&vo->in->lock);
+ int64_t r = vo->in->drop_count;
+ pthread_mutex_unlock(&vo->in->lock);
+ return r;
+}
+
// Make the VO redraw the OSD at some point in the future.
void vo_redraw(struct vo *vo)
{