summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-07-01 19:24:28 +0200
committerwm4 <wm4@nowhere>2015-07-01 22:38:02 +0200
commit0739cfc20934ac7772ab71dbae7ecba4ba10fda4 (patch)
tree939c829441af634577d71fbdf99a60314aa3ab42
parentf166d1298545154618ee2d046bb3c433469469c2 (diff)
downloadmpv-0739cfc20934ac7772ab71dbae7ecba4ba10fda4.tar.bz2
mpv-0739cfc20934ac7772ab71dbae7ecba4ba10fda4.tar.xz
vo: change internal API for drawing frames
draw_image_timed is renamed to draw_frame. struct frame_timing is renamed to vo_frame. flip_page_timed is merged into draw_frame (the additional parameters are part of struct vo_frame). draw_frame also deprecates VOCTRL_REDRAW_FRAME, and replaces it with a method that works for both VOs which can cache the current frame, and VOs which need to redraw it anyway. This is preparation to making the interpolation and (work in progress) display sync code saner. Lots of other refactoring, and also some simplifications.
-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;
+ }
}