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.c462
1 files changed, 258 insertions, 204 deletions
diff --git a/video/out/vo.c b/video/out/vo.c
index 27be4735ab..db29690950 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -15,18 +15,17 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <assert.h>
+#include <math.h>
+#include <stdatomic.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
-#include <stdbool.h>
-#include <pthread.h>
-#include <math.h>
#include "mpv_talloc.h"
#include "config.h"
-#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "osdep/threads.h"
#include "misc/dispatch.h"
@@ -52,6 +51,7 @@ extern const struct vo_driver video_out_x11;
extern const struct vo_driver video_out_vdpau;
extern const struct vo_driver video_out_xv;
extern const struct vo_driver video_out_gpu;
+extern const struct vo_driver video_out_gpu_next;
extern const struct vo_driver video_out_libmpv;
extern const struct vo_driver video_out_null;
extern const struct vo_driver video_out_image;
@@ -61,17 +61,19 @@ extern const struct vo_driver video_out_drm;
extern const struct vo_driver video_out_direct3d;
extern const struct vo_driver video_out_sdl;
extern const struct vo_driver video_out_vaapi;
+extern const struct vo_driver video_out_dmabuf_wayland;
extern const struct vo_driver video_out_wlshm;
-extern const struct vo_driver video_out_rpi;
extern const struct vo_driver video_out_tct;
+extern const struct vo_driver video_out_sixel;
+extern const struct vo_driver video_out_kitty;
-const struct vo_driver *const video_out_drivers[] =
+static const struct vo_driver *const video_out_drivers[] =
{
- &video_out_libmpv,
#if HAVE_ANDROID
&video_out_mediacodec_embed,
#endif
&video_out_gpu,
+ &video_out_gpu_next,
#if HAVE_VDPAU
&video_out_vdpau,
#endif
@@ -87,12 +89,16 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_SDL2_VIDEO
&video_out_sdl,
#endif
+#if HAVE_DMABUF_WAYLAND
+ &video_out_dmabuf_wayland,
+#endif
#if HAVE_VAAPI_X11 && HAVE_GPL
&video_out_vaapi,
#endif
#if HAVE_X11
&video_out_x11,
#endif
+ &video_out_libmpv,
&video_out_null,
// should not be auto-selected
&video_out_image,
@@ -103,21 +109,21 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_DRM
&video_out_drm,
#endif
-#if HAVE_RPI_MMAL
- &video_out_rpi,
+#if HAVE_SIXEL
+ &video_out_sixel,
#endif
+ &video_out_kitty,
&video_out_lavc,
- NULL
};
struct vo_internal {
- pthread_t thread;
+ mp_thread thread;
struct mp_dispatch_queue *dispatch;
struct dr_helper *dr_helper;
// --- The following fields are protected by lock
- pthread_mutex_t lock;
- pthread_cond_t wakeup;
+ mp_mutex lock;
+ mp_cond wakeup;
bool need_wakeup;
bool terminate;
@@ -132,14 +138,14 @@ struct vo_internal {
int queued_events; // event mask for the user
int internal_events; // event mask for us
- int64_t nominal_vsync_interval;
+ double nominal_vsync_interval;
- int64_t vsync_interval;
+ double vsync_interval;
int64_t *vsync_samples;
int num_vsync_samples;
int64_t num_total_vsync_samples;
int64_t prev_vsync;
- int64_t base_vsync;
+ double base_vsync;
int drop_point;
double estimated_vsync_interval;
double estimated_vsync_jitter;
@@ -171,11 +177,11 @@ struct vo_internal {
extern const struct m_sub_options gl_video_conf;
static void forget_frames(struct vo *vo);
-static void *vo_thread(void *ptr);
+static MP_THREAD_VOID vo_thread(void *ptr);
static bool get_desc(struct m_obj_desc *dst, int index)
{
- if (index >= MP_ARRAY_SIZE(video_out_drivers) - 1)
+ if (index >= MP_ARRAY_SIZE(video_out_drivers))
return false;
const struct vo_driver *vo = video_out_drivers[index];
*dst = (struct m_obj_desc) {
@@ -203,7 +209,6 @@ const struct m_obj_list vo_obj_list = {
{"opengl-cb", "libmpv"},
{0}
},
- .allow_unknown_entries = true,
.allow_trailer = true,
.disallow_positional_parameters = true,
.use_global_options = true,
@@ -220,9 +225,9 @@ static void read_opts(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
- in->timing_offset = (uint64_t)(vo->opts->timing_offset * 1e6);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_lock(&in->lock);
+ in->timing_offset = (uint64_t)(MP_TIME_S_TO_NS(vo->opts->timing_offset));
+ mp_mutex_unlock(&in->lock);
}
static void update_opts(void *p)
@@ -231,7 +236,6 @@ static void update_opts(void *p)
if (m_config_cache_update(vo->opts_cache)) {
read_opts(vo);
-
if (vo->driver->control) {
vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL);
// "Legacy" update of video position related options.
@@ -239,18 +243,6 @@ static void update_opts(void *p)
vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
}
}
-
- if (vo->gl_opts_cache && m_config_cache_update(vo->gl_opts_cache)) {
- // "Legacy" update of video GL renderer related options.
- if (vo->driver->control)
- vo->driver->control(vo, VOCTRL_UPDATE_RENDER_OPTS, NULL);
- }
-
- if (m_config_cache_update(vo->eq_opts_cache)) {
- // "Legacy" update of video equalizer related options.
- if (vo->driver->control)
- vo->driver->control(vo, VOCTRL_SET_EQUALIZER, NULL);
- }
}
// Does not include thread- and VO uninit.
@@ -262,9 +254,10 @@ static void dealloc_vo(struct vo *vo)
talloc_free(vo->opts_cache);
talloc_free(vo->gl_opts_cache);
talloc_free(vo->eq_opts_cache);
+ mp_mutex_destroy(&vo->params_mutex);
- pthread_mutex_destroy(&vo->in->lock);
- pthread_cond_destroy(&vo->in->wakeup);
+ mp_mutex_destroy(&vo->in->lock);
+ mp_cond_destroy(&vo->in->wakeup);
talloc_free(vo);
}
@@ -293,6 +286,7 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
.probing = probing,
.in = talloc(vo, struct vo_internal),
};
+ mp_mutex_init(&vo->params_mutex);
talloc_steal(vo, log);
*vo->in = (struct vo_internal) {
.dispatch = mp_dispatch_create(vo),
@@ -301,8 +295,8 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
.stats = stats_ctx_create(vo, global, "vo"),
};
mp_dispatch_set_wakeup_fn(vo->in->dispatch, dispatch_wakeup_cb, vo);
- pthread_mutex_init(&vo->in->lock, NULL);
- pthread_cond_init(&vo->in->wakeup, NULL);
+ mp_mutex_init(&vo->in->lock);
+ mp_cond_init(&vo->in->wakeup);
vo->opts_cache = m_config_cache_alloc(NULL, global, &vo_sub_opts);
vo->opts = vo->opts_cache->opts;
@@ -311,12 +305,7 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
update_opts, vo);
vo->gl_opts_cache = m_config_cache_alloc(NULL, global, &gl_video_conf);
- m_config_cache_set_dispatch_change_cb(vo->gl_opts_cache, vo->in->dispatch,
- update_opts, vo);
-
vo->eq_opts_cache = m_config_cache_alloc(NULL, global, &mp_csp_equalizer_conf);
- m_config_cache_set_dispatch_change_cb(vo->eq_opts_cache, vo->in->dispatch,
- update_opts, vo);
mp_input_set_mouse_transform(vo->input_ctx, NULL, NULL);
if (vo->driver->encode != !!vo->encode_lavc_ctx)
@@ -325,10 +314,10 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
if (!vo->priv)
goto error;
- if (pthread_create(&vo->in->thread, NULL, vo_thread, vo))
+ if (mp_thread_create(&vo->in->thread, vo_thread, vo))
goto error;
if (mp_rendezvous(vo, 0) < 0) { // init barrier
- pthread_join(vo->in->thread, NULL);
+ mp_thread_join(vo->in->thread);
goto error;
}
return vo;
@@ -358,7 +347,7 @@ struct vo *init_best_video_out(struct mpv_global *global, struct vo_extra *ex)
}
autoprobe:
// now try the rest...
- for (int i = 0; video_out_drivers[i]; i++) {
+ for (int i = 0; i < MP_ARRAY_SIZE(video_out_drivers); i++) {
const struct vo_driver *driver = video_out_drivers[i];
if (driver == &video_out_null)
break;
@@ -382,7 +371,7 @@ void vo_destroy(struct vo *vo)
{
struct vo_internal *in = vo->in;
mp_dispatch_run(in->dispatch, terminate_vo, vo);
- pthread_join(vo->in->thread, NULL);
+ mp_thread_join(vo->in->thread);
dealloc_vo(vo);
}
@@ -403,7 +392,7 @@ static void reset_vsync_timings(struct vo *vo)
in->num_successive_vsyncs = 0;
}
-static double vsync_stddef(struct vo *vo, int64_t ref_vsync)
+static double vsync_stddef(struct vo *vo, double ref_vsync)
{
struct vo_internal *in = vo->in;
double jitter = 0;
@@ -414,7 +403,8 @@ static double vsync_stddef(struct vo *vo, int64_t ref_vsync)
return sqrt(jitter / in->num_vsync_samples);
}
-#define MAX_VSYNC_SAMPLES 200
+#define MAX_VSYNC_SAMPLES 1000
+#define DELAY_VSYNC_SAMPLES 10
// Check if we should switch to measured average display FPS if it seems
// "better" then the system-reported one. (Note that small differences are
@@ -425,8 +415,8 @@ static void check_estimated_display_fps(struct vo *vo)
bool use_estimated = false;
if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES / 2 &&
- in->estimated_vsync_interval <= 1e6 / 20.0 &&
- in->estimated_vsync_interval >= 1e6 / 99.0)
+ in->estimated_vsync_interval <= 1e9 / 20.0 &&
+ in->estimated_vsync_interval >= 1e9 / 400.0)
{
for (int n = 0; n < in->num_vsync_samples; n++) {
if (fabs(in->vsync_samples[n] - in->estimated_vsync_interval)
@@ -439,16 +429,16 @@ static void check_estimated_display_fps(struct vo *vo)
use_estimated = true;
done: ;
}
- if (use_estimated == (in->vsync_interval == in->nominal_vsync_interval)) {
+ if (use_estimated == (fabs(in->vsync_interval - in->nominal_vsync_interval) < 1e9)) {
if (use_estimated) {
- MP_VERBOSE(vo, "adjusting display FPS to a value closer to %.3f Hz\n",
- 1e6 / in->estimated_vsync_interval);
+ MP_TRACE(vo, "adjusting display FPS to a value closer to %.3f Hz\n",
+ 1e9 / in->estimated_vsync_interval);
} else {
- MP_VERBOSE(vo, "switching back to assuming display fps = %.3f Hz\n",
- 1e6 / in->nominal_vsync_interval);
+ MP_TRACE(vo, "switching back to assuming display fps = %.3f Hz\n",
+ 1e9 / in->nominal_vsync_interval);
}
}
- in->vsync_interval = use_estimated ? (int64_t)in->estimated_vsync_interval
+ in->vsync_interval = use_estimated ? in->estimated_vsync_interval
: in->nominal_vsync_interval;
}
@@ -459,7 +449,7 @@ static void vsync_skip_detection(struct vo *vo)
struct vo_internal *in = vo->in;
int window = 4;
- int64_t t_r = in->prev_vsync, t_e = in->base_vsync, diff = 0, desync_early = 0;
+ double t_r = in->prev_vsync, t_e = in->base_vsync, diff = 0.0, desync_early = 0.0;
for (int n = 0; n < in->drop_point; n++) {
diff += t_r - t_e;
t_r -= in->vsync_samples[n];
@@ -467,9 +457,9 @@ static void vsync_skip_detection(struct vo *vo)
if (n == window + 1)
desync_early = diff / window;
}
- int64_t desync = diff / in->num_vsync_samples;
+ double desync = diff / in->num_vsync_samples;
if (in->drop_point > window * 2 &&
- llabs(desync - desync_early) >= in->vsync_interval * 3 / 4)
+ fabs(desync - desync_early) >= in->vsync_interval * 3 / 4)
{
// Assume a drop. An underflow can technically speaking not be a drop
// (it's up to the driver what this is supposed to mean), but no reason
@@ -499,7 +489,15 @@ static void update_vsync_timing_after_swap(struct vo *vo,
}
in->num_successive_vsyncs++;
- if (in->num_successive_vsyncs <= 2)
+ if (in->num_successive_vsyncs <= DELAY_VSYNC_SAMPLES)
+ return;
+
+ if (vsync_time <= 0 || vsync_time <= prev_vsync) {
+ in->prev_vsync = 0;
+ return;
+ }
+
+ if (prev_vsync <= 0)
return;
if (in->num_vsync_samples >= MAX_VSYNC_SAMPLES)
@@ -515,8 +513,10 @@ static void update_vsync_timing_after_swap(struct vo *vo,
}
double avg = 0;
- for (int n = 0; n < in->num_vsync_samples; n++)
+ for (int n = 0; n < in->num_vsync_samples; n++) {
+ assert(in->vsync_samples[n] > 0);
avg += in->vsync_samples[n];
+ }
in->estimated_vsync_interval = avg / in->num_vsync_samples;
in->estimated_vsync_jitter =
vsync_stddef(vo, in->vsync_interval) / in->vsync_interval;
@@ -525,33 +525,33 @@ static void update_vsync_timing_after_swap(struct vo *vo,
vsync_skip_detection(vo);
MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter);
- MP_STATS(vo, "value %f vsync-diff", in->vsync_samples[0] / 1e6);
+ MP_STATS(vo, "value %f vsync-diff", MP_TIME_NS_TO_S(in->vsync_samples[0]));
}
// to be called from VO thread only
static void update_display_fps(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (in->internal_events & VO_EVENT_WIN_STATE) {
in->internal_events &= ~(unsigned)VO_EVENT_WIN_STATE;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
double fps = 0;
vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &fps);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->reported_display_fps = fps;
}
- double display_fps = vo->opts->override_display_fps;
+ double display_fps = vo->opts->display_fps_override;
if (display_fps <= 0)
display_fps = in->reported_display_fps;
if (in->display_fps != display_fps) {
- in->nominal_vsync_interval = display_fps > 0 ? 1e6 / display_fps : 0;
+ in->nominal_vsync_interval = display_fps > 0 ? 1e9 / display_fps : 0;
in->vsync_interval = MPMAX(in->nominal_vsync_interval, 1);
in->display_fps = display_fps;
@@ -562,7 +562,7 @@ static void update_display_fps(struct vo *vo)
wakeup_core(vo);
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static void check_vo_caps(struct vo *vo)
@@ -594,8 +594,10 @@ static void run_reconfig(void *p)
mp_image_params_get_dsize(params, &vo->dwidth, &vo->dheight);
+ mp_mutex_lock(&vo->params_mutex);
talloc_free(vo->params);
vo->params = talloc_dup(vo, params);
+ mp_mutex_unlock(&vo->params_mutex);
if (vo->driver->reconfig2) {
*ret = vo->driver->reconfig2(vo, img);
@@ -606,16 +608,18 @@ static void run_reconfig(void *p)
if (vo->config_ok) {
check_vo_caps(vo);
} else {
+ mp_mutex_lock(&vo->params_mutex);
talloc_free(vo->params);
vo->params = NULL;
+ mp_mutex_unlock(&vo->params_mutex);
}
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
talloc_free(in->current_frame);
in->current_frame = NULL;
forget_frames(vo);
reset_vsync_timings(vo);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
update_display_fps(vo);
}
@@ -704,12 +708,10 @@ void vo_wait_default(struct vo *vo, int64_t until_time)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
- if (!in->need_wakeup) {
- struct timespec ts = mp_time_us_to_timespec(until_time);
- pthread_cond_timedwait(&in->wakeup, &in->lock, &ts);
- }
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_lock(&in->lock);
+ if (!in->need_wakeup)
+ mp_cond_timedwait_until(&in->wakeup, &in->lock, until_time);
+ mp_mutex_unlock(&in->lock);
}
// Called unlocked.
@@ -722,16 +724,16 @@ static void wait_vo(struct vo *vo, int64_t until_time)
} else {
vo_wait_default(vo, until_time);
}
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->need_wakeup = false;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static void wakeup_locked(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_cond_broadcast(&in->wakeup);
+ mp_cond_broadcast(&in->wakeup);
if (vo->driver->wakeup)
vo->driver->wakeup(vo);
in->need_wakeup = true;
@@ -743,44 +745,55 @@ void vo_wakeup(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
wakeup_locked(vo);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
+}
+
+static int64_t get_current_frame_end(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ if (!in->current_frame)
+ return -1;
+ return in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
}
static bool still_displaying(struct vo *vo)
{
struct vo_internal *in = vo->in;
- int64_t now = mp_time_us();
- int64_t frame_end = 0;
- if (in->current_frame) {
- frame_end = in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
- if (in->current_frame->display_synced)
- frame_end = in->current_frame->num_vsyncs > 0 ? INT64_MAX : 0;
- }
- return (now < frame_end || in->rendering || in->frame_queued) && in->hasframe;
+ bool working = in->rendering || in->frame_queued;
+ if (working)
+ goto done;
+
+ int64_t frame_end = get_current_frame_end(vo);
+ if (frame_end < 0)
+ goto done;
+ working = mp_time_ns() < frame_end;
+
+done:
+ return working && in->hasframe;
}
// Return true if there is still a frame being displayed (or queued).
bool vo_still_displaying(struct vo *vo)
{
- pthread_mutex_lock(&vo->in->lock);
+ mp_mutex_lock(&vo->in->lock);
bool res = still_displaying(vo);
- pthread_mutex_unlock(&vo->in->lock);
+ mp_mutex_unlock(&vo->in->lock);
return res;
}
-// Make vo issue a wakeup once vo_still_displaying() becomes true.
+// Make vo issue a wakeup once vo_still_displaying() becomes false.
void vo_request_wakeup_on_done(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&vo->in->lock);
+ mp_mutex_lock(&vo->in->lock);
if (still_displaying(vo)) {
in->wakeup_on_done = true;
} else {
wakeup_core(vo);
}
- pthread_mutex_unlock(&vo->in->lock);
+ mp_mutex_unlock(&vo->in->lock);
}
// Whether vo_queue_frame() can be called. If the VO is not ready yet, the
@@ -794,7 +807,7 @@ void vo_request_wakeup_on_done(struct vo *vo)
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
bool blocked = vo->driver->initially_blocked &&
!(in->internal_events & VO_EVENT_INITIAL_UNBLOCK);
bool r = vo->config_ok && !in->frame_queued && !blocked &&
@@ -806,7 +819,7 @@ bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
// time.
next_pts -= in->timing_offset;
next_pts -= in->flip_queue_offset;
- int64_t now = mp_time_us();
+ int64_t now = mp_time_ns();
if (next_pts > now)
r = false;
if (!in->wakeup_pts || next_pts < in->wakeup_pts) {
@@ -816,7 +829,7 @@ bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
wakeup_locked(vo);
}
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return r;
}
@@ -826,7 +839,7 @@ bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
assert(vo->config_ok && !in->frame_queued &&
(!in->current_frame || in->current_frame->num_vsyncs < 1));
in->hasframe = true;
@@ -835,7 +848,7 @@ void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
in->wakeup_pts = frame->display_synced
? 0 : frame->pts + MPMAX(frame->duration, 0);
wakeup_locked(vo);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
// If a frame is currently being rendered (or queued), wait until it's done.
@@ -843,10 +856,10 @@ void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
void vo_wait_frame(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
while (in->frame_queued || in->rendering)
- pthread_cond_wait(&in->wakeup, &in->lock);
- pthread_mutex_unlock(&in->lock);
+ mp_cond_wait(&in->wakeup, &in->lock);
+ mp_mutex_unlock(&in->lock);
}
// Wait until realtime is >= ts
@@ -854,15 +867,14 @@ void vo_wait_frame(struct vo *vo)
static void wait_until(struct vo *vo, int64_t target)
{
struct vo_internal *in = vo->in;
- struct timespec ts = mp_time_us_to_timespec(target);
- pthread_mutex_lock(&in->lock);
- while (target > mp_time_us()) {
+ mp_mutex_lock(&in->lock);
+ while (target > mp_time_ns()) {
if (in->queued_events & VO_EVENT_LIVE_RESIZING)
break;
- if (pthread_cond_timedwait(&in->wakeup, &in->lock, &ts))
+ if (mp_cond_timedwait_until(&in->wakeup, &in->lock, target))
break;
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static bool render_frame(struct vo *vo)
@@ -873,7 +885,7 @@ static bool render_frame(struct vo *vo)
update_display_fps(vo);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (in->frame_queued) {
talloc_free(in->current_frame);
@@ -894,7 +906,7 @@ static bool render_frame(struct vo *vo)
frame->duration = -1;
}
- int64_t now = mp_time_us();
+ int64_t now = mp_time_ns();
int64_t pts = frame->pts;
int64_t duration = frame->duration;
int64_t end_time = pts + duration;
@@ -910,14 +922,20 @@ static bool render_frame(struct vo *vo)
in->dropped_frame &= frame->can_drop;
// Even if we're hopelessly behind, rather degrade to 10 FPS playback,
// instead of just freezing the display forever.
- in->dropped_frame &= now - in->prev_vsync < 100 * 1000;
+ in->dropped_frame &= now - in->prev_vsync < MP_TIME_MS_TO_NS(100);
in->dropped_frame &= in->hasframe_rendered;
// 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->current_frame->vsync_interval;
+ // Increment the offset only if it's not the last vsync. The current_frame
+ // can still be reused. This is mostly important for redraws that might
+ // overshoot the target vsync point.
+ if (in->current_frame->num_vsyncs > 1) {
+ in->current_frame->vsync_offset += in->current_frame->vsync_interval;
+ in->current_frame->ideal_frame_vsync += in->current_frame->ideal_frame_vsync_duration;
+ }
in->dropped_frame |= in->current_frame->num_vsyncs < 1;
}
if (in->current_frame->num_vsyncs > 0)
@@ -931,6 +949,9 @@ static bool render_frame(struct vo *vo)
in->prev_vsync = now;
in->expecting_vsync = use_vsync;
+ // Store the initial value before we unlock.
+ bool request_redraw = in->request_redraw;
+
if (in->dropped_frame) {
in->drop_count += 1;
wakeup_core(vo);
@@ -942,18 +963,14 @@ static bool render_frame(struct vo *vo)
// timer instead, but possibly benefits from preparing a frame early.
bool can_queue = !in->frame_queued &&
(in->current_frame->num_vsyncs < 1 || !use_vsync);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
if (can_queue)
wakeup_core(vo);
stats_time_start(in->stats, "video-draw");
- if (vo->driver->draw_frame) {
- vo->driver->draw_frame(vo, frame);
- } else {
- vo->driver->draw_image(vo, mp_image_new_ref(frame->current));
- }
+ vo->driver->draw_frame(vo, frame);
stats_time_end(in->stats, "video-draw");
@@ -971,12 +988,12 @@ static bool render_frame(struct vo *vo)
vo->driver->get_vsync(vo, &vsync);
// Make up some crap if presentation feedback is missing.
- if (vsync.last_queue_display_time < 0)
- vsync.last_queue_display_time = mp_time_us();
+ if (vsync.last_queue_display_time <= 0)
+ vsync.last_queue_display_time = mp_time_ns();
stats_time_end(in->stats, "video-flip");
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->dropped_frame = prev_drop_count < vo->in->drop_count;
in->rendering = false;
@@ -991,7 +1008,14 @@ static bool render_frame(struct vo *vo)
if (in->dropped_frame) {
MP_STATS(vo, "drop-vo");
} else {
- in->request_redraw = false;
+ // If the initial redraw request was true or mpv is still playing,
+ // then we can clear it here since we just performed a redraw, or the
+ // next loop will draw what we need. However if there initially is
+ // no redraw request, then something can change this (i.e. the OSD)
+ // while the vo was unlocked. If we are paused, don't touch
+ // in->request_redraw in that case.
+ if (request_redraw || !in->paused)
+ in->request_redraw = false;
}
if (in->current_frame && in->current_frame->num_vsyncs &&
@@ -1001,15 +1025,12 @@ static bool render_frame(struct vo *vo)
if (in->frame_queued && in->frame_queued->display_synced)
more_frames = true;
- pthread_cond_broadcast(&in->wakeup); // for vo_wait_frame()
+ mp_cond_broadcast(&in->wakeup); // for vo_wait_frame()
done:
- talloc_free(frame);
- if (in->wakeup_on_done && !still_displaying(vo)) {
- in->wakeup_on_done = false;
- wakeup_core(vo);
- }
- pthread_mutex_unlock(&in->lock);
+ if (!vo->driver->frame_owner || in->dropped_frame)
+ talloc_free(frame);
+ mp_mutex_unlock(&in->lock);
return more_frames;
}
@@ -1021,7 +1042,7 @@ static void do_redraw(struct vo *vo)
if (!vo->config_ok || (vo->driver->caps & VO_CAP_NORETAIN))
return;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->request_redraw = false;
bool full_redraw = in->dropped_frame;
struct vo_frame *frame = NULL;
@@ -1037,36 +1058,29 @@ static void do_redraw(struct vo *vo)
frame->still = true;
frame->pts = 0;
frame->duration = -1;
- pthread_mutex_unlock(&in->lock);
-
- if (vo->driver->draw_frame) {
- vo->driver->draw_frame(vo, frame);
- } else if ((full_redraw || vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1)
- && frame->current)
- {
- vo->driver->draw_image(vo, mp_image_new_ref(frame->current));
- }
+ mp_mutex_unlock(&in->lock);
+ vo->driver->draw_frame(vo, frame);
vo->driver->flip_page(vo);
- if (frame != &dummy)
+ if (frame != &dummy && !vo->driver->frame_owner)
talloc_free(frame);
}
static struct mp_image *get_image_vo(void *ctx, int imgfmt, int w, int h,
- int stride_align)
+ int stride_align, int flags)
{
struct vo *vo = ctx;
- return vo->driver->get_image(vo, imgfmt, w, h, stride_align);
+ return vo->driver->get_image(vo, imgfmt, w, h, stride_align, flags);
}
-static void *vo_thread(void *ptr)
+static MP_THREAD_VOID vo_thread(void *ptr)
{
struct vo *vo = ptr;
struct vo_internal *in = vo->in;
bool vo_paused = false;
- mpthread_set_name("vo");
+ mp_thread_set_name("vo");
if (vo->driver->get_image) {
in->dr_helper = dr_helper_create(in->dispatch, get_image_vo, vo);
@@ -1089,10 +1103,12 @@ static void *vo_thread(void *ptr)
stats_event(in->stats, "iterations");
vo->driver->control(vo, VOCTRL_CHECK_EVENTS, NULL);
bool working = render_frame(vo);
- int64_t now = mp_time_us();
- int64_t wait_until = now + (working ? 0 : (int64_t)1e9);
+ int64_t now = mp_time_ns();
+ int64_t wait_until = now + MP_TIME_S_TO_NS(working ? 0 : 1000);
+ bool wakeup_on_done = false;
+ int64_t wakeup_core_after = 0;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (in->wakeup_pts) {
if (in->wakeup_pts > now) {
wait_until = MPMIN(wait_until, in->wakeup_pts);
@@ -1105,13 +1121,21 @@ static void *vo_thread(void *ptr)
in->want_redraw = true;
wakeup_core(vo);
}
+ if ((!working && !in->rendering && !in->frame_queued) && in->wakeup_on_done) {
+ // At this point we know VO is going to sleep
+ int64_t frame_end = get_current_frame_end(vo);
+ if (frame_end >= 0)
+ wakeup_core_after = frame_end;
+ wakeup_on_done = true;
+ in->wakeup_on_done = false;
+ }
vo->want_redraw = false;
bool redraw = in->request_redraw;
bool send_reset = in->send_reset;
in->send_reset = false;
bool send_pause = in->paused != vo_paused;
vo_paused = in->paused;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
if (send_reset)
vo->driver->control(vo, VOCTRL_RESET, NULL);
@@ -1124,6 +1148,20 @@ static void *vo_thread(void *ptr)
if (vo->want_redraw) // might have been set by VOCTRLs
wait_until = 0;
+ if (wait_until <= now)
+ continue;
+
+ if (wakeup_on_done) {
+ // At this point wait_until should be longer than frame duration
+ if (wakeup_core_after >= 0 && wait_until >= wakeup_core_after) {
+ wait_vo(vo, wakeup_core_after);
+ mp_mutex_lock(&in->lock);
+ in->need_wakeup = true;
+ mp_mutex_unlock(&in->lock);
+ }
+ wakeup_core(vo);
+ }
+
wait_vo(vo, wait_until);
}
forget_frames(vo); // implicitly synchronized
@@ -1132,13 +1170,13 @@ static void *vo_thread(void *ptr)
vo->driver->uninit(vo);
done:
TA_FREEP(&in->dr_helper);
- return NULL;
+ MP_THREAD_RETURN();
}
void vo_set_paused(struct vo *vo, bool paused)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (in->paused != paused) {
in->paused = paused;
if (in->paused && in->dropped_frame) {
@@ -1148,55 +1186,55 @@ void vo_set_paused(struct vo *vo, bool paused)
reset_vsync_timings(vo);
wakeup_locked(vo);
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
int64_t vo_get_drop_count(struct vo *vo)
{
- pthread_mutex_lock(&vo->in->lock);
+ mp_mutex_lock(&vo->in->lock);
int64_t r = vo->in->drop_count;
- pthread_mutex_unlock(&vo->in->lock);
+ mp_mutex_unlock(&vo->in->lock);
return r;
}
void vo_increment_drop_count(struct vo *vo, int64_t n)
{
- pthread_mutex_lock(&vo->in->lock);
+ mp_mutex_lock(&vo->in->lock);
vo->in->drop_count += n;
- pthread_mutex_unlock(&vo->in->lock);
+ mp_mutex_unlock(&vo->in->lock);
}
// Make the VO redraw the OSD at some point in the future.
void vo_redraw(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (!in->request_redraw) {
in->request_redraw = true;
in->want_redraw = false;
wakeup_locked(vo);
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
bool vo_want_redraw(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
bool r = in->want_redraw;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return r;
}
void vo_seek_reset(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
forget_frames(vo);
reset_vsync_timings(vo);
in->send_reset = true;
wakeup_locked(vo);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
// Whether at least 1 frame was queued or rendered since last seek or reconfig.
@@ -1241,53 +1279,52 @@ void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
out_src, out_dst, out_osd);
}
-// flip_page[_timed] will be called offset_us microseconds too early.
+// flip_page[_timed] will be called offset_us nanoseconds too early.
// (For vo_vdpau, which does its own timing.)
// num_req_frames set the requested number of requested vo_frame.frames.
// (For vo_gpu interpolation.)
-void vo_set_queue_params(struct vo *vo, int64_t offset_us, int num_req_frames)
+void vo_set_queue_params(struct vo *vo, int64_t offset_ns, int num_req_frames)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
- in->flip_queue_offset = offset_us;
+ mp_mutex_lock(&in->lock);
+ in->flip_queue_offset = offset_ns;
in->req_frames = MPCLAMP(num_req_frames, 1, VO_MAX_REQ_FRAMES);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
int vo_get_num_req_frames(struct vo *vo)
{
struct vo_internal *in = vo->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
int res = in->req_frames;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return res;
}
-int64_t vo_get_vsync_interval(struct vo *vo)
+double vo_get_vsync_interval(struct vo *vo)
{
struct vo_internal *in = vo->in;</