diff options
Diffstat (limited to 'video/out/vo.c')
-rw-r--r-- | video/out/vo.c | 119 |
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) { |