vo_vdpau: fix off-by-one in rare error check
[mplayer.git] / libvo / vo_vdpau.c
blob0c99f5dfb2c13658bd8fcad0b9e740a3f7290564
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;
873 int depth;
875 #ifdef CONFIG_XF86VM
876 int vm = flags & VOFLAG_MODESWITCHING;
877 #endif
879 if (handle_preemption(vo) < 0)
880 return -1;
882 vc->flip = flags & VOFLAG_FLIPPING;
883 vc->image_format = format;
884 vc->vid_width = width;
885 vc->vid_height = height;
886 vc->vid_d_width = d_width;
887 vc->vid_d_height = d_height;
889 free_video_specific(vo);
890 if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
891 return -1;
893 #ifdef CONFIG_XF86VM
894 if (vm) {
895 vo_vm_switch(vo);
896 vc->mode_switched = true;
898 #endif
899 XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
900 &attribs);
901 depth = attribs.depth;
902 if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
903 depth = 24;
904 XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
906 xswa.background_pixel = 0;
907 xswa.border_pixel = 0;
908 /* Do not use CWBackPixel: It leads to VDPAU errors after
909 * aspect ratio changes. */
910 xswamask = CWBorderPixel;
912 vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, d_width, d_height,
913 flags, CopyFromParent, "vdpau");
914 XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
916 #ifdef CONFIG_XF86VM
917 if (vm) {
918 /* Grab the mouse pointer in our window */
919 if (vo_grabpointer)
920 XGrabPointer(x11->display, x11->window, True, 0,
921 GrabModeAsync, GrabModeAsync,
922 x11->window, None, CurrentTime);
923 XSetInputFocus(x11->display, x11->window, RevertToNone, CurrentTime);
925 #endif
927 if ((flags & VOFLAG_FULLSCREEN) && WinID <= 0)
928 vo_fs = 1;
930 if (initialize_vdpau_objects(vo) < 0)
931 return -1;
933 return 0;
936 static void check_events(struct vo *vo)
938 if (handle_preemption(vo) < 0)
939 return;
941 int e = vo_x11_check_events(vo);
943 if (e & VO_EVENT_RESIZE)
944 resize(vo);
945 else if (e & VO_EVENT_EXPOSE) {
946 vo->want_redraw = true;
950 static struct bitmap_packer *make_packer(struct vo *vo, VdpRGBAFormat format)
952 struct vdpctx *vc = vo->priv;
953 struct vdp_functions *vdp = vc->vdp;
955 struct bitmap_packer *packer = talloc_zero(vo, struct bitmap_packer);
956 uint32_t w_max = 0, h_max = 0;
957 VdpStatus vdp_st = vdp->
958 bitmap_surface_query_capabilities(vc->vdp_device, format,
959 &(VdpBool){0}, &w_max, &h_max);
960 CHECK_ST_WARNING("Query to get max EOSD surface size failed");
961 packer->w_max = w_max;
962 packer->h_max = h_max;
963 return packer;
966 static void draw_eosd(struct vo *vo)
968 struct vdpctx *vc = vo->priv;
969 struct vdp_functions *vdp = vc->vdp;
970 VdpStatus vdp_st;
971 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
972 int i;
974 VdpOutputSurfaceRenderBlendState blend_state = {
975 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
976 .blend_factor_source_color =
977 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
978 .blend_factor_source_alpha =
979 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
980 .blend_factor_destination_color =
981 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
982 .blend_factor_destination_alpha =
983 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
984 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
985 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
988 for (i = 0; i < vc->eosd_render_count; i++) {
989 vdp_st = vdp->
990 output_surface_render_bitmap_surface(output_surface,
991 &vc->eosd_targets[i].dest,
992 vc->eosd_surface.surface,
993 &vc->eosd_targets[i].source,
994 &vc->eosd_targets[i].color,
995 &blend_state,
996 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
997 CHECK_ST_WARNING("EOSD: Error when rendering");
1001 static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
1003 struct vdpctx *vc = vo->priv;
1004 struct vdp_functions *vdp = vc->vdp;
1005 VdpStatus vdp_st;
1006 struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
1007 bool need_upload = false;
1009 if (imgs->bitmap_pos_id == vc->bitmap_pos_id)
1010 return; // Nothing changed and we still have the old data
1012 vc->eosd_render_count = 0;
1014 if (imgs->type == SUBBITMAP_EMPTY)
1015 return;
1017 if (imgs->bitmap_id == vc->bitmap_id)
1018 goto eosd_skip_upload;
1020 need_upload = true;
1021 VdpRGBAFormat format;
1022 int format_size;
1023 switch (imgs->type) {
1024 case SUBBITMAP_LIBASS:
1025 format = VDP_RGBA_FORMAT_A8;
1026 format_size = 1;
1027 break;
1028 case SUBBITMAP_RGBA:
1029 format = VDP_RGBA_FORMAT_B8G8R8A8;
1030 format_size = 4;
1031 break;
1032 default:
1033 abort();
1035 if (sfc->format != format) {
1036 talloc_free(sfc->packer);
1037 sfc->packer = NULL;
1039 sfc->format = format;
1040 if (!sfc->packer)
1041 sfc->packer = make_packer(vo, format);
1042 int r = packer_pack_from_subbitmaps(sfc->packer, imgs, imgs->scaled);
1043 if (r < 0) {
1044 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
1045 "a surface with the maximum supported size\n");
1046 return;
1047 } else if (r == 1) {
1048 if (sfc->surface != VDP_INVALID_HANDLE) {
1049 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
1050 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1052 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
1053 "EOSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
1054 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
1055 sfc->packer->w, sfc->packer->h,
1056 true, &sfc->surface);
1057 if (vdp_st != VDP_STATUS_OK)
1058 sfc->surface = VDP_INVALID_HANDLE;
1059 CHECK_ST_WARNING("EOSD: error when creating surface");
1061 if (imgs->scaled) {
1062 char zeros[sfc->packer->used_width * format_size];
1063 memset(zeros, 0, sizeof(zeros));
1064 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1065 &(const void *){zeros}, &(uint32_t){0},
1066 &(VdpRect){0, 0, sfc->packer->used_width,
1067 sfc->packer->used_height});
1070 eosd_skip_upload:
1071 if (sfc->surface == VDP_INVALID_HANDLE)
1072 return;
1073 if (sfc->packer->count > vc->eosd_targets_size) {
1074 talloc_free(vc->eosd_targets);
1075 vc->eosd_targets_size = sfc->packer->count;
1076 vc->eosd_targets = talloc_size(vc, vc->eosd_targets_size
1077 * sizeof(*vc->eosd_targets));
1080 if (imgs->type == SUBBITMAP_LIBASS) {
1081 int i = 0;
1082 for (ASS_Image *p = imgs->imgs; p; p = p->next, i++) {
1083 if (p->w == 0 || p->h == 0)
1084 continue;
1085 struct eosd_target *target = vc->eosd_targets +
1086 vc->eosd_render_count;
1087 int x = sfc->packer->result[i].x;
1088 int y = sfc->packer->result[i].y;
1089 target->source = (VdpRect){x, y, x + p->w, y + p->h};
1090 if (need_upload) {
1091 vdp_st = vdp->
1092 bitmap_surface_put_bits_native(sfc->surface,
1093 (const void *) &p->bitmap,
1094 &p->stride, &target->source);
1095 CHECK_ST_WARNING("EOSD: putbits failed");
1097 // Render dest, color, etc.
1098 target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
1099 target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
1100 target->color.green = ((p->color >> 16) & 0xff) / 255.0;
1101 target->color.red = ((p->color >> 24) & 0xff) / 255.0;
1102 target->dest.x0 = p->dst_x;
1103 target->dest.y0 = p->dst_y;
1104 target->dest.x1 = p->w + p->dst_x;
1105 target->dest.y1 = p->h + p->dst_y;
1106 vc->eosd_render_count++;
1108 } else {
1109 for (int i = 0 ;i < sfc->packer->count; i++) {
1110 struct sub_bitmap *b = &imgs->parts[i];
1111 struct eosd_target *target = vc->eosd_targets +
1112 vc->eosd_render_count;
1113 int x = sfc->packer->result[i].x;
1114 int y = sfc->packer->result[i].y;
1115 target->source = (VdpRect){x, y, x + b->w, y + b->h};
1116 if (need_upload) {
1117 vdp_st = vdp->
1118 bitmap_surface_put_bits_native(sfc->surface,
1119 &(const void *){b->bitmap},
1120 &(uint32_t){b->w * 4},
1121 &target->source);
1122 CHECK_ST_WARNING("EOSD: putbits failed");
1124 target->color = (VdpColor){1, 1, 1, 1};
1125 target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
1126 vc->eosd_render_count++;
1129 vc->bitmap_id = imgs->bitmap_id;
1130 vc->bitmap_pos_id = imgs->bitmap_pos_id;
1133 static void record_osd(void *ctx, int x0, int y0, int w, int h,
1134 unsigned char *src, unsigned char *srca, int stride)
1136 struct vo *vo = ctx;
1137 struct vdpctx *vc = vo->priv;
1139 assert(vc->old_osd_count < MAX_OLD_OSD_BITMAPS);
1140 if (!w || !h)
1141 return;
1142 vc->old_osd_elements[vc->old_osd_count++] = (struct old_osd){
1143 x0, y0, w, h, src, srca, stride};
1146 static void render_old_osd(struct vo *vo)
1148 struct vdpctx *vc = vo->priv;
1149 struct vdp_functions *vdp = vc->vdp;
1150 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
1151 VdpStatus vdp_st;
1152 struct eosd_bitmap_surface *sfc = &vc->osd_surface;
1154 if (!sfc->packer)
1155 sfc->packer = make_packer(vo, VDP_RGBA_FORMAT_A8);
1157 packer_set_size(sfc->packer, vc->old_osd_count * 2);
1158 for (int i = 0; i < vc->old_osd_count; i++) {
1159 struct old_osd *o = &vc->old_osd_elements[i];
1160 sfc->packer->in[i*2] = sfc->packer->in[i*2 + 1] =
1161 (struct pos){o->w, o->h};
1163 int r = packer_pack(sfc->packer);
1164 if (r < 0) {
1165 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] OSD bitmaps do not fit on "
1166 "a surface with the maximum supported size\n");
1167 vc->old_osd_count = 0;
1168 return;
1169 } else if (r == 1) {
1170 if (sfc->surface != VDP_INVALID_HANDLE) {
1171 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
1172 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1174 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
1175 "OSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
1176 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
1177 sfc->packer->w, sfc->packer->h,
1178 true, &sfc->surface);
1179 if (vdp_st != VDP_STATUS_OK)
1180 sfc->surface = VDP_INVALID_HANDLE;
1181 CHECK_ST_WARNING("OSD: error when creating surface");
1184 for (int i = 0; i < vc->old_osd_count; i++) {
1185 struct old_osd *o = &vc->old_osd_elements[i];
1186 struct eosd_target *target1 = &vc->osd_targets[i][0];
1187 struct eosd_target *target2 = &vc->osd_targets[i][1];
1188 int w = o->w, h = o->h;
1189 int sx = sfc->packer->result[i * 2].x;
1190 int sy = sfc->packer->result[i * 2].y;
1191 target1->source = (VdpRect){ sx, sy, sx + w, sy + h };
1192 target1->dest = (VdpRect){ o->x0, o->y0, o->x0 + w, o->y0 + h };
1193 sx = sfc->packer->result[i * 2 + 1].x;
1194 sy = sfc->packer->result[i * 2 + 1].y;
1195 target2->source = (VdpRect){ sx, sy, sx + w, sy + h };
1196 target2->dest = target1->dest;
1197 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1198 &(const void *){o->src},
1199 &(uint32_t){o->stride},
1200 &target1->source);
1201 CHECK_ST_WARNING("OSD: putbits failed");
1202 int size_required = w * h;
1203 if (vc->osd_data_size < size_required) {
1204 talloc_free(vc->osd_data_temp);
1205 vc->osd_data_temp = talloc_size(vc, size_required);
1206 vc->osd_data_size = size_required;
1208 for (int y = 0; y < h; y++)
1209 for (int x = 0; x < w; x++)
1210 vc->osd_data_temp[y * w + x] = -o->srca[y * o->stride + x];
1211 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
1212 &(const void *){vc->osd_data_temp},
1213 &(uint32_t){w},
1214 &target2->source);
1215 CHECK_ST_WARNING("OSD: putbits failed");
1218 VdpOutputSurfaceRenderBlendState blend_state_alpha = {
1219 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1220 .blend_factor_source_color =
1221 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
1222 .blend_factor_source_alpha =
1223 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
1224 .blend_factor_destination_color =
1225 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1226 .blend_factor_destination_alpha =
1227 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1228 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1229 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1232 VdpOutputSurfaceRenderBlendState blend_state_gray = {
1233 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
1234 .blend_factor_source_color =
1235 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1236 .blend_factor_source_alpha =
1237 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
1238 .blend_factor_destination_color =
1239 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1240 .blend_factor_destination_alpha =
1241 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
1242 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1243 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
1246 for (int i = 0; i < vc->old_osd_count; i++) {
1247 struct eosd_target *target1 = &vc->osd_targets[i][0];
1248 struct eosd_target *target2 = &vc->osd_targets[i][1];
1249 vdp_st = vdp->
1250 output_surface_render_bitmap_surface(output_surface,
1251 &target2->dest,
1252 vc->osd_surface.surface,
1253 &target2->source,
1254 &(VdpColor){1, 1, 1, 1},
1255 &blend_state_alpha,
1256 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1257 CHECK_ST_WARNING("OSD: Error when rendering");
1258 vdp_st = vdp->
1259 output_surface_render_bitmap_surface(output_surface,
1260 &target1->dest,
1261 vc->osd_surface.surface,
1262 &target1->source,
1263 &(VdpColor){1, 1, 1, 1},
1264 &blend_state_gray,
1265 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
1266 CHECK_ST_WARNING("OSD: Error when rendering");
1270 static void draw_osd(struct vo *vo, struct osd_state *osd)
1272 struct vdpctx *vc = vo->priv;
1274 if (handle_preemption(vo) < 0)
1275 return;
1277 vc->old_osd_count = 0;
1278 osd_draw_text_ext(osd, vo->dwidth, vo->dheight, vc->border_x, vc->border_y,
1279 vc->border_x, vc->border_y, vc->vid_width,
1280 vc->vid_height, record_osd, vo);
1281 render_old_osd(vo);
1284 static int update_presentation_queue_status(struct vo *vo)
1286 struct vdpctx *vc = vo->priv;
1287 struct vdp_functions *vdp = vc->vdp;
1288 VdpStatus vdp_st;
1290 while (vc->query_surface_num != vc->surface_num) {
1291 VdpTime vtime;
1292 VdpPresentationQueueStatus status;
1293 VdpOutputSurface surface = vc->output_surfaces[vc->query_surface_num];
1294 vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
1295 surface,
1296 &status, &vtime);
1297 CHECK_ST_WARNING("Error calling "
1298 "presentation_queue_query_surface_status");
1299 if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
1300 break;
1301 if (vc->vsync_interval > 1) {
1302 uint64_t qtime = vc->queue_time[vc->query_surface_num];
1303 if (vtime < qtime + vc->vsync_interval / 2)
1304 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown too early\n");
1305 if (vtime > qtime + vc->vsync_interval)
1306 mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown late\n");
1308 vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
1309 vc->num_output_surfaces);
1310 vc->recent_vsync_time = vtime;
1312 int num_queued = WRAP_ADD(vc->surface_num, -vc->query_surface_num,
1313 vc->num_output_surfaces);
1314 mp_msg(MSGT_VO, MSGL_DBG3, "[vdpau] Queued surface count (before add): "
1315 "%d\n", num_queued);
1316 return num_queued;
1319 static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
1321 uint64_t offset = ts - vc->recent_vsync_time;
1322 // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
1323 offset += (uint64_t)vc->vsync_interval << shift;
1324 offset %= vc->vsync_interval;
1325 return ts - offset;
1328 static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
1330 struct vdpctx *vc = vo->priv;
1331 struct vdp_functions *vdp = vc->vdp;
1332 VdpStatus vdp_st;
1333 uint32_t vsync_interval = vc->vsync_interval;
1335 if (handle_preemption(vo) < 0)
1336 return;
1338 if (duration > INT_MAX / 1000)
1339 duration = -1;
1340 else
1341 duration *= 1000;
1343 if (vc->vsync_interval == 1)
1344 duration = -1; // Make sure drop logic is disabled
1346 uint64_t now = sync_vdptime(vo);
1347 uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
1348 uint64_t ideal_pts = pts;
1349 uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
1351 #define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
1352 // Only gives accurate results for ts >= vc->recent_vsync_time
1353 #define PREV_VSYNC(ts) PREV_VS2(ts, 0)
1355 /* We hope to be here at least one vsync before the frame should be shown.
1356 * If we are running late then don't drop the frame unless there is
1357 * already one queued for the next vsync; even if we _hope_ to show the
1358 * next frame soon enough to mean this one should be dropped we might
1359 * not make the target time in reality. Without this check we could drop
1360 * every frame, freezing the display completely if video lags behind.
1362 if (now > PREV_VSYNC(FFMAX(pts, vc->last_queue_time + vsync_interval)))
1363 npts = UINT64_MAX;
1365 /* Allow flipping a frame at a vsync if its presentation time is a
1366 * bit after that vsync and the change makes the flip time delta
1367 * from previous frame better match the target timestamp delta.
1368 * This avoids instability with frame timestamps falling near vsyncs.
1369 * For example if the frame timestamps were (with vsyncs at
1370 * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
1371 * straightforward timing at next vsync would flip the frames at
1372 * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
1373 * regular 2-vsync intervals.
1375 * Also allow moving the frame forward if it looks like we dropped
1376 * the previous frame incorrectly (now that we know better after
1377 * having final exact timestamp information for this frame) and
1378 * there would unnecessarily be a vsync without a frame change.
1380 uint64_t vsync = PREV_VSYNC(pts);
1381 if (pts < vsync + vsync_interval / 4
1382 && (vsync - PREV_VS2(vc->last_queue_time, 16)
1383 > pts - vc->last_ideal_time + vsync_interval / 2
1384 || vc->dropped_frame && vsync > vc->dropped_time))
1385 pts -= vsync_interval / 2;
1387 vc->dropped_frame = true; // changed at end if false
1388 vc->dropped_time = ideal_pts;
1390 pts = FFMAX(pts, vc->last_queue_time + vsync_interval);
1391 pts = FFMAX(pts, now);
1392 if (npts < PREV_VSYNC(pts) + vsync_interval)
1393 return;
1395 int num_flips = update_presentation_queue_status(vo);
1396 vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
1397 now = sync_vdptime(vo);
1398 pts = FFMAX(pts, now);
1399 pts = FFMAX(pts, vsync + (vsync_interval >> 2));
1400 vsync = PREV_VSYNC(pts);
1401 if (npts < vsync + vsync_interval)
1402 return;
1403 pts = vsync + (vsync_interval >> 2);
1404 vdp_st =
1405 vdp->presentation_queue_display(vc->flip_queue,
1406 vc->output_surfaces[vc->surface_num],
1407 vo->dwidth, vo->dheight, pts);
1408 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display");
1410 vc->last_queue_time = pts;
1411 vc->queue_time[vc->surface_num] = pts;
1412 vc->last_ideal_time = ideal_pts;
1413 vc->dropped_frame = false;
1414 vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
1417 static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
1418 int h, int x, int y)
1420 struct vdpctx *vc = vo->priv;
1421 struct vdp_functions *vdp = vc->vdp;
1422 VdpStatus vdp_st;
1424 if (handle_preemption(vo) < 0)
1425 return VO_TRUE;
1427 struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
1428 int max_refs = vc->image_format == IMGFMT_VDPAU_H264 ?
1429 rndr->info.h264.num_ref_frames : 2;
1430 if (!IMGFMT_IS_VDPAU(vc->image_format))
1431 return VO_FALSE;
1432 if ((vc->decoder == VDP_INVALID_HANDLE || vc->decoder_max_refs < max_refs)
1433 && !create_vdp_decoder(vo, max_refs))
1434 return VO_FALSE;
1436 vdp_st = vdp->decoder_render(vc->decoder, rndr->surface,
1437 (void *)&rndr->info,
1438 rndr->bitstream_buffers_used,
1439 rndr->bitstream_buffers);
1440 CHECK_ST_WARNING("Failed VDPAU decoder rendering");
1441 return VO_TRUE;
1445 static struct vdpau_render_state *get_surface(struct vo *vo, int number)
1447 struct vdpctx *vc = vo->priv;
1448 struct vdp_functions *vdp = vc->vdp;
1450 if (number >= MAX_VIDEO_SURFACES)
1451 return NULL;
1452 if (vc->surface_render[number].surface == VDP_INVALID_HANDLE
1453 && !vc->is_preempted) {
1454 VdpStatus vdp_st;
1455 vdp_st = vdp->video_surface_create(vc->vdp_device, vc->vdp_chroma_type,
1456 vc->vid_width, vc->vid_height,
1457 &vc->surface_render[number].surface);
1458 CHECK_ST_WARNING("Error when calling vdp_video_surface_create");
1460 mp_msg(MSGT_VO, MSGL_DBG3, "vdpau vid create: %u\n",
1461 vc->surface_render[number].surface);
1462 return &vc->surface_render[number];
1465 static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
1467 struct vdpctx *vc = vo->priv;
1468 struct vdp_functions *vdp = vc->vdp;
1469 struct mp_image *reserved_mpi = NULL;
1470 struct vdpau_render_state *rndr;
1472 if (IMGFMT_IS_VDPAU(vc->image_format)) {
1473 rndr = mpi->priv;
1474 reserved_mpi = mpi;
1475 } else if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
1476 rndr = get_surface(vo, vc->deint_counter);
1477 vc->deint_counter = WRAP_ADD(vc->deint_counter, 1, NUM_BUFFERED_VIDEO);
1478 if (handle_preemption(vo) >= 0) {
1479 VdpStatus vdp_st;
1480 const void *destdata[3] = {mpi->planes[0], mpi->planes[2],
1481 mpi->planes[1]};
1482 if (vc->image_format == IMGFMT_NV12)
1483 destdata[1] = destdata[2];
1484 vdp_st = vdp->video_surface_put_bits_y_cb_cr(rndr->surface,
1485 vc->vdp_pixel_format, destdata, mpi->stride);
1486 CHECK_ST_WARNING("Error when calling "
1487 "vdp_video_surface_put_bits_y_cb_cr");
1489 } else
1490 // We don't support slice callbacks so this shouldn't occur -
1491 // I think the flags test above in pointless, but I'm adding
1492 // this instead of removing it just in case.
1493 abort();
1494 if (mpi->fields & MP_IMGFIELD_ORDERED)
1495 vc->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
1496 else
1497 vc->top_field_first = 1;
1499 add_new_video_surface(vo, rndr->surface, reserved_mpi, pts);
1501 return;
1504 // warning: the size and pixel format of surface must match that of the
1505 // surfaces in vc->output_surfaces
1506 static struct mp_image *read_output_surface(struct vdpctx *vc,
1507 VdpOutputSurface surface,
1508 int width, int height)
1510 VdpStatus vdp_st;
1511 struct vdp_functions *vdp = vc->vdp;
1512 struct mp_image *image = alloc_mpi(width, height, IMGFMT_BGR32);
1514 void *dst_planes[] = { image->planes[0] };
1515 uint32_t dst_pitches[] = { image->stride[0] };
1516 vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes,
1517 dst_pitches);
1518 CHECK_ST_WARNING("Error when calling vdp_output_surface_get_bits_native");
1520 return image;
1523 static struct mp_image *get_screenshot(struct vo *vo)
1525 struct vdpctx *vc = vo->priv;
1526 VdpStatus vdp_st;
1527 struct vdp_functions *vdp = vc->vdp;
1529 if (vc->screenshot_surface == VDP_INVALID_HANDLE) {
1530 vdp_st = vdp->output_surface_create(vc->vdp_device,
1531 OUTPUT_RGBA_FORMAT,
1532 vc->vid_width, vc->vid_height,
1533 &vc->screenshot_surface);
1534 CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
1537 VdpRect rc = { .x1 = vc->vid_width, .y1 = vc->vid_height };
1538 render_video_to_output_surface(vo, vc->screenshot_surface, &rc);
1540 struct mp_image *image = read_output_surface(vc, vc->screenshot_surface,
1541 vc->vid_width, vc->vid_height);
1543 image->width = vc->vid_width;
1544 image->height = vc->vid_height;
1545 image->w = vc->vid_d_width;
1546 image->h = vc->vid_d_height;
1548 return image;
1551 static struct mp_image *get_window_screenshot(struct vo *vo)
1553 struct vdpctx *vc = vo->priv;
1554 int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces);
1555 VdpOutputSurface screen = vc->output_surfaces[last_surface];
1556 struct mp_image *image = read_output_surface(vo->priv, screen,
1557 vc->output_surface_width,
1558 vc->output_surface_height);
1559 image->width = image->w = vo->dwidth;
1560 image->height = image->h = vo->dheight;
1561 return image;
1564 static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
1566 struct vdpctx *vc = vo->priv;
1567 struct vdpau_render_state *rndr;
1569 // no dr for non-decoding for now
1570 if (!IMGFMT_IS_VDPAU(vc->image_format))
1571 return VO_FALSE;
1572 if (mpi->type != MP_IMGTYPE_NUMBERED)
1573 return VO_FALSE;
1575 rndr = get_surface(vo, mpi->number);
1576 if (!rndr) {
1577 mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
1578 "get_image\n");
1579 // TODO: this probably breaks things forever, provide a dummy buffer?
1580 return VO_FALSE;
1582 mpi->flags |= MP_IMGFLAG_DIRECT;
1583 mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
1584 mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = NULL;
1585 // hack to get around a check and to avoid a special-case in vd_ffmpeg.c
1586 mpi->planes[0] = (void *)rndr;
1587 mpi->num_planes = 1;
1588 mpi->priv = rndr;
1589 return VO_TRUE;
1592 static int query_format(uint32_t format)
1594 int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
1595 | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD
1596 | VFCAP_EOSD_UNSCALED | VFCAP_EOSD_RGBA | VFCAP_FLIP;
1597 switch (format) {
1598 case IMGFMT_YV12:
1599 case IMGFMT_I420:
1600 case IMGFMT_IYUV:
1601 case IMGFMT_NV12:
1602 case IMGFMT_YUY2:
1603 case IMGFMT_UYVY:
1604 return default_flags | VOCAP_NOSLICES;
1605 case IMGFMT_VDPAU_MPEG1:
1606 case IMGFMT_VDPAU_MPEG2:
1607 case IMGFMT_VDPAU_H264:
1608 case IMGFMT_VDPAU_WMV3:
1609 case IMGFMT_VDPAU_VC1:
1610 case IMGFMT_VDPAU_MPEG4:
1611 return default_flags;
1613 return 0;
1616 static void destroy_vdpau_objects(struct vo *vo)
1618 struct vdpctx *vc = vo->priv;
1619 struct vdp_functions *vdp = vc->vdp;
1621 int i;
1622 VdpStatus vdp_st;
1624 free_video_specific(vo);
1626 if (vc->flip_queue != VDP_INVALID_HANDLE) {
1627 vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
1628 CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy");
1631 if (vc->flip_target != VDP_INVALID_HANDLE) {
1632 vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
1633 CHECK_ST_WARNING("Error when calling "
1634 "vdp_presentation_queue_target_destroy");
1637 for (i = 0; i < vc->num_output_surfaces; i++) {
1638 if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
1639 continue;
1640 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
1641 CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
1644 if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
1645 vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
1646 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1649 if (vc->osd_surface.surface != VDP_INVALID_HANDLE) {
1650 vdp_st = vdp->bitmap_surface_destroy(vc->osd_surface.surface);
1651 CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
1654 vdp_st = vdp->device_destroy(vc->vdp_device);
1655 CHECK_ST_WARNING("Error when calling vdp_device_destroy");
1658 static void uninit(struct vo *vo)
1660 struct vdpctx *vc = vo->priv;
1662 /* Destroy all vdpau objects */
1663 destroy_vdpau_objects(vo);
1665 #ifdef CONFIG_XF86VM
1666 if (vc->mode_switched)
1667 vo_vm_close(vo);
1668 #endif
1669 vo_x11_uninit(vo);
1671 // Free bitstream buffers allocated by FFmpeg
1672 for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
1673 av_freep(&vc->surface_render[i].bitstream_buffers);
1676 static int preinit(struct vo *vo, const char *arg)
1678 struct vdpctx *vc = vo->priv;
1680 // Mark everything as invalid first so uninit() can tell what has been
1681 // allocated
1682 mark_vdpau_objects_uninitialized(vo);
1684 vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
1685 vc->video_eq.capabilities = MP_CSP_EQ_CAPS_COLORMATRIX;
1687 vc->deint_type = vc->deint ? FFABS(vc->deint) : 3;
1688 if (vc->deint < 0)
1689 vc->deint = 0;
1691 if (!vo_init(vo))
1692 return -1;
1694 // After this calling uninit() should work to free resources
1696 if (win_x11_init_vdpau_procs(vo) < 0) {
1697 if (vc->vdp->device_destroy)
1698 vc->vdp->device_destroy(vc->vdp_device);
1699 vo_x11_uninit(vo);
1700 return -1;
1703 return 0;
1706 static int get_equalizer(struct vo *vo, const char *name, int *value)
1708 struct vdpctx *vc = vo->priv;
1709 return mp_csp_equalizer_get(&vc->video_eq, name, value) >= 0 ?
1710 VO_TRUE : VO_NOTIMPL;
1713 static bool status_ok(struct vo *vo)
1715 if (!vo->config_ok || handle_preemption(vo) < 0)
1716 return false;
1717 return true;
1720 static int set_equalizer(struct vo *vo, const char *name, int value)
1722 struct vdpctx *vc = vo->priv;
1724 if (mp_csp_equalizer_set(&vc->video_eq, name, value) < 0)
1725 return VO_NOTIMPL;
1727 if (status_ok(vo))
1728 update_csc_matrix(vo);
1729 return true;
1732 static void checked_resize(struct vo *vo)
1734 if (!status_ok(vo))
1735 return;
1736 resize(vo);
1739 static int control(struct vo *vo, uint32_t request, void *data)
1741 struct vdpctx *vc = vo->priv;
1742 struct vdp_functions *vdp = vc->vdp;
1744 handle_preemption(vo);
1746 switch (request) {
1747 case VOCTRL_GET_DEINTERLACE:
1748 *(int *)data = vc->deint;
1749 return VO_TRUE;
1750 case VOCTRL_SET_DEINTERLACE:
1751 vc->deint = *(int *)data;
1752 if (vc->deint)
1753 vc->deint = vc->deint_type;
1754 if (vc->deint_type > 2 && status_ok(vo)) {
1755 VdpStatus vdp_st;
1756 VdpVideoMixerFeature features[1] =
1757 {vc->deint_type == 3 ?
1758 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL :
1759 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL};
1760 VdpBool feature_enables[1] = {vc->deint ? VDP_TRUE : VDP_FALSE};
1761 vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
1762 1, features,
1763 feature_enables);
1764 CHECK_ST_WARNING("Error changing deinterlacing settings");
1766 vo->want_redraw = true;
1767 return VO_TRUE;
1768 case VOCTRL_PAUSE:
1769 if (vc->dropped_frame)
1770 vo->want_redraw = true;
1771 return true;
1772 case VOCTRL_QUERY_FORMAT:
1773 return query_format(*(uint32_t *)data);
1774 case VOCTRL_GET_IMAGE:
1775 return get_image(vo, data);
1776 case VOCTRL_DRAW_IMAGE:
1777 abort(); // draw_image() should get called directly
1778 case VOCTRL_BORDER:
1779 vo_x11_border(vo);
1780 checked_resize(vo);
1781 return VO_TRUE;
1782 case VOCTRL_FULLSCREEN:
1783 vo_x11_fullscreen(vo);
1784 checked_resize(vo);
1785 return VO_TRUE;
1786 case VOCTRL_GET_PANSCAN:
1787 return VO_TRUE;
1788 case VOCTRL_SET_PANSCAN:
1789 checked_resize(vo);
1790 return VO_TRUE;
1791 case VOCTRL_SET_EQUALIZER: {
1792 vo->want_redraw = true;
1793 struct voctrl_set_equalizer_args *args = data;
1794 return set_equalizer(vo, args->name, args->value);
1796 case VOCTRL_GET_EQUALIZER: {
1797 struct voctrl_get_equalizer_args *args = data;
1798 return get_equalizer(vo, args->name, args->valueptr);
1800 case VOCTRL_SET_YUV_COLORSPACE:
1801 vc->colorspace = *(struct mp_csp_details *)data;
1802 if (status_ok(vo))
1803 update_csc_matrix(vo);
1804 vo->want_redraw = true;
1805 return true;
1806 case VOCTRL_GET_YUV_COLORSPACE:
1807 *(struct mp_csp_details *)data = vc->colorspace;
1808 return true;
1809 case VOCTRL_ONTOP:
1810 vo_x11_ontop(vo);
1811 return VO_TRUE;
1812 case VOCTRL_UPDATE_SCREENINFO:
1813 update_xinerama_info(vo);
1814 return VO_TRUE;
1815 case VOCTRL_DRAW_EOSD:
1816 if (!data)
1817 return VO_FALSE;
1818 if (status_ok(vo)) {
1819 generate_eosd(vo, data);
1820 draw_eosd(vo);
1822 return VO_TRUE;
1823 case VOCTRL_GET_EOSD_RES: {
1824 struct mp_eosd_res *r = data;
1825 r->w = vo->dwidth;
1826 r->h = vo->dheight;
1827 r->ml = r->mr = vc->border_x;
1828 r->mt = r->mb = vc->border_y;
1829 return VO_TRUE;
1831 case VOCTRL_NEWFRAME:
1832 vc->deint_queue_pos = next_deint_queue_pos(vo, true);
1833 if (status_ok(vo))
1834 video_to_output_surface(vo);
1835 return true;
1836 case VOCTRL_SKIPFRAME:
1837 vc->deint_queue_pos = next_deint_queue_pos(vo, true);
1838 return true;
1839 case VOCTRL_REDRAW_FRAME:
1840 if (status_ok(vo))
1841 video_to_output_surface(vo);
1842 return true;
1843 case VOCTRL_RESET:
1844 forget_frames(vo);
1845 return true;
1846 case VOCTRL_SCREENSHOT: {
1847 if (!status_ok(vo))
1848 return false;
1849 struct voctrl_screenshot_args *args = data;
1850 if (args->full_window)
1851 args->out_image = get_window_screenshot(vo);
1852 else
1853 args->out_image = get_screenshot(vo);
1854 return true;
1857 return VO_NOTIMPL;
1860 #undef OPT_BASE_STRUCT
1861 #define OPT_BASE_STRUCT struct vdpctx
1863 const struct vo_driver video_out_vdpau = {
1864 .is_new = true,
1865 .buffer_frames = true,
1866 .info = &(const struct vo_info_s){
1867 "VDPAU with X11",
1868 "vdpau",
1869 "Rajib Mahapatra <rmahapatra@nvidia.com> and others",
1872 .preinit = preinit,
1873 .config = config,
1874 .control = control,
1875 .draw_image = draw_image,
1876 .get_buffered_frame = set_next_frame_info,
1877 .draw_slice = draw_slice,
1878 .draw_osd = draw_osd,
1879 .flip_page_timed = flip_page_timed,
1880 .check_events = check_events,
1881 .uninit = uninit,
1882 .privsize = sizeof(struct vdpctx),
1883 .options = (const struct m_option []){
1884 OPT_INTRANGE("deint", deint, 0, -4, 4),
1885 OPT_FLAG_ON("chroma-deint", chroma_deint, 0, OPTDEF_INT(1)),
1886 OPT_FLAG_OFF("nochroma-deint", chroma_deint, 0),
1887 OPT_MAKE_FLAGS("pullup", pullup, 0),
1888 OPT_FLOATRANGE("denoise", denoise, 0, 0, 1),
1889 OPT_FLOATRANGE("sharpen", sharpen, 0, -1, 1),
1890 OPT_ERRORMESSAGE("colorspace", "vo_vdpau suboption \"colorspace\" has "
1891 "been removed. Use --colormatrix instead.\n"),
1892 OPT_ERRORMESSAGE("studio", "vo_vdpau suboption \"studio\" has been "
1893 "removed. Use --colormatrix-output-range=limited "
1894 "instead.\n"),
1895 OPT_INTRANGE("hqscaling", hqscaling, 0, 0, 9),
1896 OPT_FLOAT("fps", user_fps, 0),
1897 OPT_FLAG_ON("composite-detect", composite_detect, 0, OPTDEF_INT(1)),
1898 OPT_INT("queuetime_windowed", flip_offset_window, 0, OPTDEF_INT(50)),
1899 OPT_INT("queuetime_fs", flip_offset_fs, 0, OPTDEF_INT(50)),
1900 OPT_INTRANGE("output_surfaces", num_output_surfaces, 0,
1901 2, MAX_OUTPUT_SURFACES, OPTDEF_INT(3)),
1902 {NULL},