summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--player/video.c32
-rw-r--r--video/out/gl_video.c40
-rw-r--r--video/out/gl_video.h5
-rw-r--r--video/out/vo.c225
-rw-r--r--video/out/vo.h62
-rw-r--r--video/out/vo_opengl.c24
-rw-r--r--video/out/vo_opengl_cb.c104
-rw-r--r--video/out/vo_vdpau.c45
8 files changed, 267 insertions, 270 deletions
diff --git a/player/video.c b/player/video.c
index da93203b7a..87e6a8fb60 100644
--- a/player/video.c
+++ b/player/video.c
@@ -582,6 +582,17 @@ static void handle_new_frame(struct MPContext *mpctx)
MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
}
+// Remove the first frame in mpctx->next_frames
+static void shift_frames(struct MPContext *mpctx)
+{
+ if (mpctx->num_next_frames < 1)
+ return;
+ talloc_free(mpctx->next_frames[0]);
+ for (int n = 0; n < mpctx->num_next_frames - 1; n++)
+ mpctx->next_frames[n] = mpctx->next_frames[n + 1];
+ mpctx->num_next_frames -= 1;
+}
+
static int get_req_frames(struct MPContext *mpctx, bool eof)
{
struct MPOpts *opts = mpctx->opts;
@@ -880,17 +891,16 @@ void write_video(struct MPContext *mpctx, double endpts)
update_subtitles(mpctx);
assert(mpctx->num_next_frames >= 1);
- struct mp_image *frames[VO_MAX_FUTURE_FRAMES + 2] = {0};
- frames[0] = mpctx->next_frames[0];
- for (int n = 0; n < mpctx->num_next_frames - 1; n++)
- mpctx->next_frames[n] = mpctx->next_frames[n + 1];
- mpctx->num_next_frames -= 1;
- for (int n = 0; n < mpctx->num_next_frames && n < VO_MAX_FUTURE_FRAMES; n++) {
- frames[n + 1] = mp_image_new_ref(mpctx->next_frames[n]);
- if (!frames[n + 1])
- break; // OOM
- }
- vo_queue_frame(vo, frames, pts, duration);
+ struct vo_frame dummy = {
+ .pts = pts,
+ .duration = duration,
+ .num_frames = mpctx->num_next_frames,
+ };
+ for (int n = 0; n < dummy.num_frames; n++)
+ dummy.frames[n] = mpctx->next_frames[n];
+ vo_queue_frame(vo, vo_frame_ref(&dummy));
+
+ shift_frames(mpctx);
// The frames were shifted down; "initialize" the new first entry.
if (mpctx->num_next_frames >= 1)
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 478cd2263b..3d867512f3 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -484,6 +484,7 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
static bool init_format(int fmt, struct gl_video *init);
static void gl_video_upload_image(struct gl_video *p);
+static void gl_video_set_image(struct gl_video *p, struct mp_image *mpi);
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
@@ -2063,8 +2064,8 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo)
}
// Draws an interpolate frame to fbo, based on the frame timing in t
-static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
- struct frame_timing *t)
+static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
+ int fbo)
{
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0;
@@ -2072,7 +2073,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
// First of all, figure out if we have a frame availble at all, and draw
// it manually + reset the queue if not
if (p->surfaces[p->surface_now].pts == MP_NOPTS_VALUE) {
- pass_render_frame(p, t->frame);
+ pass_render_frame(p, t->current);
finish_pass_fbo(p, &p->surfaces[p->surface_now].fbotex,
vp_w, vp_h, 0, FBOTEX_FUZZY);
p->surfaces[p->surface_now].pts = p->image.mpi->pts;
@@ -2080,11 +2081,11 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
}
// Find the right frame for this instant
- if (t->frame && t->frame->pts != MP_NOPTS_VALUE) {
+ if (t->current&& t->current->pts != MP_NOPTS_VALUE) {
int next = fbosurface_wrap(p->surface_now + 1);
while (p->surfaces[next].pts != MP_NOPTS_VALUE &&
p->surfaces[next].pts > p->surfaces[p->surface_now].pts &&
- p->surfaces[p->surface_now].pts < t->frame->pts)
+ p->surfaces[p->surface_now].pts < t->current->pts)
{
p->surface_now = next;
next = fbosurface_wrap(next + 1);
@@ -2115,12 +2116,12 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
// it only barely matters at the very beginning of playback, and this way
// makes the code much more linear.
int surface_dst = fbosurface_wrap(p->surface_idx+1);
- for (int i = -1; i < t->num_future_frames; i++) {
+ for (int i = 0; i < t->num_frames; i++) {
// Avoid overwriting data we might still need
if (surface_dst == surface_bse - 1)
break;
- struct mp_image *f = i < 0 ? t->frame : t->future_frames[i];
+ struct mp_image *f = t->frames[i];
if (!f || f->pts == MP_NOPTS_VALUE)
continue;
@@ -2202,15 +2203,16 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
}
// (fbo==0 makes BindFramebuffer select the screen backbuffer)
-void gl_video_render_frame(struct gl_video *p, struct mp_image *mpi, int fbo,
- struct frame_timing *t)
+void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
{
GL *gl = p->gl;
struct video_image *vimg = &p->image;
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- if ((!mpi && !vimg->mpi) || p->dst_rect.x0 > 0 || p->dst_rect.y0 > 0 ||
+ bool has_frame = frame->current || vimg->mpi;
+
+ if (!has_frame || p->dst_rect.x0 > 0 || p->dst_rect.y0 > 0 ||
p->dst_rect.x1 < p->vp_w || p->dst_rect.y1 < abs(p->vp_h))
{
struct m_color c = p->opts.background;
@@ -2218,14 +2220,16 @@ void gl_video_render_frame(struct gl_video *p, struct mp_image *mpi, int fbo,
gl->Clear(GL_COLOR_BUFFER_BIT);
}
- gl_sc_set_vao(p->sc, &p->vao);
+ if (has_frame) {
+ gl_sc_set_vao(p->sc, &p->vao);
- if (p->opts.interpolation && t) {
- gl_video_interpolate_frame(p, fbo, t);
- } else {
- // Skip interpolation if there's nothing to be done
- pass_render_frame(p, mpi);
- pass_draw_to_screen(p, fbo);
+ if (p->opts.interpolation && !frame->still) {
+ gl_video_interpolate_frame(p, frame, fbo);
+ } else {
+ // Skip interpolation if there's nothing to be done
+ pass_render_frame(p, frame->redraw ? NULL : frame->current);
+ pass_draw_to_screen(p, fbo);
+ }
}
debug_check_gl(p, "after video rendering");
@@ -2293,7 +2297,7 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi)
return true;
}
-void gl_video_set_image(struct gl_video *p, struct mp_image *mpi)
+static void gl_video_set_image(struct gl_video *p, struct mp_image *mpi)
{
assert(mpi);
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index e3ce40b606..4e82215268 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -77,6 +77,7 @@ extern const struct gl_video_opts gl_video_opts_hq_def;
extern const struct gl_video_opts gl_video_opts_def;
struct gl_video;
+struct vo_frame;
struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g);
void gl_video_uninit(struct gl_video *p);
@@ -87,9 +88,7 @@ bool gl_video_check_format(struct gl_video *p, int mp_format);
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b);
void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d);
-void gl_video_set_image(struct gl_video *p, struct mp_image *img);
-void gl_video_render_frame(struct gl_video *p, struct mp_image *img, int fbo,
- struct frame_timing *t);
+void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo);
void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
struct mp_rect *src, struct mp_rect *dst,
struct mp_osd_res *osd);
diff --git a/video/out/vo.c b/video/out/vo.c
index ae5eca7ba5..920fb00e6b 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -140,17 +140,13 @@ struct vo_internal {
int64_t drop_count;
bool dropped_frame; // the previous frame was dropped
- struct mp_image *current_frame; // last frame queued to the VO
+ struct vo_frame *current_frame; // last frame queued to the VO
int64_t wakeup_pts; // time at which to pull frame from decoder
bool rendering; // true if an image is being rendered
- struct mp_image *frame_queued; // the image that should be rendered
- struct mp_image *future_frames[VO_MAX_FUTURE_FRAMES];
- int num_future_frames;
- int req_future_frames; // VO's requested value of num_future_frames
- int64_t frame_pts; // realtime of intended display
- int64_t frame_duration; // realtime frame duration (for framedrop)
+ struct vo_frame *frame_queued; // should be drawn next
+ int req_frames; // VO's requested value of num_frames
double display_fps;
@@ -239,6 +235,7 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
talloc_steal(vo, log);
*vo->in = (struct vo_internal) {
.dispatch = mp_dispatch_create(vo),
+ .req_frames = 1,
};
mp_make_wakeup_pipe(vo->in->wakeup_pipe);
mp_dispatch_set_wakeup_fn(vo->in->dispatch, dispatch_wakeup_cb, vo);
@@ -377,7 +374,8 @@ static void run_reconfig(void *p)
}
pthread_mutex_lock(&in->lock);
- mp_image_unrefp(&in->current_frame);
+ talloc_free(in->current_frame);
+ in->current_frame = NULL;
forget_frames(vo);
pthread_mutex_unlock(&in->lock);
@@ -413,31 +411,14 @@ int vo_control(struct vo *vo, uint32_t request, void *data)
}
// must be called locked
-// transfers ownership of frames[] items to the VO
-static void set_future_frames(struct vo *vo, struct mp_image **frames)
-{
- struct vo_internal *in = vo->in;
- for (int n = 0; n < in->num_future_frames; n++)
- talloc_free(in->future_frames[n]);
- in->num_future_frames = 0;
- for (int n = 0; frames && frames[n]; n++) {
- if (n < in->req_future_frames) {
- in->future_frames[in->num_future_frames++] = frames[n];
- } else {
- talloc_free(frames[n]);
- }
- }
-}
-
-// must be called locked
static void forget_frames(struct vo *vo)
{
struct vo_internal *in = vo->in;
in->hasframe = false;
in->hasframe_rendered = false;
in->drop_count = 0;
- mp_image_unrefp(&in->frame_queued);
- set_future_frames(vo, NULL);
+ talloc_free(in->frame_queued);
+ in->frame_queued = NULL;
// don't unref current_frame; we always want to be able to redraw it
}
@@ -551,22 +532,15 @@ bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
// Direct the VO thread to put the currently queued image on the screen.
// vo_is_ready_for_frame() must have returned true before this call.
-// images[0] is the frame to draw, images[n+1] are future frames (NULL
-// terminated). Ownership of all the images is handed to the vo.
-void vo_queue_frame(struct vo *vo, struct mp_image **images,
- int64_t pts_us, int64_t duration)
+// Ownership of frame is handed to the vo.
+void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
{
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
- struct mp_image *image = images[0];
- assert(image);
assert(vo->config_ok && !in->frame_queued);
in->hasframe = true;
- in->frame_queued = image;
- in->frame_pts = pts_us;
- in->frame_duration = duration;
- in->wakeup_pts = in->vsync_timed ? 0 : in->frame_pts + MPMAX(duration, 0);
- set_future_frames(vo, images + 1);
+ in->frame_queued = frame;
+ in->wakeup_pts = in->vsync_timed ? 0 : frame->pts + MPMAX(frame->duration, 0);
wakeup_locked(vo);
pthread_mutex_unlock(&in->lock);
}
@@ -613,6 +587,8 @@ static int64_t prev_sync(struct vo *vo, int64_t ts)
static bool render_frame(struct vo *vo)
{
struct vo_internal *in = vo->in;
+ struct vo_frame *frame = NULL;
+ bool got_frame = false;
update_display_fps(vo);
@@ -621,32 +597,35 @@ 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);
- int64_t pts = in->frame_pts;
- int64_t duration = in->frame_duration;
- struct mp_image *img = in->frame_queued;
-
- if (!img && (!in->vsync_timed || in->paused))
- goto nothing_done;
-
- if (in->vsync_timed && !in->hasframe)
- goto nothing_done;
+ 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)
+ {
+ goto done;
+ }
- if (img)
- mp_image_setrefp(&in->current_frame, img);
+ frame = vo_frame_ref(in->current_frame);
+ assert(frame);
- in->frame_queued = NULL;
+ int64_t pts = frame->pts;
+ int64_t duration = frame->duration;
+ int64_t end_time = pts + duration;
// The next time a flip (probably) happens.
int64_t prev_vsync = prev_sync(vo, mp_time_us());
int64_t next_vsync = prev_vsync + in->vsync_interval;
- int64_t end_time = pts + duration;
+
+ frame->next_vsync = next_vsync;
+ frame->prev_vsync = prev_vsync;
+
+ frame->vsync_offset = next_vsync - pts;
// Time at which we should flip_page on the VO.
int64_t target = pts - in->flip_queue_offset;
- if (!in->hasframe_rendered)
- duration = -1; // disable framedrop
-
bool prev_dropped_frame = in->dropped_frame;
// "normal" strict drop threshold.
@@ -675,6 +654,7 @@ static bool render_frame(struct vo *vo)
// 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;
+ in->dropped_frame &= in->hasframe_rendered;
if (in->vsync_timed) {
// this is a heuristic that wakes the thread up some
@@ -684,55 +664,37 @@ static bool render_frame(struct vo *vo)
// We are very late with the frame and using vsync timing: probably
// no new frames are coming in. This must be done whether or not
// framedrop is enabled. Also, if the frame is to be dropped, even
- // though it's an interpolated frame (img==NULL), exit early.
- if (!img && ((in->hasframe_rendered &&
- prev_vsync > pts + duration + in->vsync_interval_approx)
- || in->dropped_frame))
+ // though it's an interpolated frame (repeat set), exit early.
+ bool late = prev_vsync > pts + duration + in->vsync_interval_approx;
+ if (frame->repeat && ((in->hasframe_rendered && late) || in->dropped_frame))
{
in->dropped_frame = false;
- goto nothing_done;
+ goto done;
}
}
- if (in->dropped_frame) {
- talloc_free(img);
- } else {
+ // 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 (!in->dropped_frame) {
in->rendering = true;
in->hasframe_rendered = true;
- int num_future_frames = in->num_future_frames;
- in->num_future_frames = 0;
- struct mp_image *future_frames[VO_MAX_FUTURE_FRAMES];
- for (int n = 0; n < num_future_frames; n++) {
- future_frames[n] = in->future_frames[n];
- in->future_frames[n] = NULL;
- }
+ int64_t prev_drop_count = vo->in->drop_count;
pthread_mutex_unlock(&in->lock);
mp_input_wakeup(vo->input_ctx); // core can queue new video now
MP_STATS(vo, "start video");
- if (vo->driver->draw_image_timed) {
- struct frame_timing t = (struct frame_timing) {
- .pts = pts,
- .next_vsync = next_vsync,
- .prev_vsync = prev_vsync,
- .vsync_offset = next_vsync - pts,
- .frame = img,
- .num_future_frames = num_future_frames,
- .future_frames = future_frames,
- };
- vo->driver->draw_image_timed(vo, img, &t);
+ if (vo->driver->draw_frame) {
+ vo->driver->draw_frame(vo, frame);
} else {
- vo->driver->draw_image(vo, img);
+ vo->driver->draw_image(vo, mp_image_new_ref(frame->current));
}
wait_until(vo, target);
- bool drop = false;
- if (vo->driver->flip_page_timed)
- drop = vo->driver->flip_page_timed(vo, pts, duration) < 1;
- else
- vo->driver->flip_page(vo);
+ vo->driver->flip_page(vo);
int64_t prev_flip = in->last_flip;
@@ -748,10 +710,8 @@ static bool render_frame(struct vo *vo)
MP_STATS(vo, "end video");
pthread_mutex_lock(&in->lock);
- in->dropped_frame = drop;
+ in->dropped_frame = prev_drop_count < vo->in->drop_count;
in->rendering = false;
- for (int n = 0; n < num_future_frames; n++)
- talloc_free(future_frames[n]);
}
if (in->dropped_frame) {
@@ -765,12 +725,12 @@ static bool render_frame(struct vo *vo)
pthread_cond_broadcast(&in->wakeup); // for vo_wait_frame()
mp_input_wakeup(vo->input_ctx);
- pthread_mutex_unlock(&in->lock);
- return true;
+ got_frame = true;
-nothing_done:
+done:
+ talloc_free(frame);
pthread_mutex_unlock(&in->lock);
- return false;
+ return got_frame;
}
static void do_redraw(struct vo *vo)
@@ -779,28 +739,37 @@ static void do_redraw(struct vo *vo)
vo->want_redraw = false;
+ if (!vo->config_ok)
+ return;
+
pthread_mutex_lock(&in->lock);
in->request_redraw = false;
in->want_redraw = false;
bool full_redraw = in->dropped_frame;
- struct mp_image *img = NULL;
- if (vo->config_ok && !(vo->driver->untimed))
- img = mp_image_new_ref(in->current_frame);
- if (img)
+ struct vo_frame *frame = NULL;
+ if (!vo->driver->untimed)
+ frame = vo_frame_ref(in->current_frame);
+ if (frame)
in->dropped_frame = false;
+ struct vo_frame dummy = {0};
+ if (!frame)
+ frame = &dummy;
+ frame->redraw = !full_redraw; // unconditionally redraw if it was dropped
+ frame->still = true;
pthread_mutex_unlock(&in->lock);
- if (full_redraw || vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1) {
- if (img)
- vo->driver->draw_image(vo, img);
- } else {
- talloc_free(img);
+ 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));
}
- if (vo->driver->flip_page_timed)
- vo->driver->flip_page_timed(vo, 0, -1);
- else
- vo->driver->flip_page(vo);
+ vo->driver->flip_page(vo);
+
+ if (frame != &dummy)
+ talloc_free(frame);
}
static void *vo_thread(void *ptr)
@@ -851,9 +820,10 @@ static void *vo_thread(void *ptr)
}
wait_vo(vo, wait_until);
}
- forget_frames(vo); // implicitly synchronized
- mp_image_unrefp(&in->current_frame);
vo->driver->uninit(vo);
+ forget_frames(vo); // implicitly synchronized
+ talloc_free(in->current_frame);
+ in->current_frame = NULL;
return NULL;
}
@@ -922,7 +892,9 @@ bool vo_still_displaying(struct vo *vo)
struct vo_internal *in = vo->in;
pthread_mutex_lock(&vo->in->lock);
int64_t now = mp_time_us();
- int64_t frame_end = in->frame_pts + MPMAX(in->frame_duration, 0);
+ int64_t frame_end = 0;
+ if (in->current_frame)
+ frame_end = in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
bool working = now < frame_end || in->rendering || in->frame_queued;
pthread_mutex_unlock(&vo->in->lock);
return working && in->hasframe;
@@ -995,7 +967,7 @@ void vo_set_queue_params(struct vo *vo, int64_t offset_us, bool vsync_timed,
pthread_mutex_lock(&in->lock);
in->flip_queue_offset = offset_us;
in->vsync_timed = vsync_timed;
- in->req_future_frames = MPMIN(num_future_frames, VO_MAX_FUTURE_FRAMES);
+ in->req_frames = 1 + MPMIN(num_future_frames, VO_MAX_FUTURE_FRAMES);
pthread_mutex_unlock(&in->lock);
}
@@ -1003,7 +975,7 @@ int vo_get_num_future_frames(struct vo *vo)
{
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
- int res = in->req_future_frames;
+ int res = in->req_frames + 1;
pthread_mutex_unlock(&in->lock);
return res;
}
@@ -1058,11 +1030,40 @@ struct mp_image *vo_get_current_frame(struct vo *vo)
{
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
- struct mp_image *r = mp_image_new_ref(vo->in->current_frame);
+ struct mp_image *r = NULL;
+ if (vo->in->current_frame)
+ r = mp_image_new_ref(vo->in->current_frame->current);
pthread_mutex_unlock(&in->lock);
return r;
}
+static void destroy_frame(void *p)
+{
+ struct vo_frame *frame = p;
+ for (int n = 0; n < frame->num_frames; n++)
+ talloc_free(frame->frames[n]);
+}
+
+// Return a new reference to the given frame. The image pointers are also new
+// references. Calling talloc_free() on the frame unrefs all currently set
+// image references. (Assuming current==frames[0].)
+struct vo_frame *vo_frame_ref(struct vo_frame *frame)
+{
+ if (!frame)
+ return NULL;
+
+ struct vo_frame *new = talloc_ptrtype(NULL, new);
+ talloc_set_destructor(new, destroy_frame);
+ *new = *frame;
+ for (int n = 0; n < frame->num_frames; n++) {
+ new->frames[n] = mp_image_new_ref(frame->frames[n]);
+ if (!new->frames[n])
+ abort(); // OOM on tiny allocs
+ }
+ new->current = new->num_frames ? new->frames[0] : NULL;
+ return new;
+}
+
/*
* lookup an integer in a table, table must have 0 as the last key
* param: key key to search for
diff --git a/video/out/vo.h b/video/out/vo.h
index 3d36269d26..9e1a6db93c 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -154,29 +154,38 @@ struct vo_extra {
struct mpv_opengl_cb_context *opengl_cb_context;
};
-struct frame_timing {
+struct vo_frame {
// If > 0, realtime when frame should be shown, in mp_time_us() units.
+ // If 0, present immediately.
int64_t pts;
+ // Approximate frame duration, in us.
+ int duration;
// Realtime of estimated previous and next vsync events.
int64_t next_vsync;
int64_t prev_vsync;
// "ideal" display time within the vsync
int64_t vsync_offset;
- // The current frame to be drawn. NULL means redraw previous frame
- // (e.g. repeated frames).
- // (Equivalent to the mp_image parameter of draw_image_timed, until the
- // parameter is removed.)
- struct mp_image *frame;
+ // Set if the current frame is repeated from the previous. It's guaranteed
+ // that the current is the same as the previous one, even if the image
+ // pointer is different.
+ // The repeat flag is additionally set if the OSD does not need to be
+ // redrawn.
+ bool redraw, repeat;
+ // The frame is not in movement - e.g. redrawing while paused.
+ bool still;
+ // The current frame to be drawn.
+ // Warning: When OSD should be redrawn in --force-window --idle mode, this
+ // can be NULL. The VO should draw a black background, OSD on top.
+ struct mp_image *current;
// List of future images, starting with the next one. This does not
// care about repeated frames - it simply contains the next real frames.
- // vo_set_queue_params() sets how many frames this should include, though
- // the actual number can be lower.
- // future_frames[0] is the next frame.
- // Note that this has frames only when a new real frame is pushed. Redraw
- // calls or repeated frames do not include this.
- // Ownership of the frames belongs to the caller.
- int num_future_frames;
- struct mp_image **future_frames;
+ // vo_set_queue_params() sets how many future frames this should include.
+ // The actual number of frames delivered to the VO can be lower.
+ // frames[0] is current, frames[1] is the next frame.
+ // Note that some future frames may never be sent as current frame to the
+ // VO if frames are dropped.
+ int num_frames;
+ struct mp_image *frames[VO_MAX_FUTURE_FRAMES + 1];
};
struct vo_driver {
@@ -223,31 +232,21 @@ struct vo_driver {
* mpi belongs to the VO; the VO must free it eventually.
*
* This also should draw the OSD.
+ *
+ * Deprecated for draw_frame. A VO should have only either callback set.
*/
void (*draw_image)(struct vo *vo, struct mp_image *mpi);
- /* Like draw image, but is called before every vsync with timing
- * information
+ /* Render the given frame. Note that this is also called when repeating
+ * or redrawing frames.
*/
- void (*draw_image_timed)(struct vo *vo, struct mp_image *mpi,
- struct frame_timing *t);
+ void (*draw_frame)(struct vo *vo, struct vo_frame *frame);
/*
* Blit/Flip buffer to the screen. Must be called after each frame!
*/
void (*flip_page)(struct vo *vo);
- /*
- * Timed version of flip_page (optional).
- * pts_us is the frame presentation time, linked to mp_time_us().
- * pts_us is 0 if the frame should be presented immediately.
- * duration is estimated time in us until the next frame is shown.
- * duration is -1 if it is unknown or unset (also: disable framedrop).
- * If the VO does manual framedropping, VO_CAP_FRAMEDROP should be set.
- * Returns 1 on display, or 0 if the frame was dropped.
- */
- int (*flip_page_timed)(struct vo *vo, int64_t pts_us, int duration);
-
/* These optional callbacks can be provided if the GUI framework used by
* the VO requires entering a message loop for receiving events, does not
* provide event_fd, and does not call vo_wakeup() from a separate thread
@@ -323,8 +322,7 @@ int vo_reconfig(struct vo *vo, struct mp_image_params *p, int flags);
int vo_control(struct vo *vo, uint32_t request, void *data);
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts);
-void vo_queue_frame(struct vo *vo, struct mp_image **images,
- int64_t pts_us, int64_t duration);
+void vo_queue_frame(struct vo *vo, struct vo_frame *frame);
void vo_wait_frame(struct vo *vo);
bool vo_still_displaying(struct vo *vo);
bool vo_has_frame(struct vo *vo);
@@ -359,4 +357,6 @@ struct mp_osd_res;
void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
struct mp_rect *out_dst, struct mp_osd_res *out_osd);
+struct vo_frame *vo_frame_ref(struct vo_frame *frame);
+
#endif /* MPLAYER_VIDEO_OUT_H */
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 2d3ab5ba93..043ddaa836 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -164,8 +164,7 @@ static void flip_page(struct vo *vo)
}
}
-static void draw_image_timed(struct vo *vo, mp_image_t *mpi,
- struct frame_timing *t)
+static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
@@ -174,7 +173,7 @@ static void draw_image_timed(struct vo *vo, mp_image_t *mpi,
return;
p->frame_started = true;
- gl_video_render_frame(p->renderer, mpi, 0, t);
+ gl_video_render_frame(p->renderer, frame, 0);
// The playloop calls this last before waiting some time until it decides
// to call flip_page(). Tell OpenGL to start execution of the GPU commands
@@ -183,13 +182,6 @@ static void draw_image_timed(struct vo *vo, mp_image_t *mpi,
if (p->use_glFinish)
gl->Finish();
-
- talloc_free(mpi);
-}
-
-static void draw_image(struct vo *vo, mp_image_t *mpi)
-{
- draw_image_timed(vo, mpi, NULL);
}
static int query_format(struct vo *vo, int format)
@@ -355,12 +347,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_LOAD_HWDEC_API:
request_hwdec_api(p, data);
return true;
- case VOCTRL_REDRAW_FRAME:
- if (!(p->glctx->start_frame && !p->glctx->start_frame(p->glctx))) {
- p->frame_started = true;
- gl_video_render_frame(p->renderer, NULL, 0, NULL);
- }
- return true;
case VOCTRL_SET_COMMAND_LINE: {
char *arg = data;
return reparse_cmdline(p, arg);
@@ -489,8 +475,7 @@ const struct vo_driver video_out_opengl = {
.query_format = query_format,
.reconfig = reconfig,
.control = control,
- .draw_image = draw_image,
- .draw_image_timed = draw_image_timed,
+ .draw_frame = draw_frame,
.flip_page = flip_page,
.uninit = uninit,
.priv_size = sizeof(struct gl_priv),
@@ -505,8 +490,7 @@ const struct vo_driver video_out_opengl_hq = {
.query_format = query_format,
.reconfig = reconfig,
.control = control,
- .draw_image = draw_image,
- .draw_image_timed = draw_image_timed,
+ .draw_frame = draw_frame,
.flip_page = flip_page,
.uninit = uninit,
.priv_size = sizeof(struct gl_priv),
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index 96a312f98c..c1a8748f4e 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -64,9 +64,10 @@ struct mpv_opengl_cb_context {
bool initialized;
mpv_opengl_cb_update_fn update_cb;
void *update_cb_ctx;
- struct mp_image *waiting_frame;
- struct mp_image **frame_queue;
+ struct vo_frame *waiting_frame;
+ struct vo_frame **frame_queue;
int queued_frames;
+ struct vo_frame *cur_frame;
struct mp_image_params img_params;
bool reconfigured;
int vp_w, vp_h;
@@ -81,7 +82,6 @@ struct mpv_opengl_cb_context {
struct mp_csp_equalizer eq;
int64_t recent_flip;
int64_t approx_vsync;
- int64_t cur_pts;
bool vsync_timed;
// --- All of these can only be accessed from the thread where the host
@@ -103,11 +103,11 @@ static void update(struct vo_priv *p);
// all queue manipulation functions shold be called under locked state
-static struct mp_image *frame_queue_pop(struct mpv_opengl_cb_context *ctx)
+static struct vo_frame *frame_queue_pop(struct mpv_opengl_cb_context *ctx)
{
if (ctx->queued_frames == 0)
return NULL;
- struct mp_image *ret = ctx->frame_queue[0];
+ struct vo_frame *ret = ctx->frame_queue[0];
MP_TARRAY_REMOVE_AT(ctx->frame_queue, ctx->queued_frames, 0);
pthread_cond_broadcast(&ctx->wakeup);
return ret;
@@ -115,9 +115,9 @@ static struct mp_image *frame_queue_pop(struct mpv_opengl_cb_context *ctx)
static void frame_queue_drop(struct mpv_opengl_cb_context *ctx)
{
- struct mp_image *mpi = frame_queue_pop(ctx);
- if (mpi) {
- talloc_free(mpi);
+ struct vo_frame *frame = frame_queue_pop(ctx);
+ if (frame) {
+ talloc_free(frame);
if (ctx->active)
vo_increment_drop_count(ctx->active, 1);
pthread_cond_broadcast(&ctx->wakeup);
@@ -143,9 +143,10 @@ static void frame_queue_drop_all(struct mpv_opengl_cb_context *ctx)
pthread_cond_broadcast(&ctx->wakeup);
}
-static void frame_queue_push(struct mpv_opengl_cb_context *ctx, struct mp_image *mpi)
+static void frame_queue_push(struct mpv_opengl_cb_context *ctx,
+ struct vo_frame *frame)
{
- MP_TARRAY_APPEND(ctx, ctx->frame_queue, ctx->queued_frames, mpi);
+ MP_TARRAY_APPEND(ctx, ctx->frame_queue, ctx->queued_frames, frame);
pthread_cond_broadcast(&ctx->wakeup);
}
@@ -156,11 +157,16 @@ static void frame_queue_shrink(struct mpv_opengl_cb_context *ctx, int size)
frame_queue_drop(ctx);
}
-static void forget_frames(struct mpv_opengl_cb_context *ctx)
+static void forget_frames(struct mpv_opengl_cb_context *ctx, bool all)
{
pthread_cond_broadcast(&ctx->wakeup);
frame_queue_clear(ctx);
- mp_image_unrefp(&ctx->waiting_frame);
+ talloc_free(ctx->waiting_frame);
+ ctx->waiting_frame = NULL;
+ if (all) {
+ talloc_free(ctx->cur_frame);
+ ctx->cur_frame = NULL;
+ }
}
static void free_ctx(void *ptr)
@@ -261,7 +267,7 @@ int mpv_opengl_cb_uninit_gl(struct mpv_opengl_cb_context *ctx)
// context. Setting initialized=false guarantees it can't come back.
pthread_mutex_lock(&ctx->lock);
- forget_frames(ctx);
+ forget_frames(ctx, true);
ctx->initialized = false;
pthread_mutex_unlock(&ctx->lock);
@@ -352,30 +358,31 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
ctx->eq_changed = false;
ctx->eq = *eq;
- struct mp_image *mpi = frame_queue_pop(ctx);
- if (mpi) {
- struct frame_timing *t = mpi->priv; // set by draw_image_timed
- if (t)
- ctx->cur_pts = t->pts;
+ struct vo_frame *frame = frame_queue_pop(ctx);
+ if (frame) {
+ talloc_free(ctx->cur_frame);
+ ctx->cur_frame = vo_frame_ref(frame);
+ } else {
+ frame = vo_frame_ref(ctx->cur_frame);
}
+ struct vo_frame dummy = {0};
+ if (!frame)
+ frame = &dummy;
- struct frame_timing timing = {
- .pts = ctx->cur_pts,
- };
if (ctx->approx_vsync > 0) {
- timing.prev_vsync = prev_sync(ctx, mp_time_us());
- timing.next_vsync = timing.prev_vsync + ctx->approx_vsync;
+ frame->prev_vsync = prev_sync(ctx, mp_time_us());
+ frame->next_vsync = frame->prev_vsync + ctx->approx_vsync;
}
pthread_mutex_unlock(&ctx->lock);
- if (mpi)
-