Fix vf_tcdump's compilation
[mplayer/kovensky.git] / libvo / vo_vdpau.c
blob1cacae994d89ea447fa78b80e0accbfb26095007
1 /*
2 * VDPAU video output driver
4 * Copyright (C) 2008 NVIDIA
5 * Copyright (C) 2009 Uoti Urpala
7 * This file is part of MPlayer.
9 * MPlayer is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * MPlayer is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * Actual decoding and presentation are implemented here.
26 * All necessary frame information is collected through
27 * the "vdpau_render_state" structure after parsing all headers
28 * etc. in libavcodec for different codecs.
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <stdbool.h>
35 #include <limits.h>
37 #include "config.h"
38 #include "mp_msg.h"
39 #include "options.h"
40 #include "talloc.h"
41 #include "video_out.h"
42 #include "x11_common.h"
43 #include "aspect.h"
44 #include "sub.h"
45 #include "subopt-helper.h"
46 #include "libmpcodecs/vfcap.h"
47 #include "libmpcodecs/mp_image.h"
48 #include "osdep/timer.h"
50 #include "libavcodec/vdpau.h"
52 #include "font_load.h"
54 #include "libavutil/common.h"
55 #include "libavutil/mathematics.h"
57 #include "ass_mp.h"
59 #define CHECK_ST_ERROR(message) \
60 do { \
61 if (vdp_st != VDP_STATUS_OK) { \
62 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] %s: %s\n", \
63 message, vdp->get_error_string(vdp_st)); \
64 return -1; \
65 } \
66 } while (0)
68 #define CHECK_ST_WARNING(message) \
69 do { \
70 if (vdp_st != VDP_STATUS_OK) \
71 mp_msg(MSGT_VO, MSGL_WARN, "[ vdpau] %s: %s\n", \
72 message, vdp->get_error_string(vdp_st)); \
73 } while (0)
75 /* number of video and output surfaces */
76 #define NUM_OUTPUT_SURFACES 3
77 #define MAX_VIDEO_SURFACES 50
78 #define NUM_BUFFERED_VIDEO 4
80 /* number of palette entries */
81 #define PALETTE_SIZE 256
83 /* Initial size of EOSD surface in pixels (x*x) */
84 #define EOSD_SURFACE_INITIAL_SIZE 256
87 * Global variable declaration - VDPAU specific
90 struct vdp_functions {
91 #define VDP_FUNCTION(vdp_type, _, mp_name) vdp_type *mp_name;
92 #include "vdpau_template.c"
93 #undef VDP_FUNCTION
96 struct vdpctx {
97 struct vdp_functions *vdp;
99 VdpDevice vdp_device;
100 bool is_preempted;
101 bool preemption_acked;
102 bool preemption_user_notified;
103 unsigned int last_preemption_retry_fail;
104 VdpGetProcAddress *vdp_get_proc_address;
106 VdpPresentationQueueTarget flip_target;
107 VdpPresentationQueue flip_queue;
108 uint64_t last_vdp_time;
109 unsigned int last_sync_update;
111 /* output_surfaces[NUM_OUTPUT_SURFACES] is misused for OSD. */
112 #define osd_surface vc->output_surfaces[NUM_OUTPUT_SURFACES]
113 VdpOutputSurface output_surfaces[NUM_OUTPUT_SURFACES + 1];
114 struct buffered_video_surface {
115 VdpVideoSurface surface;
116 double pts;
117 mp_image_t *mpi;
118 } buffered_video[NUM_BUFFERED_VIDEO];
119 int deint_queue_pos;
120 int output_surface_width, output_surface_height;
122 VdpVideoMixer video_mixer;
123 int user_colorspace;
124 int colorspace;
125 int deint;
126 int deint_type;
127 int deint_counter;
128 int pullup;
129 float denoise;
130 float sharpen;
131 int hqscaling;
132 int chroma_deint;
133 int flip_offset_window;
134 int flip_offset_fs;
135 int top_field_first;
136 bool flip;
138 VdpDecoder decoder;
139 int decoder_max_refs;
141 VdpRect src_rect_vid;
142 VdpRect out_rect_vid;
143 int border_x, border_y;
145 struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES];
146 int surface_num;
147 VdpTime recent_vsync_time;
148 float user_fps;
149 unsigned int vsync_interval;
150 uint64_t last_queue_time;
151 uint64_t last_ideal_time;
152 bool dropped_frame;
153 uint64_t dropped_time;
154 uint32_t vid_width, vid_height;
155 uint32_t image_format;
156 VdpChromaType vdp_chroma_type;
157 VdpYCbCrFormat vdp_pixel_format;
159 /* draw_osd */
160 unsigned char *index_data;
161 int index_data_size;
162 uint32_t palette[PALETTE_SIZE];
164 // EOSD
165 // Pool of surfaces
166 struct eosd_bitmap_surface {
167 VdpBitmapSurface surface;
168 int w;
169 int h;
170 uint32_t max_width;
171 uint32_t max_height;
172 } eosd_surface;
174 // List of surfaces to be rendered
175 struct eosd_target {
176 VdpRect source;
177 VdpRect dest;
178 VdpColor color;
179 } *eosd_targets;
180 int eosd_targets_size;
181 int *eosd_scratch;
183 int eosd_render_count;
185 // Video equalizer
186 VdpProcamp procamp;
188 int num_shown_frames;
189 bool paused;
191 // These tell what's been initialized and uninit() should free/uninitialize
192 bool mode_switched;
195 static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t)
197 struct vdp_functions *vdp = vc->vdp;
198 VdpStatus vdp_st;
199 VdpTime vdp_time;
200 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
201 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
202 unsigned int t1 = *t;
203 unsigned int t2 = GetTimer();
204 uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL;
205 if (vdp_time > old)
206 if (vdp_time > old + (t2 - t1) * 1000ULL)
207 vdp_time -= (t2 - t1) * 1000ULL;
208 else
209 vdp_time = old;
210 mp_msg(MSGT_VO, MSGL_V, "[vdpau] adjusting VdpTime offset by %f µs\n",
211 (int64_t)(vdp_time - old) / 1000.);
212 vc->last_vdp_time = vdp_time;
213 vc->last_sync_update = t1;
214 *t = t2;
215 return 0;
218 static uint64_t sync_vdptime(struct vo *vo)
220 struct vdpctx *vc = vo->priv;
222 unsigned int t = GetTimer();
223 if (t - vc->last_sync_update > 5000000)
224 change_vdptime_sync(vc, &t);
225 uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time;
226 // Make sure nanosecond inaccuracies don't make things inconsistent
227 now = FFMAX(now, vc->recent_vsync_time);
228 return now;
231 static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t)
233 struct vdpctx *vc = vo->priv;
234 return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time;
237 static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration);
239 static int video_to_output_surface(struct vo *vo)
241 struct vdpctx *vc = vo->priv;
242 struct vdp_functions *vdp = vc->vdp;
243 VdpTime dummy;
244 VdpStatus vdp_st;
245 if (vc->deint_queue_pos < 0)
246 return -1;
248 struct buffered_video_surface *bv = vc->buffered_video;
249 int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
250 unsigned int dp = vc->deint_queue_pos;
251 // dp==0 means last field of latest frame, 1 earlier field of latest frame,
252 // 2 last field of previous frame and so on
253 if (vc->deint) {
254 field = vc->top_field_first ^ (dp & 1) ?
255 VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
256 VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
258 const VdpVideoSurface *past_fields = (const VdpVideoSurface []){
259 bv[(dp+1)/2].surface, bv[(dp+2)/2].surface};
260 const VdpVideoSurface *future_fields = (const VdpVideoSurface []){
261 dp >= 1 ? bv[(dp-1)/2].surface : VDP_INVALID_HANDLE};
262 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
263 vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
264 output_surface,
265 &dummy);
266 CHECK_ST_WARNING("Error when calling "
267 "vdp_presentation_queue_block_until_surface_idle");
269 vdp_st = vdp->video_mixer_render(vc->video_mixer, VDP_INVALID_HANDLE,
270 0, field, 2, past_fields,
271 bv[dp/2].surface, 1, future_fields,
272 &vc->src_rect_vid, output_surface,
273 NULL, &vc->out_rect_vid, 0, NULL);
274 CHECK_ST_WARNING("Error when calling vdp_video_mixer_render");
275 return 0;
278 static void get_buffered_frame(struct vo *vo, bool eof)
280 struct vdpctx *vc = vo->priv;
282 int dqp = vc->deint_queue_pos;
283 if (dqp < 0)
284 dqp += 1000;
285 else
286 dqp = vc->deint >= 2 ? dqp - 1 : dqp - 2 | 1;
287 if (dqp < (eof ? 0 : 3))
288 return;
290 dqp = FFMIN(dqp, 4);
291 vc->deint_queue_pos = dqp;
292 vo->frame_loaded = true;
294 // Set pts values
295 struct buffered_video_surface *bv = vc->buffered_video;
296 int idx = vc->deint_queue_pos >> 1;
297 if (idx == 0) { // no future frame/pts available
298 vo->next_pts = bv[0].pts;
299 vo->next_pts2 = MP_NOPTS_VALUE;
300 } else if (!(vc->deint >= 2)) { // no field-splitting deinterlace
301 vo->next_pts = bv[idx].pts;
302 vo->next_pts2 = bv[idx - 1].pts;
303 } else { // deinterlace with separate fields
304 double intermediate_pts;
305 double diff = bv[idx - 1].pts - bv[idx].pts;
306 if (diff > 0 && diff < 0.5)
307 intermediate_pts = (bv[idx].pts + bv[idx - 1].pts) / 2;
308 else
309 intermediate_pts = bv[idx].pts;
310 if (vc->deint_queue_pos & 1) { // first field
311 vo->next_pts = bv[idx].pts;
312 vo->next_pts2 = intermediate_pts;
313 } else {
314 vo->next_pts = intermediate_pts;
315 vo->next_pts2 = bv[idx - 1].pts;
319 video_to_output_surface(vo);
322 static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
323 struct mp_image *reserved_mpi, double pts)
325 struct vdpctx *vc = vo->priv;
326 struct buffered_video_surface *bv = vc->buffered_video;
328 if (reserved_mpi)
329 reserved_mpi->usage_count++;
330 if (bv[NUM_BUFFERED_VIDEO - 1].mpi)
331 bv[NUM_BUFFERED_VIDEO - 1].mpi->usage_count--;
333 for (int i = NUM_BUFFERED_VIDEO - 1; i > 0; i--)
334 bv[i] = bv[i - 1];
335 bv[0] = (struct buffered_video_surface){
336 .mpi = reserved_mpi,
337 .surface = surface,
338 .pts = pts,
341 vc->deint_queue_pos += 2;
342 get_buffered_frame(vo, false);
345 static void forget_frames(struct vo *vo)
347 struct vdpctx *vc = vo->priv;
349 vc->deint_queue_pos = -1001;
350 vc->dropped_frame = false;
351 for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) {
352 struct buffered_video_surface *p = vc->buffered_video + i;
353 if (p->mpi)
354 p->mpi->usage_count--;
355 *p = (struct buffered_video_surface){
356 .surface = VDP_INVALID_HANDLE,
361 static void resize(struct vo *vo)
363 struct vdpctx *vc = vo->priv;
364 struct vdp_functions *vdp = vc->vdp;
365 VdpStatus vdp_st;
366 int i;
367 struct vo_rect src_rect;
368 struct vo_rect dst_rect;
369 struct vo_rect borders;
370 calc_src_dst_rects(vo, vc->vid_width, vc->vid_height, &src_rect, &dst_rect,
371 &borders, NULL);
372 vc->out_rect_vid.x0 = dst_rect.left;
373 vc->out_rect_vid.x1 = dst_rect.right;
374 vc->out_rect_vid.y0 = dst_rect.top;
375 vc->out_rect_vid.y1 = dst_rect.bottom;
376 vc->src_rect_vid.x0 = src_rect.left;
377 vc->src_rect_vid.x1 = src_rect.right;
378 vc->src_rect_vid.y0 = vc->flip ? src_rect.bottom : src_rect.top;
379 vc->src_rect_vid.y1 = vc->flip ? src_rect.top : src_rect.bottom;
380 vc->border_x = borders.left;
381 vc->border_y = borders.top;
382 #ifdef CONFIG_FREETYPE
383 // adjust font size to display size
384 force_load_font = 1;
385 #endif
386 vo_osd_changed(OSDTYPE_OSD);
387 int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window;
388 vo->flip_queue_offset = flip_offset_ms / 1000.;
390 bool had_frames = vc->num_shown_frames;
391 if (vc->output_surface_width < vo->dwidth
392 || vc->output_surface_height < vo->dheight) {
393 if (vc->output_surface_width < vo->dwidth) {
394 vc->output_surface_width += vc->output_surface_width >> 1;
395 vc->output_surface_width = FFMAX(vc->output_surface_width,
396 vo->dwidth);
398 if (vc->output_surface_height < vo->dheight) {
399 vc->output_surface_height += vc->output_surface_height >> 1;
400 vc->output_surface_height = FFMAX(vc->output_surface_height,
401 vo->dheight);
403 // Creation of output_surfaces
404 for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) {
405 if (vc->output_surfaces[i] != VDP_INVALID_HANDLE)
406 vdp->output_surface_destroy(vc->output_surfaces[i]);
407 vdp_st = vdp->output_surface_create(vc->vdp_device,
408 VDP_RGBA_FORMAT_B8G8R8A8,
409 vc->output_surface_width,
410 vc->output_surface_height,
411 &vc->output_surfaces[i]);
412 CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
413 mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n",
414 vc->output_surfaces[i]);
416 vc->num_shown_frames = 0;
418 if (vc->paused && had_frames)
419 if (video_to_output_surface(vo) >= 0)
420 flip_page_timed(vo, 0, -1);
423 static void preemption_callback(VdpDevice device, void *context)
425 struct vdpctx *vc = context;
426 vc->is_preempted = true;
427 vc->preemption_acked = false;
430 static void preemption_callback(VdpDevice device, void *context)
432 struct vdpctx *vc = context;
433 vc->is_preempted = true;
434 vc->preemption_acked = false;
437 /* Initialize vdp_get_proc_address, called from preinit() */
438 static int win_x11_init_vdpau_procs(struct vo *vo)
440 struct vo_x11_state *x11 = vo->x11;
441 struct vdpctx *vc = vo->priv;
442 talloc_free(vc->vdp); // In case this is reinitialization after preemption
443 struct vdp_functions *vdp = talloc_zero(vc, struct vdp_functions);
444 vc->vdp = vdp;
445 VdpStatus vdp_st;
447 struct vdp_function {
448 const int id;
449 int offset;
452 const struct vdp_function *dsc;
454 static const struct vdp_function vdp_func[] = {
455 #define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)},
456 #include "vdpau_template.c"
457 #undef VDP_FUNCTION
458 {0, -1}
461 vdp_st = vdp_device_create_x11(x11->display, x11->screen,&vc->vdp_device,
462 &vc->vdp_get_proc_address);
463 if (vdp_st != VDP_STATUS_OK) {
464 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
465 "vdp_device_create_x11: %i\n", vdp_st);
466 return -1;
469 vdp->get_error_string = NULL;
470 for (dsc = vdp_func; dsc->offset >= 0; dsc++) {
471 vdp_st = vc->vdp_get_proc_address(vc->vdp_device, dsc->id,
472 (void **)((char *)vdp + dsc->offset));
473 if (vdp_st != VDP_STATUS_OK) {
474 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
475 "vdp_get_proc_address(function id %d): %s\n", dsc->id,
476 vdp->get_error_string ? vdp->get_error_string(vdp_st) : "?");
477 return -1;
480 vdp_st = vdp->preemption_callback_register(vc->vdp_device,
481 preemption_callback, vc);
482 return 0;
485 static int win_x11_init_vdpau_flip_queue(struct vo *vo)
487 struct vdpctx *vc = vo->priv;
488 struct vdp_functions *vdp = vc->vdp;
489 struct vo_x11_state *x11 = vo->x11;
490 VdpStatus vdp_st;
492 if (vc->flip_target == VDP_INVALID_HANDLE) {
493 vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device,
494 x11->window,
495 &vc->flip_target);
496 CHECK_ST_ERROR("Error when calling "
497 "vdp_presentation_queue_target_create_x11");
500 /* Emperically this seems to be the first call which fails when we
501 * try to reinit after preemption while the user is still switched
502 * from X to a virtual terminal (creating the vdp_device initially
503 * succeeds, as does creating the flip_target above). This is
504 * probably not guaranteed behavior, but we'll assume it as a simple
505 * way to reduce warnings while trying to recover from preemption.
507 if (vc->flip_queue == VDP_INVALID_HANDLE) {
508 vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target,
509 &vc->flip_queue);
510 if (vc->is_preempted && vdp_st != VDP_STATUS_OK) {
511 mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Failed to create flip queue "
512 "while preempted: %s\n", vdp->get_error_string(vdp_st));
513 return -1;
514 } else
515 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create");
518 VdpTime vdp_time;
519 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
520 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
521 vc->last_vdp_time = vdp_time;
522 vc->last_sync_update = GetTimer();
524 vc->vsync_interval = 1;
525 if (vc->user_fps > 0) {
526 vc->vsync_interval = 1e9 / vc->user_fps;
527 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Assuming user-specified display "
528 "refresh rate of %.3f Hz.\n", vc->user_fps);
529 } else if (vc->user_fps == 0) {
530 #ifdef CONFIG_XF86VM
531 double fps = vo_vm_get_fps(vo);
532 if (!fps)
533 mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Failed to get display FPS\n");
534 else {
535 vc->vsync_interval = 1e9 / fps;
536 // This is verbose, but I'm not yet sure how common wrong values are
537 mp_msg(MSGT_VO, MSGL_INFO,
538 "[vdpau] Got display refresh rate %.3f Hz.\n"
539 "[vdpau] If that value looks wrong give the "
540 "-vo vdpau:fps=X suboption manually.\n", fps);
542 #else
543 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] This binary has been compiled "
544 "without XF86VidMode support.\n");
545 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Can't use vsync-aware timing "
546 "without manually provided -vo vdpau:fps=X suboption.\n");
547 #endif
548 } else
549 mp_msg(MSGT_VO, MSGL_V, "[vdpau] framedrop/timing logic disabled by "
550 "user.\n");
552 return 0;
555 static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr,
556 const void *value, char *attr_name)
558 struct vdp_functions *vdp = vc->vdp;
559 VdpStatus vdp_st;
561 vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1, &attr,
562 &value);
563 if (vdp_st != VDP_STATUS_OK) {
564 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error setting video mixer "
565 "attribute %s: %s\n", attr_name, vdp->get_error_string(vdp_st));
566 return -1;
568 return 0;
571 static void update_csc_matrix(struct vo *vo)
573 struct vdpctx *vc = vo->priv;
574 struct vdp_functions *vdp = vc->vdp;
575 VdpStatus vdp_st;
577 const VdpColorStandard vdp_colors[] = {VDP_COLOR_STANDARD_ITUR_BT_601,
578 VDP_COLOR_STANDARD_ITUR_BT_709,
579 VDP_COLOR_STANDARD_SMPTE_240M};
580 char * const vdp_names[] = {"BT.601", "BT.709", "SMPTE-240M"};
581 int csp = vc->colorspace;
582 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix for %s\n",
583 vdp_names[csp]);
585 VdpCSCMatrix matrix;
586 vdp_st = vdp->generate_csc_matrix(&vc->procamp, vdp_colors[csp], &matrix);
587 CHECK_ST_WARNING("Error when generating CSC matrix");
589 set_video_attribute(vc, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX,
590 &matrix, "CSC matrix");
593 #define SET_VIDEO_ATTR(attr_name, attr_type, value) set_video_attribute(vc, \
594 VDP_VIDEO_MIXER_ATTRIBUTE_ ## attr_name, &(attr_type){value},\
595 # attr_name)
596 static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
598 struct vdpctx *vc = vo->priv;
599 struct vdp_functions *vdp = vc->vdp;
600 #define VDP_NUM_MIXER_PARAMETER 3
601 #define MAX_NUM_FEATURES 6
602 int i;
603 VdpStatus vdp_st;
605 if (vc->video_mixer != VDP_INVALID_HANDLE)
606 return 0;
608 int feature_count = 0;
609 VdpVideoMixerFeature features[MAX_NUM_FEATURES];
610 VdpBool feature_enables[MAX_NUM_FEATURES];
611 static const VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
612 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
613 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
614 VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
616 const void *const parameter_values[VDP_NUM_MIXER_PARAMETER] = {
617 &vc->vid_width,
618 &vc->vid_height,
619 &vdp_chroma_type,
621 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
622 if (vc->deint == 4)
623 features[feature_count++] =
624 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
625 if (vc->pullup)
626 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
627 if (vc->denoise)
628 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
629 if (vc->sharpen)
630 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
631 if (vc->hqscaling) {
632 #ifndef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
633 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] MPlayer was compiled with (old?) "
634 "libvdpau headers with no support for requested hqscaling.\n");
635 #else
636 VdpVideoMixerFeature hqscaling_feature =
637 VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + vc->hqscaling-1;
638 VdpBool hqscaling_available;
639 vdp_st = vdp->video_mixer_query_feature_support(vc->vdp_device,
640 hqscaling_feature,
641 &hqscaling_available);
642 CHECK_ST_ERROR("Error when calling video_mixer_query_feature_support");
643 if (hqscaling_available)
644 features[feature_count++] = hqscaling_feature;
645 else
646 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Your hardware or VDPAU "
647 "library does not support requested hqscaling.\n");
648 #endif
651 vdp_st = vdp->video_mixer_create(vc->vdp_device, feature_count, features,
652 VDP_NUM_MIXER_PARAMETER,
653 parameters, parameter_values,
654 &vc->video_mixer);
655 CHECK_ST_ERROR("Error when calling vdp_video_mixer_create");
657 for (i = 0; i < feature_count; i++)
658 feature_enables[i] = VDP_TRUE;
659 if (vc->deint < 3)
660 feature_enables[0] = VDP_FALSE;
661 if (feature_count) {
662 vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
663 feature_count, features,
664 feature_enables);
665 CHECK_ST_WARNING("Error calling vdp_video_mixer_set_feature_enables");
667 if (vc->denoise)
668 SET_VIDEO_ATTR(NOISE_REDUCTION_LEVEL, float, vc->denoise);
669 if (vc->sharpen)
670 SET_VIDEO_ATTR(SHARPNESS_LEVEL, float, vc->sharpen);
671 if (!vc->chroma_deint)
672 SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
674 update_csc_matrix(vo);
675 return 0;
678 // Free everything specific to a certain video file
679 static void free_video_specific(struct vo *vo)
681 struct vdpctx *vc = vo->priv;
682 struct vdp_functions *vdp = vc->vdp;
683 int i;
684 VdpStatus vdp_st;
686 if (vc->decoder != VDP_INVALID_HANDLE)
687 vdp->decoder_destroy(vc->decoder);
688 vc->decoder = VDP_INVALID_HANDLE;
689 vc->decoder_max_refs = -1;
691 forget_frames(vo);
693 for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
694 if (vc->surface_render[i].surface != VDP_INVALID_HANDLE) {
695 vdp_st = vdp->video_surface_destroy(vc->surface_render[i].surface);
696 CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy");
698 vc->surface_render[i].surface = VDP_INVALID_HANDLE;
701 if (vc->video_mixer != VDP_INVALID_HANDLE) {
702 vdp_st = vdp->video_mixer_destroy(vc->video_mixer);
703 CHECK_ST_WARNING("Error when calling vdp_video_mixer_destroy");
705 vc->video_mixer = VDP_INVALID_HANDLE;
708 static int create_vdp_decoder(struct vo *vo, int max_refs)
710 struct vdpctx *vc = vo->priv;
711 struct vdp_functions *vdp = vc->vdp;
712 VdpStatus vdp_st;
713 VdpDecoderProfile vdp_decoder_profile;
714 if (vc->decoder != VDP_INVALID_HANDLE)
715 vdp->decoder_destroy(vc->decoder);
716 switch (vc->image_format) {
717 case IMGFMT_VDPAU_MPEG1:
718 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
719 break;
720 case IMGFMT_VDPAU_MPEG2:
721 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
722 break;
723 case IMGFMT_VDPAU_H264:
724 vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
725 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder "
726 "for %d reference frames.\n", max_refs);
727 break;
728 case IMGFMT_VDPAU_WMV3:
729 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
730 break;
731 case IMGFMT_VDPAU_VC1:
732 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
733 break;
734 #ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
735 case IMGFMT_VDPAU_MPEG4:
736 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
737 break;
738 #endif
740 vdp_st = vdp->decoder_create(vc->vdp_device, vdp_decoder_profile,
741 vc->vid_width, vc->vid_height, max_refs,
742 &vc->decoder);
743 CHECK_ST_WARNING("Failed creating VDPAU decoder");
744 if (vdp_st != VDP_STATUS_OK) {
745 vc->decoder = VDP_INVALID_HANDLE;
746 vc->decoder_max_refs = 0;
747 return 0;
749 vc->decoder_max_refs = max_refs;
750 return 1;
753 static int initialize_vdpau_objects(struct vo *vo)
755 struct vdpctx *vc = vo->priv;
756 struct vdp_functions *vdp = vc->vdp;
757 VdpStatus vdp_st;
759 vc->vdp_chroma_type = VDP_CHROMA_TYPE_420;
760 switch (vc->image_format) {
761 case IMGFMT_YV12:
762 case IMGFMT_I420:
763 case IMGFMT_IYUV:
764 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YV12;
765 break;
766 case IMGFMT_NV12:
767 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_NV12;
768 break;
769 case IMGFMT_YUY2:
770 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YUYV;
771 vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
772 break;
773 case IMGFMT_UYVY:
774 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_UYVY;
775 vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
777 if (win_x11_init_vdpau_flip_queue(vo) < 0)
778 return -1;
780 if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
781 return -1;
783 vdp_st = vdp->
784 bitmap_surface_query_capabilities(vc->vdp_device,
785 VDP_RGBA_FORMAT_A8,
786 &(VdpBool){0},
787 &vc->eosd_surface.max_width,
788 &vc->eosd_surface.max_height);
789 CHECK_ST_WARNING("Query to get max EOSD surface size failed");
790 forget_frames(vo);
791 resize(vo);
792 return 0;
795 static void mark_vdpau_objects_uninitialized(struct vo *vo)
797 struct vdpctx *vc = vo->priv;
799 vc->decoder = VDP_INVALID_HANDLE;
800 for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
801 vc->surface_render[i].surface = VDP_INVALID_HANDLE;
802 forget_frames(vo);
803 vc->video_mixer = VDP_INVALID_HANDLE;
804 vc->flip_queue = VDP_INVALID_HANDLE;
805 vc->flip_target = VDP_INVALID_HANDLE;
806 for (int i = 0; i <= NUM_OUTPUT_SURFACES; i++)
807 vc->output_surfaces[i] = VDP_INVALID_HANDLE;
808 vc->vdp_device = VDP_INVALID_HANDLE;
809 vc->eosd_surface = (struct eosd_bitmap_surface){
810 .surface = VDP_INVALID_HANDLE,
812 vc->output_surface_width = vc->output_surface_height = -1;
813 vc->eosd_render_count = 0;
814 vc->num_shown_frames = 0;
817 static int handle_preemption(struct vo *vo)
819 struct vdpctx *vc = vo->priv;
821 if (!vc->is_preempted)
822 return 0;
823 if (!vc->preemption_acked)
824 mark_vdpau_objects_uninitialized(vo);
825 vc->preemption_acked = true;
826 if (!vc->preemption_user_notified) {
827 mp_tmsg(MSGT_VO, MSGL_ERR, "[vdpau] Got display preemption notice! "
828 "Will attempt to recover.\n");
829 vc->preemption_user_notified = true;
831 /* Trying to initialize seems to be quite slow, so only try once a
832 * second to avoid using 100% CPU. */
833 if (vc->last_preemption_retry_fail
834 && GetTimerMS() - vc->last_preemption_retry_fail < 1000)
835 return -1;
836 if (win_x11_init_vdpau_procs(vo) < 0 || initialize_vdpau_objects(vo) < 0) {
837 vc->last_preemption_retry_fail = GetTimerMS() | 1;
838 return -1;
840 vc->last_preemption_retry_fail = 0;
841 vc->is_preempted = false;
842 vc->preemption_user_notified = false;
843 mp_tmsg(MSGT_VO, MSGL_INFO, "[vdpau] Recovered from display preemption.\n");
844 return 1;
848 * connect to X server, create and map window, initialize all
849 * VDPAU objects, create different surfaces etc.
851 static int config(struct vo *vo, uint32_t width, uint32_t height,
852 uint32_t d_width, uint32_t d_height, uint32_t flags,
853 char *title, uint32_t format)
855 struct vdpctx *vc = vo->priv;
856 struct vo_x11_state *x11 = vo->x11;
857 XVisualInfo vinfo;
858 XSetWindowAttributes xswa;
859 XWindowAttributes attribs;
860 unsigned long xswamask;
861 int depth;
863 #ifdef CONFIG_XF86VM
864 int vm = flags & VOFLAG_MODESWITCHING;
865 #endif
867 if (handle_preemption(vo) < 0)
868 return -1;
870 vc->flip = flags & VOFLAG_FLIPPING;
871 vc->image_format = format;
872 vc->vid_width = width;
873 vc->vid_height = height;
874 if (vc->user_colorspace == 0)
875 vc->colorspace = width >= 1280 || height > 576 ? 1 : 0;
876 else
877 vc->colorspace = vc->user_colorspace - 1;
878 free_video_specific(vo);
879 if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
880 return -1;
882 #ifdef CONFIG_XF86VM
883 if (vm) {
884 vo_vm_switch(vo);
885 vc->mode_switched = true;
887 #endif
888 XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
889 &attribs);
890 depth = attribs.depth;
891 if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
892 depth = 24;
893 XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
895 xswa.background_pixel = 0;
896 xswa.border_pixel = 0;
897 /* Do not use CWBackPixel: It leads to VDPAU errors after
898 * aspect ratio changes. */
899 xswamask = CWBorderPixel;
901 vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, d_width, d_height,
902 flags, CopyFromParent, "vdpau", title);
903 XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
905 #ifdef CONFIG_XF86VM
906 if (vm) {
907 /* Grab the mouse pointer in our window */
908 if (vo_grabpointer)
909 XGrabPointer(x11->display, x11->window, True, 0,
910 GrabModeAsync, GrabModeAsync,
911 x11->window, None, CurrentTime);
912 XSetInputFocus(x11->display, x11->window, RevertToNone, CurrentTime);
914 #endif
916 if ((flags & VOFLAG_FULLSCREEN) && WinID <= 0)
917 vo_fs = 1;
919 if (initialize_vdpau_objects(vo) < 0)
920 return -1;
922 return 0;
925 static void check_events(struct vo *vo)
927 struct vdpctx *vc = vo->priv;
928 struct vdp_functions *vdp = vc->vdp;
930 if (handle_preemption(vo) < 0)
931 return;
933 int e = vo_x11_check_events(vo);
935 if (e & VO_EVENT_RESIZE)
936 resize(vo);
937 else if (e & VO_EVENT_EXPOSE && vc->paused) {
938 /* did we already draw a buffer */
939 if (vc->num_shown_frames) {
940 /* redraw the last visible buffer */
941 VdpStatus vdp_st;
942 int last_surface = (vc->surface_num + NUM_OUTPUT_SURFACES - 1)
943 % NUM_OUTPUT_SURFACES;
944 vdp_st = vdp->presentation_queue_display(vc->flip_queue,
945 vc->output_surfaces[last_surface],
946 vo->dwidth, vo->dheight, 0);
947 CHECK_ST_WARNING("Error when calling "
948 "vdp_presentation_queue_display");
953 static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h,
954 unsigned char *src, unsigned char *srca, int stride)
956 struct vo *vo = ctx;
957 struct vdpctx *vc = vo->priv;
958 struct vdp_functions *vdp = vc->vdp;
959 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
960 VdpStatus vdp_st;
961 int i;
962 int pitch;
963 int index_data_size_required;
964 VdpRect output_indexed_rect_vid;
966 if (!w || !h)
967 return;
969 index_data_size_required = 2*w*h;
970 if (vc->index_data_size < index_data_size_required) {
971 vc->index_data = talloc_realloc_size(vc, vc->index_data,
972 index_data_size_required);
973 vc->index_data_size = index_data_size_required;
976 // index_data creation, component order - I, A, I, A, .....
977 for (i = 0; i < h; i++)
978 for (int j = 0; j < w; j++) {
979 vc->index_data[i*2*w + j*2] = src [i*stride+j];
980 vc->index_data[i*2*w + j*2 + 1] = -srca[i*stride+j];
983 output_indexed_rect_vid.x0 = x0;
984 output_indexed_rect_vid.y0 = y0;
985 output_indexed_rect_vid.x1 = x0 + w;
986 output_indexed_rect_vid.y1 = y0 + h;
988 pitch = w*2;
990 // write source_data to osd_surface.
991 vdp_st = vdp->
992 output_surface_put_bits_indexed(osd_surface, VDP_INDEXED_FORMAT_I8A8,
993 (const void *const*)&vc->index_data,
994 &pitch, &output_indexed_rect_vid,
995 VDP_COLOR_TABLE_FORMAT_B8G8R8X8,
996 (void *)vc->palette);
997 CHECK_ST_WARNING("Error when calling vdp_output_surface_put_bits_indexed");
999 VdpOutputSurfaceRenderBlendState blend_state = {
1000 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1001 .blend_factor_source_color =
1002 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1003 .blend_factor_source_alpha =
1004 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1005 .blend_factor_destination_color =
1006 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1007 .blend_factor_destination_alpha =
1008 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1009 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1010 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1013 vdp_st = vdp->
1014 output_surface_render_output_surface(output_surface,
1015 &output_indexed_rect_vid,
1016 osd_surface,
1017 &output_indexed_rect_vid,
1018 NULL, &blend_state,
1019 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1020 CHECK_ST_WARNING("Error when calling "
1021 "vdp_output_surface_render_output_surface");
1024 static void draw_eosd(struct vo *vo)
1026 struct vdpctx *vc = vo->priv;
1027 struct vdp_functions *vdp = vc->vdp;
1028 VdpStatus vdp_st;
1029 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
1030 int i;
1032 if (handle_preemption(vo) < 0)
1033 return;
1035 VdpOutputSurfaceRenderBlendState blend_state = {
1036 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1037 .blend_factor_source_color =
1038 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1039 .blend_factor_source_alpha =
1040 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1041 .blend_factor_destination_color =
1042 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1043 .blend_factor_destination_alpha =
1044 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1045 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1046 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1049 for (i = 0; i < vc->eosd_render_count; i++) {
1050 vdp_st = vdp->
1051 output_surface_render_bitmap_surface(output_surface,
1052 &vc->eosd_targets[i].dest,
1053 vc->eosd_surface.surface,
1054 &vc->eosd_targets[i].source,
1055 &vc->eosd_targets[i].color,
1056 &blend_state,
1057 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1058 CHECK_ST_WARNING("EOSD: Error when rendering");
1062 #define HEIGHT_SORT_BITS 4
1063 static int size_index(struct eosd_target *r)
1065 unsigned int h = r->source.y1;
1066 int n = av_log2_16bit(h);
1067 return (n << HEIGHT_SORT_BITS)
1068 + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
1071 /* Pack the given rectangles into an area of size w * h.
1072 * The size of each rectangle is read from .source.x1/.source.y1.
1073 * The height of each rectangle must be at least 1 and less than 65536.
1074 * The .source rectangle is then set corresponding to the packed position.
1075 * 'scratch' must point to work memory for num_rects+16 ints.
1076 * Return 0 on success, -1 if the rectangles did not fit in w*h.
1078 * The rectangles are placed in rows in order approximately sorted by
1079 * height (the approximate sorting is simpler than a full one would be,
1080 * and allows the algorithm to work in linear time). Additionally, to
1081 * reduce wasted space when there are a few tall rectangles, empty
1082 * lower-right parts of rows are filled recursively when the size of
1083 * rectangles in the row drops past a power-of-two threshold. So if a
1084 * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
1085 * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
1087 static int pack_rectangles(struct eosd_target *rects, int num_rects,
1088 int w, int h, int *scratch)
1090 int bins[16 << HEIGHT_SORT_BITS];
1091 int sizes[16 << HEIGHT_SORT_BITS] = {};
1092 for (int i = 0; i < num_rects; i++)
1093 sizes[size_index(rects + i)]++;
1094 int idx = 0;
1095 for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
1096 for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
1097 bins[i + j] = idx;
1098 idx += sizes[i + j];
1100 scratch[idx++] = -1;
1102 for (int i = 0; i < num_rects; i++)
1103 scratch[bins[size_index(rects + i)]++] = i;
1104 for (int i = 0; i < 16; i++)
1105 bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
1106 struct {
1107 int size, x, bottom;
1108 } stack[16] = {{15, 0, h}}, s = {};
1109 int stackpos = 1;
1110 int y;
1111 while (stackpos) {
1112 y = s.bottom;
1113 s = stack[--stackpos];
1114 s.size++;
1115 while (s.size--) {
1116 int maxy = -1;
1117 int obj;
1118 while ((obj = scratch[bins[s.size]]) >= 0) {
1119 int bottom = y + rects[obj].source.y1;
1120 if (bottom > s.bottom)
1121 break;
1122 int right = s.x + rects[obj].source.x1;
1123 if (right > w)
1124 break;
1125 bins[s.size]++;
1126 rects[obj].source.x0 = s.x;
1127 rects[obj].source.x1 += s.x;
1128 rects[obj].source.y0 = y;
1129 rects[obj].source.y1 += y;
1130 num_rects--;
1131 if (maxy <= 0)
1132 stack[stackpos++] = s;
1133 s.x = right;
1134 maxy = FFMAX(maxy, bottom);
1136 if (maxy > 0)
1137 s.bottom = maxy;
1140 return num_rects ? -1 : 0;
1143 static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
1145 struct vdpctx *vc = vo->priv;
1146 struct vdp_functions *vdp = vc->vdp;
1147 VdpStatus vdp_st;
1148 int i;
1149 ASS_Image *img = imgs->imgs;
1150 ASS_Image *p;
1151 struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
1152 bool need_upload = false;
1154 if (imgs->changed == 0)
1155 return; // Nothing changed, no need to redraw
1157 vc->eosd_render_count = 0;
1159 if (!img)
1160 return; // There's nothing to render!
1162 if (imgs->changed == 1)
1163 goto eosd_skip_upload;
1165 need_upload = true;
1166 bool reallocate = false;
1167 while (1) {
1168 for (p = img, i = 0; p; p = p->next) {
1169 if (p->w <= 0 || p->h <= 0)
1170 continue;
1171 // Allocate new space for surface/target arrays
1172 if (i >= vc->eosd_targets_size) {
1173 vc->eosd_targets_size = FFMAX(vc->eosd_targets_size * 2, 512);
1174 vc->eosd_targets =
1175 talloc_realloc_size(vc, vc->eosd_targets,
1176 vc->eosd_targets_size
1177 * sizeof(*vc->eosd_targets));
1178 vc->eosd_scratch =
1179 talloc_realloc_size(vc, vc->eosd_scratch,
1180 (vc->eosd_targets_size + 16)
1181 * sizeof(*vc->eosd_scratch));
1183 vc->eosd_targets[i].source.x1 = p->w;
1184 vc->eosd_targets[i].source.y1 = p->h;
1185 i++;
1187 if (pack_rectangles(vc->eosd_targets, i, sfc->w, sfc->h,
1188 vc->eosd_scratch) >= 0)
1189 break;
1190 int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
1191 sfc->max_width);
1192 int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
1193 sfc->max_height);
1194 if (w == sfc->w && h == sfc->h) {
1195 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
1196 "a surface with the maximum supported size\n");
1197 return;
1198 } else {
1199 sfc->w = w;
1200 sfc->h = h;
1202 reallocate = true;
1204 if (reallocate) {
1205 if (sfc->surface != VDP_INVALID_HANDLE)
1206 vdp->bitmap_surface_destroy(sfc->surface);
1207 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
1208 "EOSD bitmaps.\n", sfc->w, sfc->h);
1209 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
1210 sfc->w, sfc->h, true,
1211 &sfc->surface);
1212 if (vdp_st != VDP_STATUS_OK)
1213 sfc->surface = VDP_INVALID_HANDLE;
1214 CHECK_ST_WARNING("EOSD: error when creating surface");
1217 eosd_skip_upload:
1218 if (sfc->surface == VDP_INVALID_HANDLE)
1219 return;
1220 for (p = img; p; p = p->next) {
1221 if (p->w <= 0 || p->h <= 0)
1222 continue;
1223 struct eosd_target *target = &vc->eosd_targets[vc->eosd_render_count];
1224 if (need_upload) {
1225 vdp_st = vdp->
1226 bitmap_surface_put_bits_native(sfc->surface,
1227 (const void *) &p->bitmap,
1228 &p->stride, &target->source);
1229 CHECK_ST_WARNING("EOSD: putbits failed");
1231 // Render dest, color, etc.
1232 target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
1233 target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
1234 target->color.green = ((p->color >> 16) & 0xff) / 255.0;
1235 target->color.red = ((p->color >> 24) & 0xff) / 255.0;
1236 target->dest.x0 = p->dst_x;
1237 target->dest.y0 = p->dst_y;
1238 target->dest.x1 = p->w + p->dst_x;
1239 target->dest.y1 = p->h + p->dst_y;
1240 vc->eosd_render_count++;
1244 static void draw_osd(struct vo *vo, struct osd_state *osd)
1246 struct vdpctx *vc = vo->priv;
1247 mp_msg(MSGT_VO, MSGL_DBG2, "DRAW_OSD\n");
1249 if (handle_preemption(vo) < 0)
1250 return;
1252 osd_draw_text_ext(osd, vo->dwidth, vo->dheight, vc->border_x, vc->border_y,
1253 vc->border_x, vc->border_y, vc->vid_width,
1254 vc->vid_height, draw_osd_I8A8, vo);
1257 static void wait_for_previous_frame(struct vo *vo)
1259 struct vdpctx *vc = vo->priv;
1260 struct vdp_functions *vdp = vc->vdp;
1261 VdpStatus vdp_st;
1263 if (vc->num_shown_frames < 2)
1264 return;
1266 VdpTime vtime;
1267 VdpOutputSurface visible_s, prev_s;
1268 int base = vc->surface_num + NUM_OUTPUT_SURFACES;
1269 visible_s = vc->output_surfaces[(base - 1) % NUM_OUTPUT_SURFACES];
1270 prev_s = vc->output_surfaces[(base - 2) % NUM_OUTPUT_SURFACES];
1271 vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
1272 prev_s, &vtime);
1273 CHECK_ST_WARNING("Error calling "
1274 "presentation_queue_block_until_surface_idle");
1275 VdpPresentationQueueStatus status;
1276 vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
1277 visible_s,
1278 &status, &vtime);
1279 CHECK_ST_WARNING("Error calling presentation_queue_query_surface_status");
1280 vc->recent_vsync_time = vtime;
1283 static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
1285 uint64_t offset = ts - vc->recent_vsync_time;
1286 // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
1287 offset += (uint64_t)vc->vsync_interval << shift;
1288 offset %= vc->vsync_interval;
1289 return ts - offset;
1292 static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
1294 struct vdpctx *vc = vo->priv;
1295 struct vdp_functions *vdp = vc->vdp;
1296 VdpStatus vdp_st;
1297 uint32_t vsync_interval = vc->vsync_interval;
1299 if (handle_preemption(vo) < 0)
1300 return;
1302 if (duration > INT_MAX / 1000)
1303 duration = -1;
1304 else
1305 duration *= 1000;
1307 if (vc->user_fps < 0)
1308 duration = -1; // Make sure drop logic is disabled
1310 uint64_t now = sync_vdptime(vo);
1311 uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
1312 uint64_t ideal_pts = pts;
1313 uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
1315 #define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
1316 // Only gives accurate results for ts >= vc->recent_vsync_time
1317 #define PREV_VSYNC(ts) PREV_VS2(ts, 0)
1319 /* We hope to be here at least one vsync before the frame should be shown.
1320 * If we are running late then don't drop the frame unless there is
1321 * already one queued for the next vsync; even if we _hope_ to show the
1322 * next frame soon enough to mean this one should be dropped we might
1323 * not make the target time in reality. Without this check we could drop
1324 * every frame, freezing the display completely if video lags behind.
1326 if (now > PREV_VSYNC(FFMAX(pts,
1327 vc->last_queue_time + vsync_interval)))
1328 npts = UINT64_MAX;
1330 /* Allow flipping a frame at a vsync if its presentation time is a
1331 * bit after that vsync and the change makes the flip time delta
1332 * from previous frame better match the target timestamp delta.
1333 * This avoids instability with frame timestamps falling near vsyncs.
1334 * For example if the frame timestamps were (with vsyncs at
1335 * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
1336 * straightforward timing at next vsync would flip the frames at
1337 * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
1338 * regular 2-vsync intervals.
1340 * Also allow moving the frame forward if it looks like we dropped
1341 * the previous frame incorrectly (now that we know better after
1342 * having final exact timestamp information for this frame) and
1343 * there would unnecessarily be a vsync without a frame change.
1345 uint64_t vsync = PREV_VSYNC(pts);
1346 if (pts < vsync + vsync_interval / 4
1347 && (vsync - PREV_VS2(vc->last_queue_time, 16)
1348 > pts - vc->last_ideal_time + vsync_interval / 2
1349 || vc->dropped_frame && vsync > vc->dropped_time))
1350 pts -= vsync_interval / 2;
1352 vc->dropped_frame = true; // changed at end if false
1353 vc->dropped_time = ideal_pts;
1355 pts = FFMAX(pts, vc->last_queue_time + vsync_interval);
1356 pts = FFMAX(pts, now);
1357 if (npts < PREV_VSYNC(pts) + vsync_interval)
1358 return;
1360 /* At least on my NVIDIA 9500GT with driver versions 185.18.36 and 190.42
1361 * trying to queue two unshown frames simultaneously caused bad behavior
1362 * (high CPU use in _other_ VDPAU functions called later). Avoiding
1363 * longer queues also makes things simpler. So currently we always
1364 * try to keep exactly one frame queued for the future, queuing the
1365 * current frame immediately after the previous one is shown.
1367 wait_for_previous_frame(vo);
1369 now = sync_vdptime(vo);
1370 pts = FFMAX(pts, now);
1371 vsync = PREV_VSYNC(pts);
1372 if (npts < vsync + vsync_interval)
1373 return;
1374 pts = vsync + (vsync_interval >> 2);
1375 vdp_st =
1376 vdp->presentation_queue_display(vc->flip_queue,
1377 vc->output_surfaces[vc->surface_num],
1378 vo->dwidth, vo->dheight, pts);
1379 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display");
1381 vc->last_queue_time = pts;
1382 vc->last_ideal_time = ideal_pts;
1383 vc->dropped_frame = false;
1384 vc->surface_num = (vc->surface_num + 1) % NUM_OUTPUT_SURFACES;
1385 vc->num_shown_frames = FFMIN(vc->num_shown_frames + 1, 1000);
1388 static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
1389 int h, int x, int y)
1391 struct vdpctx *vc = vo->priv;
1392 struct vdp_functions *vdp = vc->vdp;
1393 VdpStatus vdp_st;
1395 if (handle_preemption(vo) < 0)
1396 return VO_TRUE;
1398 struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
1399 int max_refs = vc->image_format == IMGFMT_VDPAU_H264 ?
1400 rndr->info.h264.num_ref_frames : 2;
1401 if (!IMGFMT_IS_VDPAU(vc->image_format))
1402 return VO_FALSE;
1403 if ((vc->decoder == VDP_INVALID_HANDLE || vc->decoder_max_refs < max_refs)
1404 && !create_vdp_decoder(vo, max_refs))
1405 return VO_FALSE;
1407 vdp_st = vdp->decoder_render(vc->decoder, rndr->surface,
1408 (void *)&rndr->info,
1409 rndr->bitstream_buffers_used,
1410 rndr->bitstream_buffers);
1411 CHECK_ST_WARNING("Failed VDPAU decoder rendering");
1412 return VO_TRUE;
1416 static struct vdpau_render_state *get_surface(struct vo *vo, int number)
1418 struct vdpctx *vc = vo->priv;
1419 struct vdp_functions *vdp = vc->vdp;
1421 if (number > MAX_VIDEO_SURFACES)
1422 return NULL;
1423 if (vc->surface_render[number].surface == VDP_INVALID_HANDLE
1424 && !vc->is_preempted) {
1425 VdpStatus vdp_st;
1426 vdp_st = vdp->video_surface_create(vc->vdp_device, vc->vdp_chroma_type,
1427 vc->vid_width, vc->vid_height,
1428 &vc->surface_render[number].surface);
1429 CHECK_ST_WARNING("Error when calling vdp_video_surface_create");
1431 mp_msg(MSGT_VO, MSGL_DBG2, "VID CREATE: %u\n",
1432 vc->surface_render[number].surface);
1433 return &vc->surface_render[number];
1436 static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
1438 struct vdpctx *vc = vo->priv;
1439 struct vdp_functions *vdp = vc->vdp;
1440 struct mp_image *reserved_mpi = NULL;
1441 struct vdpau_render_state *rndr;
1443 if (vc->is_preempted) {
1444 vo->frame_loaded = true;
1445 return;
1448 if (IMGFMT_IS_VDPAU(vc->image_format)) {
1449 rndr = mpi->priv;
1450 reserved_mpi = mpi;
1451 } else if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
1452 VdpStatus vdp_st;
1453 void *destdata[3] = {mpi->planes[0], mpi->planes[2], mpi->planes[1]};
1454 rndr = get_surface(vo, vc->deint_counter);
1455 vc->deint_counter = (vc->deint_counter + 1) % NUM_BUFFERED_VIDEO;
1456 if (vc->image_format == IMGFMT_NV12)
1457 destdata[1] = destdata[2];
1458 vdp_st =
1459 vdp->video_surface_put_bits_y_cb_cr(rndr->surface,
1460 vc->vdp_pixel_format,
1461 (const void *const*)destdata,
1462 mpi->stride); // pitch
1463 CHECK_ST_WARNING("Error when calling "
1464 "vdp_video_surface_put_bits_y_cb_cr");
1465 } else
1466 // We don't support slice callbacks so this shouldn't occur -
1467 // I think the flags test above in pointless, but I'm adding
1468 // this instead of removing it just in case.
1469 abort();
1470 if (mpi->fields & MP_IMGFIELD_ORDERED)
1471 vc->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
1472 else
1473 vc->top_field_first = 1;
1475 add_new_video_surface(vo, rndr->surface, mpi, pts);
1477 return;
1480 static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
1482 struct vdpctx *vc = vo->priv;
1483 struct vdpau_render_state *rndr;
1485 // no dr for non-decoding for now
1486 if (!IMGFMT_IS_VDPAU(vc->image_format))
1487 return VO_FALSE;
1488 if (mpi->type != MP_IMGTYPE_NUMBERED)
1489 return VO_FALSE;
1491 rndr = get_surface(vo, mpi->number);
1492 if (!rndr) {
1493 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
1494 "get_image\n");
1495 // TODO: this probably breaks things forever, provide a dummy buffer?
1496 return VO_FALSE;
1498 mpi->flags |= MP_IMGFLAG_DIRECT;
1499 mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
1500 mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = NULL;
1501 // hack to get around a check and to avoid a special-case in vd_ffmpeg.c
1502 mpi->planes[0] = (void *)rndr;
1503 mpi->num_planes = 1;
1504 mpi->priv = rndr;
1505 return VO_TRUE;
1508 static int query_format(uint32_t format)
1510 int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
1511 | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD
1512 | VFCAP_EOSD_UNSCALED | VFCAP_FLIP;
1513 switch (format) {
1514 case IMGFMT_YV12:
1515 case IMGFMT_I420:
1516 case IMGFMT_IYUV:
1517 case IMGFMT_NV12:
1518 case IMGFMT_YUY2:
1519 case IMGFMT_UYVY:
1520 return default_flags | VOCAP_NOSLICES;
1521 case IMGFMT_VDPAU_MPEG1:
1522 case IMGFMT_VDPAU_MPEG2:
1523 case IMGFMT_VDPAU_H264:
1524 case IMGFMT_VDPAU_WMV3:
1525 case IMGFMT_VDPAU_VC1:
1526 case IMGFMT_VDPAU_MPEG4:
1527 return default_flags;
1529 return 0;
1532 static void destroy_vdpau_objects(struct vo *vo)
1534 struct vdpctx *vc = vo->priv;
1535 struct vdp_functions *vdp = vc->vdp;
1537 int i;
1538 VdpStatus vdp_st;
1540 free_video_specific(vo);
1542 if (vc->flip_queue != VDP_INVALID_HANDLE) {
1543 vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
1544 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy");
1547 if (vc->flip_target != VDP_INVALID_HANDLE) {
1548 vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
1549 CHECK_ST_WARNING("Error when calling "
1550 "vdp_presentation_queue_target_destroy");
1553 for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) {
1554 if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
1555 continue;
1556 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
1557 CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
1560 if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
1561 vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
1562 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1565 vdp_st = vdp->device_destroy(vc->vdp_device);
1566 CHECK_ST_WARNING("Error when calling vdp_device_destroy");
1569 static void uninit(struct vo *vo)
1571 struct vdpctx *vc = vo->priv;
1573 /* Destroy all vdpau objects */
1574 destroy_vdpau_objects(vo);
1576 #ifdef CONFIG_XF86VM
1577 if (vc->mode_switched)
1578 vo_vm_close(vo);
1579 #endif
1580 vo_x11_uninit(vo);
1582 // Free bitstream buffers allocated by FFmpeg
1583 for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
1584 av_freep(&vc->surface_render[i].bitstream_buffers);
1587 static int preinit(struct vo *vo, const char *arg)
1589 int i;
1591 struct vdpctx *vc = talloc_zero(vo, struct vdpctx);
1592 vo->priv = vc;
1594 // Mark everything as invalid first so uninit() can tell what has been
1595 // allocated
1596 mark_vdpau_objects_uninitialized(vo);
1598 vc->deint_type = 3;
1599 vc->chroma_deint = 1;
1600 vc->user_colorspace = 1;
1601 vc->flip_offset_window = 50;
1602 vc->flip_offset_fs = 50;
1603 const opt_t subopts[] = {
1604 {"deint", OPT_ARG_INT, &vc->deint, (opt_test_f)int_non_neg},
1605 {"chroma-deint", OPT_ARG_BOOL, &vc->chroma_deint, NULL},
1606 {"pullup", OPT_ARG_BOOL, &vc->pullup, NULL},
1607 {"denoise", OPT_ARG_FLOAT, &vc->denoise, NULL},
1608 {"sharpen", OPT_ARG_FLOAT, &vc->sharpen, NULL},
1609 {"colorspace", OPT_ARG_INT, &vc->user_colorspace, NULL},
1610 {"hqscaling", OPT_ARG_INT, &vc->hqscaling, NULL},
1611 {"fps", OPT_ARG_FLOAT, &vc->user_fps, NULL},
1612 {"queuetime_windowed", OPT_ARG_INT, &vc->flip_offset_window, NULL},
1613 {"queuetime_fs", OPT_ARG_INT, &vc->flip_offset_fs, NULL},
1614 {NULL}
1616 if (subopt_parse(arg, subopts) != 0) {
1617 mp_msg(MSGT_VO, MSGL_FATAL, "[vdpau] Could not parse suboptions.\n");
1618 return -1;
1620 if (vc->hqscaling < 0 || vc->hqscaling > 9) {
1621 mp_msg(MSGT_VO, MSGL_FATAL, "[vdpau] Invalid value for suboption "
1622 "hqscaling\n");
1623 return -1;
1625 if (vc->deint)
1626 vc->deint_type = vc->deint;
1628 if (!vo_init(vo))
1629 return -1;
1631 // After this calling uninit() should work to free resources
1633 if (win_x11_init_vdpau_procs(vo) < 0) {
1634 if (vc->vdp->device_destroy)
1635 vc->vdp->device_destroy(vc->vdp_device);
1636 vo_x11_uninit(vo);
1637 return -1;
1640 // full grayscale palette.
1641 for (i = 0; i < PALETTE_SIZE; ++i)
1642 vc->palette[i] = (i << 16) | (i << 8) | i;
1644 vc->procamp.struct_version = VDP_PROCAMP_VERSION;
1645 vc->procamp.brightness = 0.0;
1646 vc->procamp.contrast = 1.0;
1647 vc->procamp.saturation = 1.0;
1648 vc->procamp.hue = 0.0;
1650 return 0;
1653 static int get_equalizer(struct vo *vo, const char *name, int *value)
1655 struct vdpctx *vc = vo->priv;
1657 if (!strcasecmp(name, "brightness"))
1658 *value = vc->procamp.brightness * 100;
1659 else if (!strcasecmp(name, "contrast"))
1660 *value = (vc->procamp.contrast - 1.0) * 100;
1661 else if (!strcasecmp(name, "saturation"))
1662 *value = (vc->procamp.saturation - 1.0) * 100;
1663 else if (!strcasecmp(name, "hue"))
1664 *value = vc->procamp.hue * 100 / M_PI;
1665 else
1666 return VO_NOTIMPL;
1667 return VO_TRUE;
1670 static int set_equalizer(struct vo *vo, const char *name, int value)
1672 struct vdpctx *vc = vo->priv;
1674 if (!strcasecmp(name, "brightness"))
1675 vc->procamp.brightness = value / 100.0;
1676 else if (!strcasecmp(name, "contrast"))
1677 vc->procamp.contrast = value / 100.0 + 1.0;
1678 else if (!strcasecmp(name, "saturation"))
1679 vc->procamp.saturation = value / 100.0 + 1.0;
1680 else if (!strcasecmp(name, "hue"))
1681 vc->procamp.hue = value / 100.0 * M_PI;
1682 else
1683 return VO_NOTIMPL;
1685 update_csc_matrix(vo);
1686 return true;
1689 static int control(struct vo *vo, uint32_t request, void *data)
1691 struct vdpctx *vc = vo->priv;
1692 struct vdp_functions *vdp = vc->vdp;
1694 handle_preemption(vo);
1696 switch (request) {
1697 case VOCTRL_GET_DEINTERLACE:
1698 *(int*)data = vc->deint;
1699 return VO_TRUE;
1700 case VOCTRL_SET_DEINTERLACE:
1701 vc->deint = *(int*)data;
1702 if (vc->deint)
1703 vc->deint = vc->deint_type;
1704 if (vc->deint_type > 2) {
1705 VdpStatus vdp_st;
1706 VdpVideoMixerFeature features[1] =
1707 {vc->deint_type == 3 ?
1708 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL :
1709 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL};
1710 VdpBool feature_enables[1] = {vc->deint ? VDP_TRUE : VDP_FALSE};
1711 vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
1712 1, features,
1713 feature_enables);
1714 CHECK_ST_WARNING("Error changing deinterlacing settings");
1716 return VO_TRUE;
1717 case VOCTRL_PAUSE:
1718 if (vc->dropped_frame)
1719 flip_page_timed(vo, 0, -1);
1720 return (vc->paused = true);
1721 case VOCTRL_RESUME:
1722 return (vc->paused = false);
1723 case VOCTRL_QUERY_FORMAT:
1724 return query_format(*(uint32_t *)data);
1725 case VOCTRL_GET_IMAGE:
1726 return get_image(vo, data);
1727 case VOCTRL_DRAW_IMAGE:
1728 abort(); // draw_image() should get called directly
1729 case VOCTRL_BORDER:
1730 vo_x11_border(vo);
1731 resize(vo);
1732 return VO_TRUE;
1733 case VOCTRL_FULLSCREEN:
1734 vo_x11_fullscreen(vo);
1735 resize(vo);
1736 return VO_TRUE;
1737 case VOCTRL_GET_PANSCAN:
1738 return VO_TRUE;
1739 case VOCTRL_SET_PANSCAN:
1740 resize(vo);
1741 return VO_TRUE;
1742 case VOCTRL_SET_EQUALIZER: {
1743 struct voctrl_set_equalizer_args *args = data;
1744 return set_equalizer(vo, args->name, args->value);
1746 case VOCTRL_GET_EQUALIZER: {
1747 struct voctrl_get_equalizer_args *args = data;
1748 return get_equalizer(vo, args->name, args->valueptr);
1750 case VOCTRL_SET_YUV_COLORSPACE:
1751 vc->colorspace = *(int *)data % 3;
1752 update_csc_matrix(vo);
1753 return true;
1754 case VOCTRL_GET_YUV_COLORSPACE:
1755 *(int *)data = vc->colorspace;
1756 return true;
1757 case VOCTRL_ONTOP:
1758 vo_x11_ontop(vo);
1759 return VO_TRUE;
1760 case VOCTRL_UPDATE_SCREENINFO:
1761 update_xinerama_info(vo);
1762 return VO_TRUE;
1763 case VOCTRL_DRAW_EOSD:
1764 if (!data)
1765 return VO_FALSE;
1766 generate_eosd(vo, data);
1767 draw_eosd(vo);
1768 return VO_TRUE;
1769 case VOCTRL_GET_EOSD_RES: {
1770 mp_eosd_res_t *r = data;
1771 r->w = vo->dwidth;
1772 r->h = vo->dheight;
1773 r->ml = r->mr = vc->border_x;
1774 r->mt = r->mb = vc->border_y;
1775 return VO_TRUE;
1777 case VOCTRL_REDRAW_OSD:
1778 video_to_output_surface(vo);
1779 draw_eosd(vo);
1780 draw_osd(vo, data);
1781 flip_page_timed(vo, 0, -1);
1782 return true;
1783 case VOCTRL_RESET:
1784 forget_frames(vo);
1785 return true;
1787 return VO_NOTIMPL;
1790 const struct vo_driver video_out_vdpau = {
1791 .is_new = true,
1792 .buffer_frames = true,
1793 .info = &(const struct vo_info_s){
1794 "VDPAU with X11",
1795 "vdpau",
1796 "Rajib Mahapatra <rmahapatra@nvidia.com> and others",
1799 .preinit = preinit,
1800 .config = config,
1801 .control = control,
1802 .draw_image = draw_image,
1803 .get_buffered_frame = get_buffered_frame,
1804 .draw_slice = draw_slice,
1805 .draw_osd = draw_osd,
1806 .flip_page_timed = flip_page_timed,
1807 .check_events = check_events,
1808 .uninit = uninit,