summaryrefslogtreecommitdiffstats
path: root/video/out/vo.c
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 /video/out/vo.c
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.
Diffstat (limited to 'video/out/vo.c')
-rw-r--r--video/out/vo.c225
1 files changed, 113 insertions, 112 deletions
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