vo_gl3: call glFlush() after frame drawing is complete
[mplayer.git] / libvo / vo_vdpau.c
blob61027144080d6d8243598f02ead8c1aa6af38b62
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>
36 #include <assert.h>
38 #include <libavutil/common.h>
39 #include <libavcodec/vdpau.h>
41 #include "config.h"
42 #include "mp_msg.h"
43 #include "options.h"
44 #include "talloc.h"
45 #include "video_out.h"
46 #include "x11_common.h"
47 #include "aspect.h"
48 #include "csputils.h"
49 #include "sub/sub.h"
50 #include "m_option.h"
51 #include "libmpcodecs/vfcap.h"
52 #include "libmpcodecs/mp_image.h"
53 #include "osdep/timer.h"
54 #include "sub/ass_mp.h"
55 #include "bitmap_packer.h"
57 #define WRAP_ADD(x, a, m) ((a) < 0 \
58 ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
59 : ((x)+(a) < (m) ? (x)+(a) : (x)+(a)-(m)))
61 #define CHECK_ST_ERROR(message) \
62 do { \
63 if (vdp_st != VDP_STATUS_OK) { \
64 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] %s: %s\n", \
65 message, vdp->get_error_string(vdp_st)); \
66 return -1; \
67 } \
68 } while (0)
70 #define CHECK_ST_WARNING(message) \
71 do { \
72 if (vdp_st != VDP_STATUS_OK) \
73 mp_msg(MSGT_VO, MSGL_WARN, "[ vdpau] %s: %s\n", \
74 message, vdp->get_error_string(vdp_st)); \
75 } while (0)
77 /* number of video and output surfaces */
78 #define MAX_OUTPUT_SURFACES 15
79 #define MAX_VIDEO_SURFACES 50
80 #define NUM_BUFFERED_VIDEO 5
82 /* Pixelformat used for output surfaces */
83 #define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
86 * Global variable declaration - VDPAU specific
89 struct vdp_functions {
90 #define VDP_FUNCTION(vdp_type, _, mp_name) vdp_type *mp_name;
91 #include "vdpau_template.c"
92 #undef VDP_FUNCTION
95 #define MAX_OLD_OSD_BITMAPS 6
97 struct vdpctx {
98 struct vdp_functions *vdp;
100 VdpDevice vdp_device;
101 bool is_preempted;
102 bool preemption_acked;
103 bool preemption_user_notified;
104 unsigned int last_preemption_retry_fail;
105 VdpGetProcAddress *vdp_get_proc_address;
107 VdpPresentationQueueTarget flip_target;
108 VdpPresentationQueue flip_queue;
109 uint64_t last_vdp_time;
110 unsigned int last_sync_update;
112 VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
113 VdpOutputSurface screenshot_surface;
114 int num_output_surfaces;
115 struct buffered_video_surface {
116 VdpVideoSurface surface;
117 double pts;
118 mp_image_t *mpi;
119 } buffered_video[NUM_BUFFERED_VIDEO];
120 int deint_queue_pos;
121 int output_surface_width, output_surface_height;
123 VdpVideoMixer video_mixer;
124 struct mp_csp_details 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 int query_surface_num;
148 VdpTime recent_vsync_time;
149 float user_fps;
150 int composite_detect;
151 unsigned int vsync_interval;
152 uint64_t last_queue_time;
153 uint64_t queue_time[MAX_OUTPUT_SURFACES];
154 uint64_t last_ideal_time;
155 bool dropped_frame;
156 uint64_t dropped_time;
157 uint32_t vid_width, vid_height;
158 uint32_t vid_d_width, vid_d_height;
159 uint32_t image_format;
160 VdpChromaType vdp_chroma_type;
161 VdpYCbCrFormat vdp_pixel_format;
163 /* draw_osd */
164 struct old_osd {
165 int x0, y0, w, h;
166 unsigned char *src, *srca;
167 int stride;
168 } old_osd_elements[MAX_OLD_OSD_BITMAPS];
169 int old_osd_count;
170 unsigned char *osd_data_temp;
171 int osd_data_size;
173 // EOSD
174 struct eosd_bitmap_surface {
175 VdpRGBAFormat format;
176 VdpBitmapSurface surface;
177 uint32_t max_width;
178 uint32_t max_height;
179 struct bitmap_packer *packer;
180 } eosd_surface, osd_surface;
182 // List of surfaces to be rendered
183 struct eosd_target {
184 VdpRect source;
185 VdpRect dest;
186 VdpColor color;
187 } *eosd_targets, osd_targets[MAX_OLD_OSD_BITMAPS][2];
188 int eosd_targets_size;
189 int eosd_render_count;
190 unsigned int bitmap_id;
191 unsigned int bitmap_pos_id;
193 // Video equalizer
194 struct mp_csp_equalizer video_eq;
196 // These tell what's been initialized and uninit() should free/uninitialize
197 bool mode_switched;
200 static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t)
202 struct vdp_functions *vdp = vc->vdp;
203 VdpStatus vdp_st;
204 VdpTime vdp_time;
205 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
206 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
207 unsigned int t1 = *t;
208 unsigned int t2 = GetTimer();
209 uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL;
210 if (vdp_time > old)
211 if (vdp_time > old + (t2 - t1) * 1000ULL)
212 vdp_time -= (t2 - t1) * 1000ULL;
213 else
214 vdp_time = old;
215 mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] adjusting VdpTime offset by %f µs\n",
216 (int64_t)(vdp_time - old) / 1000.);
217 vc->last_vdp_time = vdp_time;
218 vc->last_sync_update = t1;
219 *t = t2;
220 return 0;
223 static uint64_t sync_vdptime(struct vo *vo)
225 struct vdpctx *vc = vo->priv;
227 unsigned int t = GetTimer();
228 if (t - vc->last_sync_update > 5000000)
229 change_vdptime_sync(vc, &t);
230 uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time;
231 // Make sure nanosecond inaccuracies don't make things inconsistent
232 now = FFMAX(now, vc->recent_vsync_time);
233 return now;
236 static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t)
238 struct vdpctx *vc = vo->priv;
239 return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time;
242 static int render_video_to_output_surface(struct vo *vo,
243 VdpOutputSurface output_surface,
244 VdpRect *output_rect)
246 struct vdpctx *vc = vo->priv;
247 struct vdp_functions *vdp = vc->vdp;
248 VdpTime dummy;
249 VdpStatus vdp_st;
250 if (vc->deint_queue_pos < 0)
251 return -1;
253 struct buffered_video_surface *bv = vc->buffered_video;
254 int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
255 unsigned int dp = vc->deint_queue_pos;
256 // dp==0 means last field of latest frame, 1 earlier field of latest frame,
257 // 2 last field of previous frame and so on
258 if (vc->deint) {
259 field = vc->top_field_first ^ (dp & 1) ?
260 VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
261 VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
263 const VdpVideoSurface *past_fields = (const VdpVideoSurface []){
264 bv[(dp+1)/2].surface, bv[(dp+2)/2].surface};
265 const VdpVideoSurface *future_fields = (const VdpVideoSurface []){
266 dp >= 1 ? bv[(dp-1)/2].surface : VDP_INVALID_HANDLE};
267 vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
268 output_surface,
269 &dummy);
270 CHECK_ST_WARNING("Error when calling "
271 "vdp_presentation_queue_block_until_surface_idle");
273 vdp_st = vdp->video_mixer_render(vc->video_mixer, VDP_INVALID_HANDLE,
274 0, field, 2, past_fields,
275 bv[dp/2].surface, 1, future_fields,
276 &vc->src_rect_vid, output_surface,
277 NULL, output_rect, 0, NULL);
278 CHECK_ST_WARNING("Error when calling vdp_video_mixer_render");
279 return 0;
282 static int video_to_output_surface(struct vo *vo)
284 struct vdpctx *vc = vo->priv;
286 return render_video_to_output_surface(vo,
287 vc->output_surfaces[vc->surface_num],
288 &vc->out_rect_vid);
291 static int next_deint_queue_pos(struct vo *vo, bool eof)
293 struct vdpctx *vc = vo->priv;
295 int dqp = vc->deint_queue_pos;
296 if (dqp < 0)
297 dqp += 1000;
298 else
299 dqp = vc->deint >= 2 ? dqp - 1 : dqp - 2 | 1;
300 if (dqp < (eof ? 0 : 3))
301 return -1;
302 return dqp;
305 static void set_next_frame_info(struct vo *vo, bool eof)
307 struct vdpctx *vc = vo->priv;
309 vo->frame_loaded = false;
310 int dqp = next_deint_queue_pos(vo, eof);
311 if (dqp < 0)
312 return;
313 vo->frame_loaded = true;
315 // Set pts values
316 struct buffered_video_surface *bv = vc->buffered_video;
317 int idx = dqp >> 1;
318 if (idx == 0) { // no future frame/pts available
319 vo->next_pts = bv[0].pts;
320 vo->next_pts2 = MP_NOPTS_VALUE;
321 } else if (!(vc->deint >= 2)) { // no field-splitting deinterlace
322 vo->next_pts = bv[idx].pts;
323 vo->next_pts2 = bv[idx - 1].pts;
324 } else { // deinterlace with separate fields
325 double intermediate_pts;
326 double diff = bv[idx - 1].pts - bv[idx].pts;
327 if (diff > 0 && diff < 0.5)
328 intermediate_pts = (bv[idx].pts + bv[idx - 1].pts) / 2;
329 else
330 intermediate_pts = bv[idx].pts;
331 if (dqp & 1) { // first field
332 vo->next_pts = bv[idx].pts;
333 vo->next_pts2 = intermediate_pts;
334 } else {
335 vo->next_pts = intermediate_pts;
336 vo->next_pts2 = bv[idx - 1].pts;
341 static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
342 struct mp_image *reserved_mpi, double pts)
344 struct vdpctx *vc = vo->priv;
345 struct buffered_video_surface *bv = vc->buffered_video;
347 if (reserved_mpi)
348 reserved_mpi->usage_count++;
349 if (bv[NUM_BUFFERED_VIDEO - 1].mpi)
350 bv[NUM_BUFFERED_VIDEO - 1].mpi->usage_count--;
352 for (int i = NUM_BUFFERED_VIDEO - 1; i > 0; i--)
353 bv[i] = bv[i - 1];
354 bv[0] = (struct buffered_video_surface){
355 .mpi = reserved_mpi,
356 .surface = surface,
357 .pts = pts,
360 vc->deint_queue_pos = FFMIN(vc->deint_queue_pos + 2,
361 NUM_BUFFERED_VIDEO * 2 - 3);
362 set_next_frame_info(vo, false);
365 static void forget_frames(struct vo *vo)
367 struct vdpctx *vc = vo->priv;
369 vc->deint_queue_pos = -1001;
370 vc->dropped_frame = false;
371 for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) {
372 struct buffered_video_surface *p = vc->buffered_video + i;
373 if (p->mpi)
374 p->mpi->usage_count--;
375 *p = (struct buffered_video_surface){
376 .surface = VDP_INVALID_HANDLE,
381 static void resize(struct vo *vo)
383 struct vdpctx *vc = vo->priv;
384 struct vdp_functions *vdp = vc->vdp;
385 VdpStatus vdp_st;
386 struct vo_rect src_rect;
387 struct vo_rect dst_rect;
388 struct vo_rect borders;
389 calc_src_dst_rects(vo, vc->vid_width, vc->vid_height, &src_rect, &dst_rect,
390 &borders, NULL);
391 vc->out_rect_vid.x0 = dst_rect.left;
392 vc->out_rect_vid.x1 = dst_rect.right;
393 vc->out_rect_vid.y0 = dst_rect.top;
394 vc->out_rect_vid.y1 = dst_rect.bottom;
395 vc->src_rect_vid.x0 = src_rect.left;
396 vc->src_rect_vid.x1 = src_rect.right;
397 vc->src_rect_vid.y0 = vc->flip ? src_rect.bottom : src_rect.top;
398 vc->src_rect_vid.y1 = vc->flip ? src_rect.top : src_rect.bottom;
399 vc->border_x = borders.left;
400 vc->border_y = borders.top;
401 int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window;
402 vo->flip_queue_offset = flip_offset_ms / 1000.;
404 if (vc->output_surface_width < vo->dwidth
405 || vc->output_surface_height < vo->dheight) {
406 if (vc->output_surface_width < vo->dwidth) {
407 vc->output_surface_width += vc->output_surface_width >> 1;
408 vc->output_surface_width = FFMAX(vc->output_surface_width,
409 vo->dwidth);
411 if (vc->output_surface_height < vo->dheight) {
412 vc->output_surface_height += vc->output_surface_height >> 1;
413 vc->output_surface_height = FFMAX(vc->output_surface_height,
414 vo->dheight);
416 // Creation of output_surfaces
417 for (int i = 0; i < vc->num_output_surfaces; i++)
418 if (vc->output_surfaces[i] != VDP_INVALID_HANDLE) {
419 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
420 CHECK_ST_WARNING("Error when calling "
421 "vdp_output_surface_destroy");
423 for (int i = 0; i < vc->num_output_surfaces; i++) {
424 vdp_st = vdp->output_surface_create(vc->vdp_device,
425 OUTPUT_RGBA_FORMAT,
426 vc->output_surface_width,
427 vc->output_surface_height,
428 &vc->output_surfaces[i]);
429 CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
430 mp_msg(MSGT_VO, MSGL_DBG2, "vdpau out create: %u\n",
431 vc->output_surfaces[i]);
434 vo->want_redraw = true;
437 static void preemption_callback(VdpDevice device, void *context)
439 struct vdpctx *vc = context;
440 vc->is_preempted = true;
441 vc->preemption_acked = false;
444 /* Initialize vdp_get_proc_address, called from preinit() */
445 static int win_x11_init_vdpau_procs(struct vo *vo)
447 struct vo_x11_state *x11 = vo->x11;
448 struct vdpctx *vc = vo->priv;
449 if (vc->vdp) // reinitialization after preemption
450 memset(vc->vdp, 0, sizeof(*vc->vdp));
451 else
452 vc->vdp = talloc_zero(vc, struct vdp_functions);
453 struct vdp_functions *vdp = vc->vdp;
454 VdpStatus vdp_st;
456 struct vdp_function {
457 const int id;
458 int offset;
461 const struct vdp_function *dsc;
463 static const struct vdp_function vdp_func[] = {
464 #define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)},
465 #include "vdpau_template.c"
466 #undef VDP_FUNCTION
467 {0, -1}
470 vdp_st = vdp_device_create_x11(x11->display, x11->screen, &vc->vdp_device,
471 &vc->vdp_get_proc_address);
472 if (vdp_st != VDP_STATUS_OK) {
473 if (vc->is_preempted)
474 mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Error calling "
475 "vdp_device_create_x11 while preempted: %d\n", vdp_st);
476 else
477 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
478 "vdp_device_create_x11: %d\n", vdp_st);
479 return -1;
482 vdp->get_error_string = NULL;
483 for (dsc = vdp_func; dsc->offset >= 0; dsc++) {
484 vdp_st = vc->vdp_get_proc_address(vc->vdp_device, dsc->id,
485 (void **)((char *)vdp + dsc->offset));
486 if (vdp_st != VDP_STATUS_OK) {
487 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
488 "vdp_get_proc_address(function id %d): %s\n", dsc->id,
489 vdp->get_error_string ? vdp->get_error_string(vdp_st) : "?");
490 return -1;
493 vdp_st = vdp->preemption_callback_register(vc->vdp_device,
494 preemption_callback, vc);
495 return 0;
498 static int win_x11_init_vdpau_flip_queue(struct vo *vo)
500 struct vdpctx *vc = vo->priv;
501 struct vdp_functions *vdp = vc->vdp;
502 struct vo_x11_state *x11 = vo->x11;
503 VdpStatus vdp_st;
505 if (vc->flip_target == VDP_INVALID_HANDLE) {
506 vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device,
507 x11->window,
508 &vc->flip_target);
509 CHECK_ST_ERROR("Error when calling "
510 "vdp_presentation_queue_target_create_x11");
513 /* Emperically this seems to be the first call which fails when we
514 * try to reinit after preemption while the user is still switched
515 * from X to a virtual terminal (creating the vdp_device initially
516 * succeeds, as does creating the flip_target above). This is
517 * probably not guaranteed behavior, but we'll assume it as a simple
518 * way to reduce warnings while trying to recover from preemption.
520 if (vc->flip_queue == VDP_INVALID_HANDLE) {
521 vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target,
522 &vc->flip_queue);
523 if (vc->is_preempted && vdp_st != VDP_STATUS_OK) {
524 mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Failed to create flip queue "
525 "while preempted: %s\n", vdp->get_error_string(vdp_st));
526 return -1;
527 } else
528 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create");
531 VdpTime vdp_time;
532 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
533 CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
534 vc->last_vdp_time = vdp_time;
535 vc->last_sync_update = GetTimer();
537 vc->vsync_interval = 1;
538 if (vc->composite_detect && vo_x11_screen_is_composited(vo)) {
539 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Compositing window manager "
540 "detected. Assuming timing info is inaccurate.\n");
541 } else if (vc->user_fps > 0) {
542 vc->vsync_interval = 1e9 / vc->user_fps;
543 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Assuming user-specified display "
544 "refresh rate of %.3f Hz.\n", vc->user_fps);
545 } else if (vc->user_fps == 0) {
546 #ifdef CONFIG_XF86VM
547 double fps = vo_vm_get_fps(vo);
548 if (!fps)
549 mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Failed to get display FPS\n");
550 else {
551 vc->vsync_interval = 1e9 / fps;
552 // This is verbose, but I'm not yet sure how common wrong values are
553 mp_msg(MSGT_VO, MSGL_INFO,
554 "[vdpau] Got display refresh rate %.3f Hz.\n"
555 "[vdpau] If that value looks wrong give the "
556 "-vo vdpau:fps=X suboption manually.\n", fps);
558 #else
559 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] This binary has been compiled "
560 "without XF86VidMode support.\n");
561 mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Can't use vsync-aware timing "
562 "without manually provided -vo vdpau:fps=X suboption.\n");
563 #endif
564 } else
565 mp_msg(MSGT_VO, MSGL_V, "[vdpau] framedrop/timing logic disabled by "
566 "user.\n");
568 return 0;
571 static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr,
572 const void *value, char *attr_name)
574 struct vdp_functions *vdp = vc->vdp;
575 VdpStatus vdp_st;
577 vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1, &attr,
578 &value);
579 if (vdp_st != VDP_STATUS_OK) {
580 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error setting video mixer "
581 "attribute %s: %s\n", attr_name, vdp->get_error_string(vdp_st));
582 return -1;
584 return 0;
587 static void update_csc_matrix(struct vo *vo)
589 struct vdpctx *vc = vo->priv;
591 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix\n");
593 // VdpCSCMatrix happens to be compatible with mplayer's CSC matrix type
594 // both are float[3][4]
595 VdpCSCMatrix matrix;
597 struct mp_csp_params cparams = {
598 .colorspace = vc->colorspace, .input_bits = 8, .texture_bits = 8 };
599 mp_csp_copy_equalizer_values(&cparams, &vc->video_eq);
600 mp_get_yuv2rgb_coeffs(&cparams, matrix);
602 set_video_attribute(vc, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX,
603 &matrix, "CSC matrix");
606 #define SET_VIDEO_ATTR(attr_name, attr_type, value) set_video_attribute(vc, \
607 VDP_VIDEO_MIXER_ATTRIBUTE_ ## attr_name, &(attr_type){value},\
608 # attr_name)
609 static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
611 struct vdpctx *vc = vo->priv;
612 struct vdp_functions *vdp = vc->vdp;
613 #define VDP_NUM_MIXER_PARAMETER 3
614 #define MAX_NUM_FEATURES 6
615 int i;
616 VdpStatus vdp_st;
618 if (vc->video_mixer != VDP_INVALID_HANDLE)
619 return 0;
621 int feature_count = 0;
622 VdpVideoMixerFeature features[MAX_NUM_FEATURES];
623 VdpBool feature_enables[MAX_NUM_FEATURES];
624 static const VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
625 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
626 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
627 VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
629 const void *const parameter_values[VDP_NUM_MIXER_PARAMETER] = {
630 &vc->vid_width,
631 &vc->vid_height,
632 &vdp_chroma_type,
634 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
635 if (vc->deint_type == 4)
636 features[feature_count++] =
637 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
638 if (vc->pullup)
639 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
640 if (vc->denoise)
641 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
642 if (vc->sharpen)
643 features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
644 if (vc->hqscaling) {
645 VdpVideoMixerFeature hqscaling_feature =
646 VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + vc->hqscaling-1;
647 VdpBool hqscaling_available;
648 vdp_st = vdp->video_mixer_query_feature_support(vc->vdp_device,
649 hqscaling_feature,
650 &hqscaling_available);
651 CHECK_ST_ERROR("Error when calling video_mixer_query_feature_support");
652 if (hqscaling_available)
653 features[feature_count++] = hqscaling_feature;
654 else
655 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Your hardware or VDPAU "
656 "library does not support requested hqscaling.\n");
659 vdp_st = vdp->video_mixer_create(vc->vdp_device, feature_count, features,
660 VDP_NUM_MIXER_PARAMETER,
661 parameters, parameter_values,
662 &vc->video_mixer);
663 CHECK_ST_ERROR("Error when calling vdp_video_mixer_create");
665 for (i = 0; i < feature_count; i++)
666 feature_enables[i] = VDP_TRUE;
667 if (vc->deint < 3)
668 feature_enables[0] = VDP_FALSE;
669 if (vc->deint_type == 4 && vc->deint < 4)
670 feature_enables[1] = VDP_FALSE;
671 if (feature_count) {
672 vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
673 feature_count, features,
674 feature_enables);
675 CHECK_ST_WARNING("Error calling vdp_video_mixer_set_feature_enables");
677 if (vc->denoise)
678 SET_VIDEO_ATTR(NOISE_REDUCTION_LEVEL, float, vc->denoise);
679 if (vc->sharpen)
680 SET_VIDEO_ATTR(SHARPNESS_LEVEL, float, vc->sharpen);
681 if (!vc->chroma_deint)
682 SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
684 update_csc_matrix(vo);
685 return 0;
688 // Free everything specific to a certain video file
689 static void free_video_specific(struct vo *vo)
691 struct vdpctx *vc = vo->priv;
692 struct vdp_functions *vdp = vc->vdp;
693 int i;
694 VdpStatus vdp_st;
696 if (vc->decoder != VDP_INVALID_HANDLE)
697 vdp->decoder_destroy(vc->decoder);
698 vc->decoder = VDP_INVALID_HANDLE;
699 vc->decoder_max_refs = -1;
701 forget_frames(vo);
703 for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
704 if (vc->surface_render[i].surface != VDP_INVALID_HANDLE) {
705 vdp_st = vdp->video_surface_destroy(vc->surface_render[i].surface);
706 CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy");
708 vc->surface_render[i].surface = VDP_INVALID_HANDLE;
711 if (vc->video_mixer != VDP_INVALID_HANDLE) {
712 vdp_st = vdp->video_mixer_destroy(vc->video_mixer);
713 CHECK_ST_WARNING("Error when calling vdp_video_mixer_destroy");
715 vc->video_mixer = VDP_INVALID_HANDLE;
717 if (vc->screenshot_surface != VDP_INVALID_HANDLE) {
718 vdp_st = vdp->output_surface_destroy(vc->screenshot_surface);
719 CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
721 vc->screenshot_surface = VDP_INVALID_HANDLE;
724 static int create_vdp_decoder(struct vo *vo, int max_refs)
726 struct vdpctx *vc = vo->priv;
727 struct vdp_functions *vdp = vc->vdp;
728 VdpStatus vdp_st;
729 VdpDecoderProfile vdp_decoder_profile;
730 if (vc->decoder != VDP_INVALID_HANDLE)
731 vdp->decoder_destroy(vc->decoder);
732 switch (vc->image_format) {
733 case IMGFMT_VDPAU_MPEG1:
734 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
735 break;
736 case IMGFMT_VDPAU_MPEG2:
737 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
738 break;
739 case IMGFMT_VDPAU_H264:
740 vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
741 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder "
742 "for %d reference frames.\n", max_refs);
743 break;
744 case IMGFMT_VDPAU_WMV3:
745 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
746 break;
747 case IMGFMT_VDPAU_VC1:
748 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
749 break;
750 case IMGFMT_VDPAU_MPEG4:
751 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
752 break;
753 default:
754 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown image format!\n");
755 goto fail;
757 vdp_st = vdp->decoder_create(vc->vdp_device, vdp_decoder_profile,
758 vc->vid_width, vc->vid_height, max_refs,
759 &vc->decoder);
760 CHECK_ST_WARNING("Failed creating VDPAU decoder");
761 if (vdp_st != VDP_STATUS_OK) {
762 fail:
763 vc->decoder = VDP_INVALID_HANDLE;
764 vc->decoder_max_refs = 0;
765 return 0;
767 vc->decoder_max_refs = max_refs;
768 return 1;
771 static int initialize_vdpau_objects(struct vo *vo)
773 struct vdpctx *vc = vo->priv;
775 vc->vdp_chroma_type = VDP_CHROMA_TYPE_420;
776 switch (vc->image_format) {
777 case IMGFMT_YV12:
778 case IMGFMT_I420:
779 case IMGFMT_IYUV:
780 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YV12;
781 break;
782 case IMGFMT_NV12:
783 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_NV12;
784 break;
785 case IMGFMT_YUY2:
786 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YUYV;
787 vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
788 break;
789 case IMGFMT_UYVY:
790 vc->vdp_pixel_format = VDP_YCBCR_FORMAT_UYVY;
791 vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
793 if (win_x11_init_vdpau_flip_queue(vo) < 0)
794 return -1;
796 if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
797 return -1;
799 forget_frames(vo);
800 resize(vo);
801 return 0;
804 static void mark_vdpau_objects_uninitialized(struct vo *vo)
806 struct vdpctx *vc = vo->priv;
808 vc->decoder = VDP_INVALID_HANDLE;
809 for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
810 vc->surface_render[i].surface = VDP_INVALID_HANDLE;
811 forget_frames(vo);
812 vc->video_mixer = VDP_INVALID_HANDLE;
813 vc->flip_queue = VDP_INVALID_HANDLE;
814 vc->flip_target = VDP_INVALID_HANDLE;
815 for (int i = 0; i < MAX_OUTPUT_SURFACES; i++)
816 vc->output_surfaces[i] = VDP_INVALID_HANDLE;
817 vc->screenshot_surface = VDP_INVALID_HANDLE;
818 vc->vdp_device = VDP_INVALID_HANDLE;
819 talloc_free(vc->osd_surface.packer);
820 talloc_free(vc->eosd_surface.packer);
821 vc->bitmap_id = vc->bitmap_pos_id = 0;
822 vc->osd_surface = vc->eosd_surface = (struct eosd_bitmap_surface){
823 .surface = VDP_INVALID_HANDLE,
825 vc->output_surface_width = vc->output_surface_height = -1;
826 vc->eosd_render_count = 0;
829 static int handle_preemption(struct vo *vo)
831 struct vdpctx *vc = vo->priv;
833 if (!vc->is_preempted)
834 return 0;
835 if (!vc->preemption_acked)
836 mark_vdpau_objects_uninitialized(vo);
837 vc->preemption_acked = true;
838 if (!vc->preemption_user_notified) {
839 mp_tmsg(MSGT_VO, MSGL_ERR, "[vdpau] Got display preemption notice! "
840 "Will attempt to recover.\n");
841 vc->preemption_user_notified = true;
843 /* Trying to initialize seems to be quite slow, so only try once a
844 * second to avoid using 100% CPU. */
845 if (vc->last_preemption_retry_fail
846 && GetTimerMS() - vc->last_preemption_retry_fail < 1000)
847 return -1;
848 if (win_x11_init_vdpau_procs(vo) < 0 || initialize_vdpau_objects(vo) < 0) {
849 vc->last_preemption_retry_fail = GetTimerMS() | 1;
850 return -1;
852 vc->last_preemption_retry_fail = 0;
853 vc->is_preempted = false;
854 vc->preemption_user_notified = false;
855 mp_tmsg(MSGT_VO, MSGL_INFO, "[vdpau] Recovered from display preemption.\n");
856 return 1;
860 * connect to X server, create and map window, initialize all
861 * VDPAU objects, create different surfaces etc.
863 static int config(struct vo *vo, uint32_t width, uint32_t height,
864 uint32_t d_width, uint32_t d_height, uint32_t flags,
865 uint32_t format)
867 struct vdpctx *vc = vo->priv;
868 struct vo_x11_state *x11 = vo->x11;
869 XVisualInfo vinfo;
870 XSetWindowAttributes xswa;
871 XWindowAttributes attribs;
872 unsigned long xswamask;
874 #ifdef CONFIG_XF86VM
875 int vm = flags & VOFLAG_MODESWITCHING;
876 #endif
878 if (handle_preemption(vo) < 0)
879 return -1;
881 vc->flip = flags & VOFLAG_FLIPPING;
882 vc->image_format = format;
883 vc->vid_width = width;
884 vc->vid_height = height;
885 vc->vid_d_width = d_width;
886 vc->vid_d_height = d_height;
888 free_video_specific(vo);
889 if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
890 return -1;
892 #ifdef CONFIG_XF86VM
893 if (vm) {
894 vo_vm_switch(vo);
895 vc->mode_switched = true;
897 #endif
898 XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
899 &attribs);
900 XMatchVisualInfo(x11->display, x11->screen, attribs.depth, TrueColor,
901 &vinfo);
903 xswa.background_pixel = 0;
904 xswa.border_pixel = 0;
905 /* Do not use CWBackPixel: It leads to VDPAU errors after
906 * aspect ratio changes. */
907 xswamask = CWBorderPixel;
909 vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, d_width, d_height,
910 flags, CopyFromParent, "vdpau");
911 XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
913 #ifdef CONFIG_XF86VM
914 if (vm) {
915 /* Grab the mouse pointer in our window */
916 if (vo_grabpointer)
917 XGrabPointer(x11->display, x11->window, True, 0,
918 GrabModeAsync, GrabModeAsync,
919 x11->window, None, CurrentTime);
920 XSetInputFocus(x11->display, x11->window, RevertToNone, CurrentTime);
922 #endif
924 if ((flags & VOFLAG_FULLSCREEN) && WinID <= 0)
925 vo_fs = 1;
927 if (initialize_vdpau_objects(vo) < 0)
928 return -1;
930 return 0;
933 static void check_events(struct vo *vo)
935 if (handle_preemption(vo) < 0)
936 return;
938 int e = vo_x11_check_events(vo);
940 if (e & VO_EVENT_RESIZE)
941 resize(vo);
942 else if (e & VO_EVENT_EXPOSE) {
943 vo->want_redraw = true;
947 static struct bitmap_packer *make_packer(struct vo *vo, VdpRGBAFormat format)
949 struct vdpctx *vc = vo->priv;
950 struct vdp_functions *vdp = vc->vdp;
952 struct bitmap_packer *packer = talloc_zero(vo, struct bitmap_packer);
953 uint32_t w_max = 0, h_max = 0;
954 VdpStatus vdp_st = vdp->
955 bitmap_surface_query_capabilities(vc->vdp_device, format,
956 &(VdpBool){0}, &w_max, &h_max);
957 CHECK_ST_WARNING("Query to get max EOSD surface size failed");
958 packer->w_max = w_max;
959 packer->h_max = h_max;
960 return packer;
963 static void draw_eosd(struct vo *vo)
965 struct vdpctx *vc = vo->priv;
966 struct vdp_functions *vdp = vc->vdp;
967 VdpStatus vdp_st;
968 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
969 int i;
971 VdpOutputSurfaceRenderBlendState blend_state = {
972 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
973 .blend_factor_source_color =
974 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
975 .blend_factor_source_alpha =
976 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
977 .blend_factor_destination_color =
978 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
979 .blend_factor_destination_alpha =
980 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
981 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
982 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
985 for (i = 0; i < vc->eosd_render_count; i++) {
986 vdp_st = vdp->
987 output_surface_render_bitmap_surface(output_surface,
988 &vc->eosd_targets[i].dest,
989 vc->eosd_surface.surface,
990 &vc->eosd_targets[i].source,
991 &vc->eosd_targets[i].color,
992 &blend_state,
993 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
994 CHECK_ST_WARNING("EOSD: Error when rendering");
998 static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
1000 struct vdpctx *vc = vo->priv;
1001 struct vdp_functions *vdp = vc->vdp;
1002 VdpStatus vdp_st;
1003 struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
1004 bool need_upload = false;
1006 if (imgs->bitmap_pos_id == vc->bitmap_pos_id)
1007 return; // Nothing changed and we still have the old data
1009 vc->eosd_render_count = 0;
1011 if (imgs->type == SUBBITMAP_EMPTY)
1012 return;
1014 if (imgs->bitmap_id == vc->bitmap_id)
1015 goto eosd_skip_upload;
1017 need_upload = true;
1018 VdpRGBAFormat format;
1019 int format_size;
1020 switch (imgs->type) {
1021 case SUBBITMAP_LIBASS:
1022 format = VDP_RGBA_FORMAT_A8;
1023 format_size = 1;
1024 break;
1025 case SUBBITMAP_RGBA:
1026 format = VDP_RGBA_FORMAT_B8G8R8A8;
1027 format_size = 4;
1028 break;
1029 default:
1030 abort();
1032 if (sfc->format != format) {
1033 talloc_free(sfc->packer);
1034 sfc->packer = NULL;
1036 sfc->format = format;
1037 if (!sfc->packer)
1038 sfc->packer = make_packer(vo, format);
1039 int r = packer_pack_from_subbitmaps(sfc->packer, imgs, imgs->scaled);
1040 if (r < 0) {
1041 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
1042 "a surface with the maximum supported size\n");
1043 return;
1044 } else if (r == 1) {
1045 if (sfc->surface != VDP_INVALID_HANDLE) {
1046 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
1047 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1049 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
1050 "EOSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
1051 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
1052 sfc->packer->w, sfc->packer->h,
1053 true, &sfc->surface);
1054 if (vdp_st != VDP_STATUS_OK)
1055 sfc->surface = VDP_INVALID_HANDLE;
1056 CHECK_ST_WARNING("EOSD: error when creating surface");
1058 if (imgs->scaled) {
1059 char zeros[sfc->packer->used_width * format_size];
1060 memset(zeros, 0, sizeof(zeros));
1061 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1062 &(const void *){zeros}, &(uint32_t){0},
1063 &(VdpRect){0, 0, sfc->packer->used_width,
1064 sfc->packer->used_height});
1067 eosd_skip_upload:
1068 if (sfc->surface == VDP_INVALID_HANDLE)
1069 return;
1070 if (sfc->packer->count > vc->eosd_targets_size) {
1071 talloc_free(vc->eosd_targets);
1072 vc->eosd_targets_size = sfc->packer->count;
1073 vc->eosd_targets = talloc_size(vc, vc->eosd_targets_size
1074 * sizeof(*vc->eosd_targets));
1077 if (imgs->type == SUBBITMAP_LIBASS) {
1078 int i = 0;
1079 for (ASS_Image *p = imgs->imgs; p; p = p->next, i++) {
1080 if (p->w == 0 || p->h == 0)
1081 continue;
1082 struct eosd_target *target = vc->eosd_targets +
1083 vc->eosd_render_count;
1084 int x = sfc->packer->result[i].x;
1085 int y = sfc->packer->result[i].y;
1086 target->source = (VdpRect){x, y, x + p->w, y + p->h};
1087 if (need_upload) {
1088 vdp_st = vdp->
1089 bitmap_surface_put_bits_native(sfc->surface,
1090 (const void *) &p->bitmap,
1091 &p->stride, &target->source);
1092 CHECK_ST_WARNING("EOSD: putbits failed");
1094 // Render dest, color, etc.
1095 target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
1096 target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
1097 target->color.green = ((p->color >> 16) & 0xff) / 255.0;
1098 target->color.red = ((p->color >> 24) & 0xff) / 255.0;
1099 target->dest.x0 = p->dst_x;
1100 target->dest.y0 = p->dst_y;
1101 target->dest.x1 = p->w + p->dst_x;
1102 target->dest.y1 = p->h + p->dst_y;
1103 vc->eosd_render_count++;
1105 } else {
1106 for (int i = 0 ;i < sfc->packer->count; i++) {
1107 struct sub_bitmap *b = &imgs->parts[i];
1108 struct eosd_target *target = vc->eosd_targets +
1109 vc->eosd_render_count;
1110 int x = sfc->packer->result[i].x;
1111 int y = sfc->packer->result[i].y;
1112 target->source = (VdpRect){x, y, x + b->w, y + b->h};
1113 if (need_upload) {
1114 vdp_st = vdp->
1115 bitmap_surface_put_bits_native(sfc->surface,
1116 &(const void *){b->bitmap},
1117 &(uint32_t){b->w * 4},
1118 &target->source);
1119 CHECK_ST_WARNING("EOSD: putbits failed");
1121 target->color = (VdpColor){1, 1, 1, 1};
1122 target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
1123 vc->eosd_render_count++;
1126 vc->bitmap_id = imgs->bitmap_id;
1127 vc->bitmap_pos_id = imgs->bitmap_pos_id;
1130 static void record_osd(void *ctx, int x0, int y0, int w, int h,
1131 unsigned char *src, unsigned char *srca, int stride)
1133 struct vo *vo = ctx;
1134 struct vdpctx *vc = vo->priv;
1136 assert(vc->old_osd_count < MAX_OLD_OSD_BITMAPS);
1137 if (!w || !h)
1138 return;
1139 vc->old_osd_elements[vc->old_osd_count++] = (struct old_osd){
1140 x0, y0, w, h, src, srca, stride};
1143 static void render_old_osd(struct vo *vo)
1145 struct vdpctx *vc = vo->priv;
1146 struct vdp_functions *vdp = vc->vdp;
1147 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
1148 VdpStatus vdp_st;
1149 struct eosd_bitmap_surface *sfc = &vc->osd_surface;
1151 if (!sfc->packer)
1152 sfc->packer = make_packer(vo, VDP_RGBA_FORMAT_A8);
1154 packer_set_size(sfc->packer, vc->old_osd_count * 2);
1155 for (int i = 0; i < vc->old_osd_count; i++) {
1156 struct old_osd *o = &vc->old_osd_elements[i];
1157 sfc->packer->in[i*2] = sfc->packer->in[i*2 + 1] =
1158 (struct pos){o->w, o->h};
1160 int r = packer_pack(sfc->packer);
1161 if (r < 0) {
1162 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] OSD bitmaps do not fit on "
1163 "a surface with the maximum supported size\n");
1164 vc->old_osd_count = 0;
1165 return;
1166 } else if (r == 1) {
1167 if (sfc->surface != VDP_INVALID_HANDLE) {
1168 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
1169 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1171 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
1172 "OSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
1173 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
1174 sfc->packer->w, sfc->packer->h,
1175 true, &sfc->surface);
1176 if (vdp_st != VDP_STATUS_OK)
1177 sfc->surface = VDP_INVALID_HANDLE;
1178 CHECK_ST_WARNING("OSD: error when creating surface");
1181 for (int i = 0; i < vc->old_osd_count; i++) {
1182 struct old_osd *o = &vc->old_osd_elements[i];
1183 struct eosd_target *target1 = &vc->osd_targets[i][0];
1184 struct eosd_target *target2 = &vc->osd_targets[i][1];
1185 int w = o->w, h = o->h;
1186 int sx = sfc->packer->result[i * 2].x;
1187 int sy = sfc->packer->result[i * 2].y;
1188 target1->source = (VdpRect){ sx, sy, sx + w, sy + h };
1189 target1->dest = (VdpRect){ o->x0, o->y0, o->x0 + w, o->y0 + h };
1190 sx = sfc->packer->result[i * 2 + 1].x;
1191 sy = sfc->packer->result[i * 2 + 1].y;
1192 target2->source = (VdpRect){ sx, sy, sx + w, sy + h };
1193 target2->dest = target1->dest;
1194 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1195 &(const void *){o->src},
1196 &(uint32_t){o->stride},
1197 &target1->source);
1198 CHECK_ST_WARNING("OSD: putbits failed");
1199 int size_required = w * h;
1200 if (vc->osd_data_size < size_required) {
1201 talloc_free(vc->osd_data_temp);
1202 vc->osd_data_temp = talloc_size(vc, size_required);
1203 vc->osd_data_size = size_required;
1205 for (int y = 0; y < h; y++)
1206 for (int x = 0; x < w; x++)
1207 vc->osd_data_temp[y * w + x] = -o->srca[y * o->stride + x];
1208 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1209 &(const void *){vc->osd_data_temp},
1210 &(uint32_t){w},
1211 &target2->source);
1212 CHECK_ST_WARNING("OSD: putbits failed");
1215 VdpOutputSurfaceRenderBlendState blend_state_alpha = {
1216 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1217 .blend_factor_source_color =
1218 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
1219 .blend_factor_source_alpha =
1220 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
1221 .blend_factor_destination_color =
1222 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1223 .blend_factor_destination_alpha =
1224 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1225 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1226 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1229 VdpOutputSurfaceRenderBlendState blend_state_gray = {
1230 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1231 .blend_factor_source_color =
1232 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1233 .blend_factor_source_alpha =
1234 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1235 .blend_factor_destination_color =
1236 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1237 .blend_factor_destination_alpha =
1238 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1239 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1240 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1243 for (int i = 0; i < vc->old_osd_count; i++) {
1244 struct eosd_target *target1 = &vc->osd_targets[i][0];
1245 struct eosd_target *target2 = &vc->osd_targets[i][1];
1246 vdp_st = vdp->
1247 output_surface_render_bitmap_surface(output_surface,
1248 &target2->dest,
1249 vc->osd_surface.surface,
1250 &target2->source,
1251 &(VdpColor){1, 1, 1, 1},
1252 &blend_state_alpha,
1253 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1254 CHECK_ST_WARNING("OSD: Error when rendering");
1255 vdp_st = vdp->
1256 output_surface_render_bitmap_surface(output_surface,
1257 &target1->dest,
1258 vc->osd_surface.surface,
1259 &target1->source,
1260 &(VdpColor){1, 1, 1, 1},
1261 &blend_state_gray,
1262 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1263 CHECK_ST_WARNING("OSD: Error when rendering");
1267 static void draw_osd(struct vo *vo, struct osd_state *osd)
1269 struct vdpctx *vc = vo->priv;
1271 if (handle_preemption(vo) < 0)
1272 return;
1274 vc->old_osd_count = 0;
1275 osd_draw_text_ext(osd, vo->dwidth, vo->dheight, vc->border_x, vc->border_y,
1276 vc->border_x, vc->border_y, vc->vid_width,
1277 vc->vid_height, record_osd, vo);
1278 render_old_osd(vo);
1281 static int update_presentation_queue_status(struct vo *vo)
1283 struct vdpctx *vc = vo->priv;
1284 struct vdp_functions *vdp = vc->vdp;
1285 VdpStatus vdp_st;
1287 while (vc->query_surface_num != vc->surface_num) {
1288 VdpTime vtime;
1289 VdpPresentationQueueStatus status;
1290 VdpOutputSurface surface = vc->output_surfaces[vc->query_surface_num];
1291 vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
1292 surface,
1293 &status, &vtime);
1294 CHECK_ST_WARNING("Error calling "
1295 "presentation_queue_query_surface_status");
1296 if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
1297 break;
1298 if (vc->vsync_interval > 1) {
1299 uint64_t qtime = vc->queue_time[vc->query_surface_num];
1300 if (vtime < qtime + vc->vsync_interval / 2)
1301 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown too early\n");
1302 if (vtime > qtime + vc->vsync_interval)
1303 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown late\n");
1305 vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
1306 vc->num_output_surfaces);
1307 vc->recent_vsync_time = vtime;
1309 int num_queued = WRAP_ADD(vc->surface_num, -vc->query_surface_num,
1310 vc->num_output_surfaces);
1311 mp_msg(MSGT_VO, MSGL_DBG3, "[vdpau] Queued surface count (before add): "
1312 "%d\n", num_queued);
1313 return num_queued;
1316 static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
1318 uint64_t offset = ts - vc->recent_vsync_time;
1319 // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
1320 offset += (uint64_t)vc->vsync_interval << shift;
1321 offset %= vc->vsync_interval;
1322 return ts - offset;
1325 static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
1327 struct vdpctx *vc = vo->priv;
1328 struct vdp_functions *vdp = vc->vdp;
1329 VdpStatus vdp_st;
1330 uint32_t vsync_interval = vc->vsync_interval;
1332 if (handle_preemption(vo) < 0)
1333 return;
1335 if (duration > INT_MAX / 1000)
1336 duration = -1;
1337 else
1338 duration *= 1000;
1340 if (vc->vsync_interval == 1)
1341 duration = -1; // Make sure drop logic is disabled
1343 uint64_t now = sync_vdptime(vo);
1344 uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
1345 uint64_t ideal_pts = pts;
1346 uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
1348 #define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
1349 // Only gives accurate results for ts >= vc->recent_vsync_time
1350 #define PREV_VSYNC(ts) PREV_VS2(ts, 0)
1352 /* We hope to be here at least one vsync before the frame should be shown.
1353 * If we are running late then don't drop the frame unless there is
1354 * already one queued for the next vsync; even if we _hope_ to show the
1355 * next frame soon enough to mean this one should be dropped we might
1356 * not make the target time in reality. Without this check we could drop
1357 * every frame, freezing the display completely if video lags behind.
1359 if (now > PREV_VSYNC(FFMAX(pts, vc->last_queue_time + vsync_interval)))
1360 npts = UINT64_MAX;
1362 /* Allow flipping a frame at a vsync if its presentation time is a
1363 * bit after that vsync and the change makes the flip time delta
1364 * from previous frame better match the target timestamp delta.
1365 * This avoids instability with frame timestamps falling near vsyncs.
1366 * For example if the frame timestamps were (with vsyncs at
1367 * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
1368 * straightforward timing at next vsync would flip the frames at
1369 * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
1370 * regular 2-vsync intervals.
1372 * Also allow moving the frame forward if it looks like we dropped
1373 * the previous frame incorrectly (now that we know better after
1374 * having final exact timestamp information for this frame) and
1375 * there would unnecessarily be a vsync without a frame change.
1377 uint64_t vsync = PREV_VSYNC(pts);
1378 if (pts < vsync + vsync_interval / 4
1379 && (vsync - PREV_VS2(vc->last_queue_time, 16)
1380 > pts - vc->last_ideal_time + vsync_interval / 2
1381 || vc->dropped_frame && vsync > vc->dropped_time))
1382 pts -= vsync_interval / 2;
1384 vc->dropped_frame = true; // changed at end if false
1385 vc->dropped_time = ideal_pts;
1387 pts = FFMAX(pts, vc->last_queue_time + vsync_interval);
1388 pts = FFMAX(pts, now);
1389 if (npts < PREV_VSYNC(pts) + vsync_interval)
1390 return;
1392 int num_flips = update_presentation_queue_status(vo);
1393 vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
1394 now = sync_vdptime(vo);
1395 pts = FFMAX(pts, now);
1396 pts = FFMAX(pts, vsync + (vsync_interval >> 2));
1397 vsync = PREV_VSYNC(pts);
1398 if (npts < vsync + vsync_interval)
1399 return;
1400 pts = vsync + (vsync_interval >> 2);
1401 vdp_st =
1402 vdp->presentation_queue_display(vc->flip_queue,
1403 vc->output_surfaces[vc->surface_num],
1404 vo->dwidth, vo->dheight, pts);
1405 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display");
1407 vc->last_queue_time = pts;
1408 vc->queue_time[vc->surface_num] = pts;
1409 vc->last_ideal_time = ideal_pts;
1410 vc->dropped_frame = false;
1411 vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
1414 static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
1415 int h, int x, int y)
1417 struct vdpctx *vc = vo->priv;
1418 struct vdp_functions *vdp = vc->vdp;
1419 VdpStatus vdp_st;
1421 if (handle_preemption(vo) < 0)
1422 return VO_TRUE;
1424 struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
1425 int max_refs = vc->image_format == IMGFMT_VDPAU_H264 ?
1426 rndr->info.h264.num_ref_frames : 2;
1427 if (!IMGFMT_IS_VDPAU(vc->image_format))
1428 return VO_FALSE;
1429 if ((vc->decoder == VDP_INVALID_HANDLE || vc->decoder_max_refs < max_refs)
1430 && !create_vdp_decoder(vo, max_refs))
1431 return VO_FALSE;
1433 vdp_st = vdp->decoder_render(vc->decoder, rndr->surface,
1434 (void *)&rndr->info,
1435 rndr->bitstream_buffers_used,
1436 rndr->bitstream_buffers);
1437 CHECK_ST_WARNING("Failed VDPAU decoder rendering");
1438 return VO_TRUE;
1442 static struct vdpau_render_state *get_surface(struct vo *vo, int number)
1444 struct vdpctx *vc = vo->priv;
1445 struct vdp_functions *vdp = vc->vdp;
1447 if (number >= MAX_VIDEO_SURFACES)
1448 return NULL;
1449 if (vc->surface_render[number].surface == VDP_INVALID_HANDLE
1450 && !vc->is_preempted) {
1451 VdpStatus vdp_st;
1452 vdp_st = vdp->video_surface_create(vc->vdp_device, vc->vdp_chroma_type,
1453 vc->vid_width, vc->vid_height,
1454 &vc->surface_render[number].surface);
1455 CHECK_ST_WARNING("Error when calling vdp_video_surface_create");
1457 mp_msg(MSGT_VO, MSGL_DBG3, "vdpau vid create: %u\n",
1458 vc->surface_render[number].surface);
1459 return &vc->surface_render[number];
1462 static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
1464 struct vdpctx *vc = vo->priv;
1465 struct vdp_functions *vdp = vc->vdp;
1466 struct mp_image *reserved_mpi = NULL;
1467 struct vdpau_render_state *rndr;
1469 if (IMGFMT_IS_VDPAU(vc->image_format)) {
1470 rndr = mpi->priv;
1471 reserved_mpi = mpi;
1472 } else if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
1473 rndr = get_surface(vo, vc->deint_counter);
1474 vc->deint_counter = WRAP_ADD(vc->deint_counter, 1, NUM_BUFFERED_VIDEO);
1475 if (handle_preemption(vo) >= 0) {
1476 VdpStatus vdp_st;
1477 const void *destdata[3] = {mpi->planes[0], mpi->planes[2],
1478 mpi->planes[1]};
1479 if (vc->image_format == IMGFMT_NV12)
1480 destdata[1] = destdata[2];
1481 vdp_st = vdp->video_surface_put_bits_y_cb_cr(rndr->surface,
1482 vc->vdp_pixel_format, destdata, mpi->stride);
1483 CHECK_ST_WARNING("Error when calling "
1484 "vdp_video_surface_put_bits_y_cb_cr");
1486 } else
1487 // We don't support slice callbacks so this shouldn't occur -
1488 // I think the flags test above in pointless, but I'm adding
1489 // this instead of removing it just in case.
1490 abort();
1491 if (mpi->fields & MP_IMGFIELD_ORDERED)
1492 vc->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
1493 else
1494 vc->top_field_first = 1;
1496 add_new_video_surface(vo, rndr->surface, reserved_mpi, pts);
1498 return;
1501 // warning: the size and pixel format of surface must match that of the
1502 // surfaces in vc->output_surfaces
1503 static struct mp_image *read_output_surface(struct vdpctx *vc,
1504 VdpOutputSurface surface,
1505 int width, int height)
1507 VdpStatus vdp_st;
1508 struct vdp_functions *vdp = vc->vdp;
1509 struct mp_image *image = alloc_mpi(width, height, IMGFMT_BGR32);
1511 void *dst_planes[] = { image->planes[0] };
1512 uint32_t dst_pitches[] = { image->stride[0] };
1513 vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes,
1514 dst_pitches);
1515 CHECK_ST_WARNING("Error when calling vdp_output_surface_get_bits_native");
1517 return image;
1520 static struct mp_image *get_screenshot(struct vo *vo)
1522 struct vdpctx *vc = vo->priv;
1523 VdpStatus vdp_st;
1524 struct vdp_functions *vdp = vc->vdp;
1526 if (vc->screenshot_surface == VDP_INVALID_HANDLE) {
1527 vdp_st = vdp->output_surface_create(vc->vdp_device,
1528 OUTPUT_RGBA_FORMAT,
1529 vc->vid_width, vc->vid_height,
1530 &vc->screenshot_surface);
1531 CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
1534 VdpRect rc = { .x1 = vc->vid_width, .y1 = vc->vid_height };
1535 render_video_to_output_surface(vo, vc->screenshot_surface, &rc);
1537 struct mp_image *image = read_output_surface(vc, vc->screenshot_surface,
1538 vc->vid_width, vc->vid_height);
1540 image->width = vc->vid_width;
1541 image->height = vc->vid_height;
1542 image->w = vc->vid_d_width;
1543 image->h = vc->vid_d_height;
1545 return image;
1548 static struct mp_image *get_window_screenshot(struct vo *vo)
1550 struct vdpctx *vc = vo->priv;
1551 int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces);
1552 VdpOutputSurface screen = vc->output_surfaces[last_surface];
1553 struct mp_image *image = read_output_surface(vo->priv, screen,
1554 vc->output_surface_width,
1555 vc->output_surface_height);
1556 image->width = image->w = vo->dwidth;
1557 image->height = image->h = vo->dheight;
1558 return image;
1561 static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
1563 struct vdpctx *vc = vo->priv;
1564 struct vdpau_render_state *rndr;
1566 // no dr for non-decoding for now
1567 if (!IMGFMT_IS_VDPAU(vc->image_format))
1568 return VO_FALSE;
1569 if (mpi->type != MP_IMGTYPE_NUMBERED)
1570 return VO_FALSE;
1572 rndr = get_surface(vo, mpi->number);
1573 if (!rndr) {
1574 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
1575 "get_image\n");
1576 // TODO: this probably breaks things forever, provide a dummy buffer?
1577 return VO_FALSE;
1579 mpi->flags |= MP_IMGFLAG_DIRECT;
1580 mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
1581 mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = NULL;
1582 // hack to get around a check and to avoid a special-case in vd_ffmpeg.c
1583 mpi->planes[0] = (void *)rndr;
1584 mpi->num_planes = 1;
1585 mpi->priv = rndr;
1586 return VO_TRUE;
1589 static int query_format(uint32_t format)
1591 int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
1592 | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD
1593 | VFCAP_EOSD_UNSCALED | VFCAP_EOSD_RGBA | VFCAP_FLIP;
1594 switch (format) {
1595 case IMGFMT_YV12:
1596 case IMGFMT_I420:
1597 case IMGFMT_IYUV:
1598 case IMGFMT_NV12:
1599 case IMGFMT_YUY2:
1600 case IMGFMT_UYVY:
1601 return default_flags | VOCAP_NOSLICES;
1602 case IMGFMT_VDPAU_MPEG1:
1603 case IMGFMT_VDPAU_MPEG2:
1604 case IMGFMT_VDPAU_H264:
1605 case IMGFMT_VDPAU_WMV3:
1606 case IMGFMT_VDPAU_VC1:
1607 case IMGFMT_VDPAU_MPEG4:
1608 return default_flags;
1610 return 0;
1613 static void destroy_vdpau_objects(struct vo *vo)
1615 struct vdpctx *vc = vo->priv;
1616 struct vdp_functions *vdp = vc->vdp;
1618 int i;
1619 VdpStatus vdp_st;
1621 free_video_specific(vo);
1623 if (vc->flip_queue != VDP_INVALID_HANDLE) {
1624 vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
1625 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy");
1628 if (vc->flip_target != VDP_INVALID_HANDLE) {
1629 vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
1630 CHECK_ST_WARNING("Error when calling "
1631 "vdp_presentation_queue_target_destroy");
1634 for (i = 0; i < vc->num_output_surfaces; i++) {
1635 if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
1636 continue;
1637 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
1638 CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
1641 if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
1642 vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
1643 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1646 if (vc->osd_surface.surface != VDP_INVALID_HANDLE) {
1647 vdp_st = vdp->bitmap_surface_destroy(vc->osd_surface.surface);
1648 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1651 vdp_st = vdp->device_destroy(vc->vdp_device);
1652 CHECK_ST_WARNING("Error when calling vdp_device_destroy");
1655 static void uninit(struct vo *vo)
1657 struct vdpctx *vc = vo->priv;
1659 /* Destroy all vdpau objects */
1660 destroy_vdpau_objects(vo);
1662 #ifdef CONFIG_XF86VM
1663 if (vc->mode_switched)
1664 vo_vm_close(vo);
1665 #endif
1666 vo_x11_uninit(vo);
1668 // Free bitstream buffers allocated by FFmpeg
1669 for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
1670 av_freep(&vc->surface_render[i].bitstream_buffers);
1673 static int preinit(struct vo *vo, const char *arg)
1675 struct vdpctx *vc = vo->priv;
1677 // Mark everything as invalid first so uninit() can tell what has been
1678 // allocated
1679 mark_vdpau_objects_uninitialized(vo);
1681 vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
1682 vc->video_eq.capabilities = MP_CSP_EQ_CAPS_COLORMATRIX;
1684 vc->deint_type = vc->deint ? FFABS(vc->deint) : 3;
1685 if (vc->deint < 0)
1686 vc->deint = 0;
1688 if (!vo_init(vo))
1689 return -1;
1691 // After this calling uninit() should work to free resources
1693 if (win_x11_init_vdpau_procs(vo) < 0) {
1694 if (vc->vdp->device_destroy)
1695 vc->vdp->device_destroy(vc->vdp_device);
1696 vo_x11_uninit(vo);
1697 return -1;
1700 return 0;
1703 static int get_equalizer(struct vo *vo, const char *name, int *value)
1705 struct vdpctx *vc = vo->priv;
1706 return mp_csp_equalizer_get(&vc->video_eq, name, value) >= 0 ?
1707 VO_TRUE : VO_NOTIMPL;
1710 static bool status_ok(struct vo *vo)
1712 if (!vo->config_ok || handle_preemption(vo) < 0)
1713 return false;
1714 return true;
1717 static int set_equalizer(struct vo *vo, const char *name, int value)
1719 struct vdpctx *vc = vo->priv;
1721 if (mp_csp_equalizer_set(&vc->video_eq, name, value) < 0)
1722 return VO_NOTIMPL;
1724 if (status_ok(vo))
1725 update_csc_matrix(vo);
1726 return true;
1729 static void checked_resize(struct vo *vo)
1731 if (!status_ok(vo))
1732 return;
1733 resize(vo);
1736 static int control(struct vo *vo, uint32_t request, void *data)
1738 struct vdpctx *vc = vo->priv;
1739 struct vdp_functions *vdp = vc->vdp;
1741 handle_preemption(vo);
1743 switch (request) {
1744 case VOCTRL_GET_DEINTERLACE:
1745 *(int *)data = vc->deint;
1746 return VO_TRUE;
1747 case VOCTRL_SET_DEINTERLACE:
1748 vc->deint = *(int *)data;
1749 if (vc->deint)
1750 vc->deint = vc->deint_type;
1751 if (vc->deint_type > 2 && status_ok(vo)) {
1752 VdpStatus vdp_st;
1753 VdpVideoMixerFeature features[1] =
1754 {vc->deint_type == 3 ?
1755 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL :
1756 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL};
1757 VdpBool feature_enables[1] = {vc->deint ? VDP_TRUE : VDP_FALSE};
1758 vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
1759 1, features,
1760 feature_enables);
1761 CHECK_ST_WARNING("Error changing deinterlacing settings");
1763 vo->want_redraw = true;
1764 return VO_TRUE;
1765 case VOCTRL_PAUSE:
1766 if (vc->dropped_frame)
1767 vo->want_redraw = true;
1768 return true;
1769 case VOCTRL_QUERY_FORMAT:
1770 return query_format(*(uint32_t *)data);
1771 case VOCTRL_GET_IMAGE:
1772 return get_image(vo, data);
1773 case VOCTRL_DRAW_IMAGE:
1774 abort(); // draw_image() should get called directly
1775 case VOCTRL_BORDER:
1776 vo_x11_border(vo);
1777 checked_resize(vo);
1778 return VO_TRUE;
1779 case VOCTRL_FULLSCREEN:
1780 vo_x11_fullscreen(vo);
1781 checked_resize(vo);
1782 return VO_TRUE;
1783 case VOCTRL_GET_PANSCAN:
1784 return VO_TRUE;
1785 case VOCTRL_SET_PANSCAN:
1786 checked_resize(vo);
1787 return VO_TRUE;
1788 case VOCTRL_SET_EQUALIZER: {
1789 vo->want_redraw = true;
1790 struct voctrl_set_equalizer_args *args = data;
1791 return set_equalizer(vo, args->name, args->value);
1793 case VOCTRL_GET_EQUALIZER: {
1794 struct voctrl_get_equalizer_args *args = data;
1795 return get_equalizer(vo, args->name, args->valueptr);
1797 case VOCTRL_SET_YUV_COLORSPACE:
1798 vc->colorspace = *(struct mp_csp_details *)data;
1799 if (status_ok(vo))
1800 update_csc_matrix(vo);
1801 vo->want_redraw = true;
1802 return true;
1803 case VOCTRL_GET_YUV_COLORSPACE:
1804 *(struct mp_csp_details *)data = vc->colorspace;
1805 return true;
1806 case VOCTRL_ONTOP:
1807 vo_x11_ontop(vo);
1808 return VO_TRUE;
1809 case VOCTRL_UPDATE_SCREENINFO:
1810 update_xinerama_info(vo);
1811 return VO_TRUE;
1812 case VOCTRL_DRAW_EOSD:
1813 if (!data)
1814 return VO_FALSE;
1815 if (status_ok(vo)) {
1816 generate_eosd(vo, data);
1817 draw_eosd(vo);
1819 return VO_TRUE;
1820 case VOCTRL_GET_EOSD_RES: {
1821 struct mp_eosd_res *r = data;
1822 r->w = vo->dwidth;
1823 r->h = vo->dheight;
1824 r->ml = r->mr = vc->border_x;
1825 r->mt = r->mb = vc->border_y;
1826 return VO_TRUE;
1828 case VOCTRL_NEWFRAME:
1829 vc->deint_queue_pos = next_deint_queue_pos(vo, true);
1830 if (status_ok(vo))
1831 video_to_output_surface(vo);
1832 return true;
1833 case VOCTRL_SKIPFRAME:
1834 vc->deint_queue_pos = next_deint_queue_pos(vo, true);
1835 return true;
1836 case VOCTRL_REDRAW_FRAME:
1837 if (status_ok(vo))
1838 video_to_output_surface(vo);
1839 return true;
1840 case VOCTRL_RESET:
1841 forget_frames(vo);
1842 return true;
1843 case VOCTRL_SCREENSHOT: {
1844 if (!status_ok(vo))
1845 return false;
1846 struct voctrl_screenshot_args *args = data;
1847 if (args->full_window)
1848 args->out_image = get_window_screenshot(vo);
1849 else
1850 args->out_image = get_screenshot(vo);
1851 return true;
1854 return VO_NOTIMPL;
1857 #undef OPT_BASE_STRUCT
1858 #define OPT_BASE_STRUCT struct vdpctx
1860 const struct vo_driver video_out_vdpau = {
1861 .is_new = true,
1862 .buffer_frames = true,
1863 .info = &(const struct vo_info_s){
1864 "VDPAU with X11",
1865 "vdpau",
1866 "Rajib Mahapatra <rmahapatra@nvidia.com> and others",
1869 .preinit = preinit,
1870 .config = config,
1871 .control = control,
1872 .draw_image = draw_image,
1873 .get_buffered_frame = set_next_frame_info,
1874 .draw_slice = draw_slice,
1875 .draw_osd = draw_osd,
1876 .flip_page_timed = flip_page_timed,
1877 .check_events = check_events,
1878 .uninit = uninit,
1879 .privsize = sizeof(struct vdpctx),
1880 .options = (const struct m_option []){
1881 OPT_INTRANGE("deint", deint, 0, -4, 4),
1882 OPT_FLAG_ON("chroma-deint", chroma_deint, 0, OPTDEF_INT(1)),
1883 OPT_FLAG_OFF("nochroma-deint", chroma_deint, 0),
1884 OPT_MAKE_FLAGS("pullup", pullup, 0),
1885 OPT_FLOATRANGE("denoise", denoise, 0, 0, 1),
1886 OPT_FLOATRANGE("sharpen", sharpen, 0, -1, 1),
1887 OPT_ERRORMESSAGE("colorspace", "vo_vdpau suboption \"colorspace\" has "
1888 "been removed. Use --colormatrix instead.\n"),
1889 OPT_ERRORMESSAGE("studio", "vo_vdpau suboption \"studio\" has been "
1890 "removed. Use --colormatrix-output-range=limited "
1891 "instead.\n"),
1892 OPT_INTRANGE("hqscaling", hqscaling, 0, 0, 9),
1893 OPT_FLOAT("fps", user_fps, 0),
1894 OPT_FLAG_ON("composite-detect", composite_detect, 0, OPTDEF_INT(1)),
1895 OPT_INT("queuetime_windowed", flip_offset_window, 0, OPTDEF_INT(50)),
1896 OPT_INT("queuetime_fs", flip_offset_fs, 0, OPTDEF_INT(50)),
1897 OPT_INTRANGE("output_surfaces", num_output_surfaces, 0,
1898 2, MAX_OUTPUT_SURFACES, OPTDEF_INT(3)),
1899 {NULL},