1 /*
2 * VDPAU video output driver
3 *
4 * Copyright (C) 2008 NVIDIA (Rajib Mahapatra <rmahapatra@nvidia.com>)
5 * Copyright (C) 2009 Uoti Urpala
6 *
7 * This file is part of mpv.
8 *
9 * mpv 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.
13 *
14 * mpv 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.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with mpv. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24 * Actual decoding is done in video/decode/vdpau.c
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <limits.h>
32 #include <assert.h>
33
34 #include "config.h"
35 #include "video/vdpau.h"
36 #include "video/vdpau_mixer.h"
37 #include "video/hwdec.h"
38 #include "common/msg.h"
39 #include "options/options.h"
40 #include "mpv_talloc.h"
41 #include "vo.h"
42 #include "x11_common.h"
43 #include "video/csputils.h"
44 #include "sub/osd.h"
45 #include "options/m_option.h"
46 #include "video/mp_image.h"
47 #include "osdep/timer.h"
48
49 // Returns x + a, but wrapped around to the range [0, m)
50 // a must be within [-m, m], x within [0, m)
51 #define WRAP_ADD(x, a, m) ((a) < 0 \
52 ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
53 : ((x)+(a) < (m) ? (x)+(a) : (x)+(a)-(m)))
54
55
56 /* number of video and output surfaces */
57 #define MAX_OUTPUT_SURFACES 15
58
59 /* Pixelformat used for output surfaces */
60 #define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
61
62 /*
63 * Global variable declaration - VDPAU specific
64 */
65
66 struct vdpctx {
67 struct mp_vdpau_ctx *mpvdp;
68 struct vdp_functions *vdp;
69 VdpDevice vdp_device;
70 uint64_t preemption_counter;
71
72 struct m_color colorkey;
73
74 VdpPresentationQueueTarget flip_target;
75 VdpPresentationQueue flip_queue;
76
77 VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
78 int num_output_surfaces;
79 VdpOutputSurface black_pixel;
80 VdpOutputSurface rotation_surface;
81
82 struct mp_image *current_image;
83 int64_t current_pts;
84 int current_duration;
85
86 int output_surface_w, output_surface_h;
87 int rotation;
88
89 int force_yuv;
90 struct mp_vdpau_mixer *video_mixer;
91 int pullup;
92 float denoise;
93 float sharpen;
94 int hqscaling;
95 int chroma_deint;
96 int flip_offset_window;
97 int flip_offset_fs;
98 int64_t flip_offset_us;
99
100 VdpRect src_rect_vid;
101 VdpRect out_rect_vid;
102 struct mp_osd_res osd_rect;
103 VdpBool supports_a8;
104
105 int surface_num; // indexes output_surfaces
106 int query_surface_num;
107 VdpTime recent_vsync_time;
108 float user_fps;
109 int composite_detect;
110 int vsync_interval;
111 uint64_t last_queue_time;
112 uint64_t queue_time[MAX_OUTPUT_SURFACES];
113 uint64_t last_ideal_time;
114 bool dropped_frame;
115 uint64_t dropped_time;
116 uint32_t vid_width, vid_height;
117 uint32_t image_format;
118 VdpYCbCrFormat vdp_pixel_format;
119 bool rgb_mode;
120
121 // OSD
122 struct osd_bitmap_surface {
123 VdpRGBAFormat format;
124 VdpBitmapSurface surface;
125 uint32_t surface_w, surface_h;
126 // List of surfaces to be rendered
127 struct osd_target {
128 VdpRect source;
129 VdpRect dest;
130 VdpColor color;
131 } *targets;
132 int targets_size;
133 int render_count;
134 int change_id;
135 } osd_surfaces[MAX_OSD_PARTS];
136 };
137
138 static bool status_ok(struct vo *vo);
139
video_to_output_surface(struct vo * vo,struct mp_image * mpi)140 static int video_to_output_surface(struct vo *vo, struct mp_image *mpi)
141 {
142 struct vdpctx *vc = vo->priv;
143 struct vdp_functions *vdp = vc->vdp;
144 VdpTime dummy;
145 VdpStatus vdp_st;
146
147 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
148 VdpRect *output_rect = &vc->out_rect_vid;
149 VdpRect *video_rect = &vc->src_rect_vid;
150
151 vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
152 output_surface,
153 &dummy);
154 CHECK_VDP_WARNING(vo, "Error when calling "
155 "vdp_presentation_queue_block_until_surface_idle");
156
157 // Clear the borders between video and window (if there are any).
158 // For some reason, video_mixer_render doesn't need it for YUV.
159 // Also, if there is nothing to render, at least clear the screen.
160 if (vc->rgb_mode || !mpi || mpi->params.rotate != 0) {
161 int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
162 vdp_st = vdp->output_surface_render_output_surface(output_surface,
163 NULL, vc->black_pixel,
164 NULL, NULL, NULL,
165 flags);
166 CHECK_VDP_WARNING(vo, "Error clearing screen");
167 }
168
169 if (!mpi)
170 return -1;
171
172 struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(mpi);
173 struct mp_vdpau_mixer_opts opts = {0};
174 if (frame)
175 opts = frame->opts;
176
177 // Apply custom vo_vdpau suboptions.
178 opts.chroma_deint |= vc->chroma_deint;
179 opts.pullup |= vc->pullup;
180 opts.denoise = MPCLAMP(opts.denoise + vc->denoise, 0, 1);
181 opts.sharpen = MPCLAMP(opts.sharpen + vc->sharpen, -1, 1);
182 if (vc->hqscaling)
183 opts.hqscaling = vc->hqscaling;
184
185 if (mpi->params.rotate != 0) {
186 int flags;
187 VdpRect r_rect;
188 switch (mpi->params.rotate) {
189 case 90:
190 r_rect.y0 = output_rect->x0;
191 r_rect.y1 = output_rect->x1;
192 r_rect.x0 = output_rect->y0;
193 r_rect.x1 = output_rect->y1;
194 flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_90;
195 break;
196 case 180:
197 r_rect.x0 = output_rect->x0;
198 r_rect.x1 = output_rect->x1;
199 r_rect.y0 = output_rect->y0;
200 r_rect.y1 = output_rect->y1;
201 flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_180;
202 break;
203 case 270:
204 r_rect.y0 = output_rect->x0;
205 r_rect.y1 = output_rect->x1;
206 r_rect.x0 = output_rect->y0;
207 r_rect.x1 = output_rect->y1;
208 flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_270;
209 break;
210 default:
211 MP_ERR(vo, "Unsupported rotation angle: %u\n", mpi->params.rotate);
212 return -1;
213 }
214
215 mp_vdpau_mixer_render(vc->video_mixer, &opts, vc->rotation_surface,
216 &r_rect, mpi, video_rect);
217 vdp_st = vdp->output_surface_render_output_surface(output_surface,
218 output_rect,
219 vc->rotation_surface,
220 &r_rect,
221 NULL,
222 NULL,
223 flags);
224 CHECK_VDP_WARNING(vo, "Error rendering rotated frame");
225 } else {
226 mp_vdpau_mixer_render(vc->video_mixer, &opts, output_surface,
227 output_rect, mpi, video_rect);
228 }
229 return 0;
230 }
231
forget_frames(struct vo * vo,bool seek_reset)232 static void forget_frames(struct vo *vo, bool seek_reset)
233 {
234 struct vdpctx *vc = vo->priv;
235
236 if (!seek_reset)
237 mp_image_unrefp(&vc->current_image);
238
239 vc->dropped_frame = false;
240 }
241
s_size(int max,int s,int disp)242 static int s_size(int max, int s, int disp)
243 {
244 disp = MPMAX(1, disp);
245 return MPMIN(max, MPMAX(s, disp));
246 }
247
resize(struct vo * vo)248 static void resize(struct vo *vo)
249 {
250 struct vdpctx *vc = vo->priv;
251 struct vdp_functions *vdp = vc->vdp;
252 VdpStatus vdp_st;
253 struct mp_rect src_rect;
254 struct mp_rect dst_rect;
255 vo_get_src_dst_rects(vo, &src_rect, &dst_rect, &vc->osd_rect);
256 vc->out_rect_vid.x0 = dst_rect.x0;
257 vc->out_rect_vid.x1 = dst_rect.x1;
258 vc->out_rect_vid.y0 = dst_rect.y0;
259 vc->out_rect_vid.y1 = dst_rect.y1;
260 if (vo->params->rotate == 90 || vo->params->rotate == 270) {
261 vc->src_rect_vid.y0 = src_rect.x0;
262 vc->src_rect_vid.y1 = src_rect.x1;
263 vc->src_rect_vid.x0 = src_rect.y0;
264 vc->src_rect_vid.x1 = src_rect.y1;
265 } else {
266 vc->src_rect_vid.x0 = src_rect.x0;
267 vc->src_rect_vid.x1 = src_rect.x1;
268 vc->src_rect_vid.y0 = src_rect.y0;
269 vc->src_rect_vid.y1 = src_rect.y1;
270 }
271
272 VdpBool ok;
273 uint32_t max_w, max_h;
274 vdp_st = vdp->output_surface_query_capabilities(vc->vdp_device,
275 OUTPUT_RGBA_FORMAT,
276 &ok, &max_w, &max_h);
277 if (vdp_st != VDP_STATUS_OK || !ok)
278 return;
279
280 vc->flip_offset_us = vo->opts->fullscreen ?
281 1000LL * vc->flip_offset_fs :
282 1000LL * vc->flip_offset_window;
283 vo_set_queue_params(vo, vc->flip_offset_us, 1);
284
285 if (vc->output_surface_w < vo->dwidth || vc->output_surface_h < vo->dheight ||
286 vc->rotation != vo->params->rotate)
287 {
288 vc->output_surface_w = s_size(max_w, vc->output_surface_w, vo->dwidth);
289 vc->output_surface_h = s_size(max_h, vc->output_surface_h, vo->dheight);
290 // Creation of output_surfaces
291 for (int i = 0; i < vc->num_output_surfaces; i++)
292 if (vc->output_surfaces[i] != VDP_INVALID_HANDLE) {
293 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
294 CHECK_VDP_WARNING(vo, "Error when calling "
295 "vdp_output_surface_destroy");
296 }
297 for (int i = 0; i < vc->num_output_surfaces; i++) {
298 vdp_st = vdp->output_surface_create(vc->vdp_device,
299 OUTPUT_RGBA_FORMAT,
300 vc->output_surface_w,
301 vc->output_surface_h,
302 &vc->output_surfaces[i]);
303 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_create");
304 MP_DBG(vo, "vdpau out create: %u\n",
305 vc->output_surfaces[i]);
306 }
307 if (vc->rotation_surface != VDP_INVALID_HANDLE) {
308 vdp_st = vdp->output_surface_destroy(vc->rotation_surface);
309 CHECK_VDP_WARNING(vo, "Error when calling "
310 "vdp_output_surface_destroy");
311 vc->rotation_surface = VDP_INVALID_HANDLE;
312 }
313 if (vo->params->rotate == 90 || vo->params->rotate == 270) {
314 vdp_st = vdp->output_surface_create(vc->vdp_device,
315 OUTPUT_RGBA_FORMAT,
316 vc->output_surface_h,
317 vc->output_surface_w,
318 &vc->rotation_surface);
319 } else if (vo->params->rotate == 180) {
320 vdp_st = vdp->output_surface_create(vc->vdp_device,
321 OUTPUT_RGBA_FORMAT,
322 vc->output_surface_w,
323 vc->output_surface_h,
324 &vc->rotation_surface);
325 }
326 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_create");
327 MP_DBG(vo, "vdpau rotation surface create: %u\n",
328 vc->rotation_surface);
329 }
330 vc->rotation = vo->params->rotate;
331 vo->want_redraw = true;
332 }
333
win_x11_init_vdpau_flip_queue(struct vo * vo)334 static int win_x11_init_vdpau_flip_queue(struct vo *vo)
335 {
336 struct vdpctx *vc = vo->priv;
337 struct vdp_functions *vdp = vc->vdp;
338 struct vo_x11_state *x11 = vo->x11;
339 VdpStatus vdp_st;
340
341 if (vc->flip_target == VDP_INVALID_HANDLE) {
342 vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device,
343 x11->window,
344 &vc->flip_target);
345 CHECK_VDP_ERROR(vo, "Error when calling "
346 "vdp_presentation_queue_target_create_x11");
347 }
348
349 /* Emperically this seems to be the first call which fails when we
350 * try to reinit after preemption while the user is still switched
351 * from X to a virtual terminal (creating the vdp_device initially
352 * succeeds, as does creating the flip_target above). This is
353 * probably not guaranteed behavior.
354 */
355 if (vc->flip_queue == VDP_INVALID_HANDLE) {
356 vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target,
357 &vc->flip_queue);
358 CHECK_VDP_ERROR(vo, "Error when calling vdp_presentation_queue_create");
359 }
360
361 if (vc->colorkey.a > 0) {
362 VdpColor color = {
363 .red = vc->colorkey.r / 255.0,
364 .green = vc->colorkey.g / 255.0,
365 .blue = vc->colorkey.b / 255.0,
366 .alpha = 0,
367 };
368 vdp_st = vdp->presentation_queue_set_background_color(vc->flip_queue,
369 &color);
370 CHECK_VDP_WARNING(vo, "Error setting colorkey");
371 }
372
373 if (vc->composite_detect && vo_x11_screen_is_composited(vo)) {
374 MP_INFO(vo, "Compositing window manager detected. Assuming timing info "
375 "is inaccurate.\n");
376 vc->user_fps = -1;
377 }
378
379 return 0;
380 }
381
382 // Free everything specific to a certain video file
free_video_specific(struct vo * vo)383 static void free_video_specific(struct vo *vo)
384 {
385 struct vdpctx *vc = vo->priv;
386 struct vdp_functions *vdp = vc->vdp;
387 VdpStatus vdp_st;
388
389 forget_frames(vo, false);
390
391 if (vc->black_pixel != VDP_INVALID_HANDLE) {
392 vdp_st = vdp->output_surface_destroy(vc->black_pixel);
393 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
394 }
395 vc->black_pixel = VDP_INVALID_HANDLE;
396 }
397
initialize_vdpau_objects(struct vo * vo)398 static int initialize_vdpau_objects(struct vo *vo)
399 {
400 struct vdpctx *vc = vo->priv;
401 struct vdp_functions *vdp = vc->vdp;
402 VdpStatus vdp_st;
403
404 mp_vdpau_get_format(vc->image_format, NULL, &vc->vdp_pixel_format);
405
406 vc->video_mixer->initialized = false;
407
408 if (win_x11_init_vdpau_flip_queue(vo) < 0)
409 return -1;
410
411 if (vc->black_pixel == VDP_INVALID_HANDLE) {
412 vdp_st = vdp->output_surface_create(vc->vdp_device, OUTPUT_RGBA_FORMAT,
413 1, 1, &vc->black_pixel);
414 CHECK_VDP_ERROR(vo, "Allocating clearing surface");
415 const char data[4] = {0};
416 vdp_st = vdp->output_surface_put_bits_native(vc->black_pixel,
417 (const void*[]){data},
418 (uint32_t[]){4}, NULL);
419 CHECK_VDP_ERROR(vo, "Initializing clearing surface");
420 }
421
422 forget_frames(vo, false);
423 resize(vo);
424 return 0;
425 }
426
mark_vdpau_objects_uninitialized(struct vo * vo)427 static void mark_vdpau_objects_uninitialized(struct vo *vo)
428 {
429 struct vdpctx *vc = vo->priv;
430
431 forget_frames(vo, false);
432 vc->black_pixel = VDP_INVALID_HANDLE;
433 vc->flip_queue = VDP_INVALID_HANDLE;
434 vc->flip_target = VDP_INVALID_HANDLE;
435 for (int i = 0; i < MAX_OUTPUT_SURFACES; i++)
436 vc->output_surfaces[i] = VDP_INVALID_HANDLE;
437 vc->rotation_surface = VDP_INVALID_HANDLE;
438 vc->vdp_device = VDP_INVALID_HANDLE;
439 for (int i = 0; i < MAX_OSD_PARTS; i++) {
440 struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
441 sfc->change_id = 0;
442 *sfc = (struct osd_bitmap_surface){
443 .surface = VDP_INVALID_HANDLE,
444 };
445 }
446 vc->output_surface_w = vc->output_surface_h = -1;
447 }
448
check_preemption(struct vo * vo)449 static bool check_preemption(struct vo *vo)
450 {
451 struct vdpctx *vc = vo->priv;
452
453 int r = mp_vdpau_handle_preemption(vc->mpvdp, &vc->preemption_counter);
454 if (r < 1) {
455 mark_vdpau_objects_uninitialized(vo);
456 if (r < 0)
457 return false;
458 vc->vdp_device = vc->mpvdp->vdp_device;
459 if (initialize_vdpau_objects(vo) < 0)
460 return false;
461 }
462 return true;
463 }
464
status_ok(struct vo * vo)465 static bool status_ok(struct vo *vo)
466 {
467 return vo->config_ok && check_preemption(vo);
468 }
469
470 /*
471 * connect to X server, create and map window, initialize all
472 * VDPAU objects, create different surfaces etc.
473 */
reconfig(struct vo * vo,struct mp_image_params * params)474 static int reconfig(struct vo *vo, struct mp_image_params *params)
475 {
476 struct vdpctx *vc = vo->priv;
477 struct vdp_functions *vdp = vc->vdp;
478 VdpStatus vdp_st;
479
480 if (!check_preemption(vo))
481 {
482 /*
483 * When prempted, leave the reconfig() immediately
484 * without reconfiguring the vo_window and without
485 * initializing the vdpau objects. When recovered
486 * from preemption, if there is a difference between
487 * the VD thread parameters and the VO thread parameters
488 * the reconfig() is triggered again.
489 */
490 return 0;
491 }
492
493 VdpChromaType chroma_type = VDP_CHROMA_TYPE_420;
494 mp_vdpau_get_format(params->imgfmt, &chroma_type, NULL);
495
496 VdpBool ok;
497 uint32_t max_w, max_h;
498 vdp_st = vdp->video_surface_query_capabilities(vc->vdp_device, chroma_type,
499 &ok, &max_w, &max_h);
500 CHECK_VDP_ERROR(vo, "Error when calling vdp_video_surface_query_capabilities");
501
502 if (!ok)
503 return -1;
504 if (params->w > max_w || params->h > max_h) {
505 if (ok)
506 MP_ERR(vo, "Video too large for vdpau.\n");
507 return -1;
508 }
509
510 vc->image_format = params->imgfmt;
511 vc->vid_width = params->w;
512 vc->vid_height = params->h;
513
514 vc->rgb_mode = mp_vdpau_get_rgb_format(params->imgfmt, NULL);
515
516 free_video_specific(vo);
517
518 vo_x11_config_vo_window(vo);
519
520 if (initialize_vdpau_objects(vo) < 0)
521 return -1;
522
523 return 0;
524 }
525
draw_osd_part(struct vo * vo,int index)526 static void draw_osd_part(struct vo *vo, int index)
527 {
528 struct vdpctx *vc = vo->priv;
529 struct vdp_functions *vdp = vc->vdp;
530 VdpStatus vdp_st;
531 struct osd_bitmap_surface *sfc = &vc->osd_surfaces[index];
532 VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
533 int i;
534
535 VdpOutputSurfaceRenderBlendState blend_state = {
536 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
537 .blend_factor_source_color =
538 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
539 .blend_factor_source_alpha =
540 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
541 .blend_factor_destination_color =
542 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
543 .blend_factor_destination_alpha =
544 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
545 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
546 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
547 };
548
549 VdpOutputSurfaceRenderBlendState blend_state_premultiplied = blend_state;
550 blend_state_premultiplied.blend_factor_source_color =
551 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
552
553 for (i = 0; i < sfc->render_count; i++) {
554 VdpOutputSurfaceRenderBlendState *blend = &blend_state;
555 if (sfc->format == VDP_RGBA_FORMAT_B8G8R8A8)
556 blend = &blend_state_premultiplied;
557 vdp_st = vdp->
558 output_surface_render_bitmap_surface(output_surface,
559 &sfc->targets[i].dest,
560 sfc->surface,
561 &sfc->targets[i].source,
562 &sfc->targets[i].color,
563 blend,
564 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
565 CHECK_VDP_WARNING(vo, "OSD: Error when rendering");
566 }
567 }
568
next_pow2(int v)569 static int next_pow2(int v)
570 {
571 for (int x = 0; x < 30; x++) {
572 if ((1 << x) >= v)
573 return 1 << x;
574 }
575 return INT_MAX;
576 }
577
generate_osd_part(struct vo * vo,struct sub_bitmaps * imgs)578 static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
579 {
580 struct vdpctx *vc = vo->priv;
581 struct vdp_functions *vdp = vc->vdp;
582 VdpStatus vdp_st;
583 struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
584
585 if (imgs->change_id == sfc->change_id)
586 return; // Nothing changed and we still have the old data
587
588 sfc->change_id = imgs->change_id;
589 sfc->render_count = 0;
590
591 if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
592 return;
593
594 VdpRGBAFormat format;
595 switch (imgs->format) {
596 case SUBBITMAP_LIBASS:
597 format = VDP_RGBA_FORMAT_A8;
598 break;
599 case SUBBITMAP_RGBA:
600 format = VDP_RGBA_FORMAT_B8G8R8A8;
601 break;
602 default:
603 abort();
604 };
605
606 assert(imgs->packed);
607
608 int r_w = next_pow2(imgs->packed_w);
609 int r_h = next_pow2(imgs->packed_h);
610
611 if (sfc->format != format || sfc->surface == VDP_INVALID_HANDLE ||
612 sfc->surface_w < r_w || sfc->surface_h < r_h)
613 {
614 MP_VERBOSE(vo, "Allocating a %dx%d surface for OSD bitmaps.\n", r_w, r_h);
615
616 uint32_t m_w = 0, m_h = 0;
617 vdp_st = vdp->bitmap_surface_query_capabilities(vc->vdp_device, format,
618 &(VdpBool){0}, &m_w, &m_h);
619 CHECK_VDP_WARNING(vo, "Query to get max OSD surface size failed");
620
621 if (r_w > m_w || r_h > m_h) {
622 MP_ERR(vo, "OSD bitmaps do not fit on a surface with the maximum "
623 "supported size\n");
624 return;
625 }
626
627 if (sfc->surface != VDP_INVALID_HANDLE) {
628 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
629 CHECK_VDP_WARNING(vo, "Error when calling vdp_bitmap_surface_destroy");
630 }
631
632 VdpBitmapSurface surface;
633 vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
634 r_w, r_h, true, &surface);
635 CHECK_VDP_WARNING(vo, "OSD: error when creating surface");
636 if (vdp_st != VDP_STATUS_OK)
637 return;
638
639 sfc->surface = surface;
640 sfc->surface_w = r_w;
641 sfc->surface_h = r_h;
642 sfc->format = format;
643 }
644
645 void *data = imgs->packed->planes[0];
646 int stride = imgs->packed->stride[0];
647 VdpRect rc = {0, 0, imgs->packed_w, imgs->packed_h};
648 vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
649 &(const void *){data},
650 &(uint32_t){stride},
651 &rc);
652 CHECK_VDP_WARNING(vo, "OSD: putbits failed");
653
654 MP_TARRAY_GROW(vc, sfc->targets, imgs->num_parts);
655 sfc->render_count = imgs->num_parts;
656
657 for (int i = 0; i < imgs->num_parts; i++) {
658 struct sub_bitmap *b = &imgs->parts[i];
659 struct osd_target *target = &sfc->targets[i];
660 target->source = (VdpRect){b->src_x, b->src_y,
661 b->src_x + b->w, b->src_y + b->h};
662 target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
663 target->color = (VdpColor){1, 1, 1, 1};
664 if (imgs->format == SUBBITMAP_LIBASS) {
665 uint32_t color = b->libass.color;
666 target->color.alpha = 1.0 - ((color >> 0) & 0xff) / 255.0;
667 target->color.blue = ((color >> 8) & 0xff) / 255.0;
668 target->color.green = ((color >> 16) & 0xff) / 255.0;
669 target->color.red = ((color >> 24) & 0xff) / 255.0;
670 }
671 }
672 }
673
draw_osd_cb(void * ctx,struct sub_bitmaps * imgs)674 static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
675 {
676 struct vo *vo = ctx;
677 generate_osd_part(vo, imgs);
678 draw_osd_part(vo, imgs->render_index);
679 }
680
draw_osd(struct vo * vo)681 static void draw_osd(struct vo *vo)
682 {
683 struct vdpctx *vc = vo->priv;
684
685 if (!status_ok(vo))
686 return;
687
688 bool formats[SUBBITMAP_COUNT] = {
689 [SUBBITMAP_LIBASS] = vc->supports_a8,
690 [SUBBITMAP_RGBA] = true,
691 };
692
693 double pts = vc->current_image ? vc->current_image->pts : 0;
694 osd_draw(vo->osd, vc->osd_rect, pts, 0, formats, draw_osd_cb, vo);
695 }
696
update_presentation_queue_status(struct vo * vo)697 static int update_presentation_queue_status(struct vo *vo)
698 {
699 struct vdpctx *vc = vo->priv;
700 struct vdp_functions *vdp = vc->vdp;
701 VdpStatus vdp_st;
702
703 while (vc->query_surface_num != vc->surface_num) {
704 VdpTime vtime;
705 VdpPresentationQueueStatus status;
706 VdpOutputSurface surface = vc->output_surfaces[vc->query_surface_num];
707 vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
708 surface,
709 &status, &vtime);
710 CHECK_VDP_WARNING(vo, "Error calling "
711 "presentation_queue_query_surface_status");
712 if (mp_msg_test(vo->log, MSGL_TRACE)) {
713 VdpTime current;
714 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, ¤t);
715 CHECK_VDP_WARNING(vo, "Error when calling "
716 "vdp_presentation_queue_get_time");
717 MP_TRACE(vo, "Vdpau time: %"PRIu64"\n", (uint64_t)current);
718 MP_TRACE(vo, "Surface %d status: %d time: %"PRIu64"\n",
719 (int)surface, (int)status, (uint64_t)vtime);
720 }
721 if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
722 break;
723 if (vc->vsync_interval > 1) {
724 uint64_t qtime = vc->queue_time[vc->query_surface_num];
725 int diff = ((int64_t)vtime - (int64_t)qtime) / 1e6;
726 MP_TRACE(vo, "Queue time difference: %d ms\n", diff);
727 if (vtime < qtime + vc->vsync_interval / 2)
728 MP_VERBOSE(vo, "Frame shown too early (%d ms)\n", diff);
729 if (vtime > qtime + vc->vsync_interval)
730 MP_VERBOSE(vo, "Frame shown late (%d ms)\n", diff);
731 }
732 vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
733 vc->num_output_surfaces);
734 vc->recent_vsync_time = vtime;
735 }
736 int num_queued = WRAP_ADD(vc->surface_num, -vc->query_surface_num,
737 vc->num_output_surfaces);
738 MP_DBG(vo, "Queued surface count (before add): %d\n", num_queued);
739 return num_queued;
740 }
741
742 // Return the timestamp of the vsync that must have happened before ts.
prev_vsync(struct vdpctx * vc,uint64_t ts)743 static inline uint64_t prev_vsync(struct vdpctx *vc, uint64_t ts)
744 {
745 int64_t diff = (int64_t)(ts - vc->recent_vsync_time);
746 int64_t offset = diff % vc->vsync_interval;
747 if (offset < 0)
748 offset += vc->vsync_interval;
749 return ts - offset;
750 }
751
flip_page(struct vo * vo)752 static void flip_page(struct vo *vo)
753 {
754 struct vdpctx *vc = vo->priv;
755 struct vdp_functions *vdp = vc->vdp;
756 VdpStatus vdp_st;
757
758 int64_t pts_us = vc->current_pts;
759 int duration = vc->current_duration;
760
761 vc->dropped_frame = true; // changed at end if false
762
763 if (!check_preemption(vo))
764 goto drop;
765
766 vc->vsync_interval = 1;
767 if (vc->user_fps > 0) {
768 vc->vsync_interval = 1e9 / vc->user_fps;
769 } else if (vc->user_fps == 0) {
770 vc->vsync_interval = vo_get_vsync_interval(vo) * 1000;
771 }
772 vc->vsync_interval = MPMAX(vc->vsync_interval, 1);
773
774 if (duration > INT_MAX / 1000)
775 duration = -1;
776 else
777 duration *= 1000;
778
779 if (vc->vsync_interval == 1)
780 duration = -1; // Make sure drop logic is disabled
781
782 VdpTime vdp_time = 0;
783 vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
784 CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_get_time");
785
786 int64_t rel_pts_ns = (pts_us - mp_time_us()) * 1000;
787 if (!pts_us || rel_pts_ns < 0)
788 rel_pts_ns = 0;
789
790 uint64_t now = vdp_time;
791 uint64_t pts = now + rel_pts_ns;
792 uint64_t ideal_pts = pts;
793 uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
794
795 /* This should normally never happen.
796 * - The last queued frame can't have a PTS that goes more than 50ms in the
797 * future. This is guaranteed by vo.c, which currently actually queues
798 * ahead by roughly the flip queue offset. Just to be sure
799 * give some additional room by doubling the time.
800 * - The last vsync can never be in the future.
801 */
802 int64_t max_pts_ahead = vc->flip_offset_us * 1000 * 2;
803 if (vc->last_queue_time > now + max_pts_ahead ||
804 vc->recent_vsync_time > now)
805 {
806 vc->last_queue_time = 0;
807 vc->recent_vsync_time = 0;
808 MP_WARN(vo, "Inconsistent timing detected.\n");
809 }
810
811 #define PREV_VSYNC(ts) prev_vsync(vc, ts)
812
813 /* We hope to be here at least one vsync before the frame should be shown.
814 * If we are running late then don't drop the frame unless there is
815 * already one queued for the next vsync; even if we _hope_ to show the
816 * next frame soon enough to mean this one should be dropped we might
817 * not make the target time in reality. Without this check we could drop
818 * every frame, freezing the display completely if video lags behind.
819 */
820 if (now > PREV_VSYNC(MPMAX(pts, vc->last_queue_time + vc->vsync_interval)))
821 npts = UINT64_MAX;
822
823 /* Allow flipping a frame at a vsync if its presentation time is a
824 * bit after that vsync and the change makes the flip time delta
825 * from previous frame better match the target timestamp delta.
826 * This avoids instability with frame timestamps falling near vsyncs.
827 * For example if the frame timestamps were (with vsyncs at
828 * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
829 * straightforward timing at next vsync would flip the frames at
830 * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
831 * regular 2-vsync intervals.
832 *
833 * Also allow moving the frame forward if it looks like we dropped
834 * the previous frame incorrectly (now that we know better after
835 * having final exact timestamp information for this frame) and
836 * there would unnecessarily be a vsync without a frame change.
837 */
838 uint64_t vsync = PREV_VSYNC(pts);
839 if (pts < vsync + vc->vsync_interval / 4
840 && (vsync - PREV_VSYNC(vc->last_queue_time)
841 > pts - vc->last_ideal_time + vc->vsync_interval / 2
842 || (vc->dropped_frame && vsync > vc->dropped_time)))
843 pts -= vc->vsync_interval / 2;
844
845 vc->dropped_time = ideal_pts;
846
847 pts = MPMAX(pts, vc->last_queue_time + vc->vsync_interval);
848 pts = MPMAX(pts, now);
849 if (npts < PREV_VSYNC(pts) + vc->vsync_interval)
850 goto drop;
851
852 int num_flips = update_presentation_queue_status(vo);
853 vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
854 pts = MPMAX(pts, now);
855 pts = MPMAX(pts, vsync + (vc->vsync_interval >> 2));
856 vsync = PREV_VSYNC(pts);
857 if (npts < vsync + vc->vsync_interval)
858 goto drop;
859 pts = vsync + (vc->vsync_interval >> 2);
860 VdpOutputSurface frame = vc->output_surfaces[vc->surface_num];
861 vdp_st = vdp->presentation_queue_display(vc->flip_queue, frame,
862 vo->dwidth, vo->dheight, pts);
863 CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_display");
864
865 MP_TRACE(vo, "Queue new surface %d: Vdpau time: %"PRIu64" "
866 "pts: %"PRIu64"\n", (int)frame, now, pts);
867
868 vc->last_queue_time = pts;
869 vc->queue_time[vc->surface_num] = pts;
870 vc->last_ideal_time = ideal_pts;
871 vc->dropped_frame = false;
872 vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
873 return;
874
875 drop:
876 vo_increment_drop_count(vo, 1);
877 }
878
draw_frame(struct vo * vo,struct vo_frame * frame)879 static void draw_frame(struct vo *vo, struct vo_frame *frame)
880 {
881 struct vdpctx *vc = vo->priv;
882
883 check_preemption(vo);
884
885 if (frame->current && !frame->redraw) {
886 struct mp_image *vdp_mpi =
887 mp_vdpau_upload_video_surface(vc->mpvdp, frame->current);
888 if (!vdp_mpi)
889 MP_ERR(vo, "Could not upload image.\n");
890
891 talloc_free(vc->current_image);
892 vc->current_image = vdp_mpi;
893 }
894
895 vc->current_pts = frame->pts;
896 vc->current_duration = frame->duration;
897
898 if (status_ok(vo)) {
899 video_to_output_surface(vo, vc->current_image);
900 draw_osd(vo);
901 }
902 }
903
904 // warning: the size and pixel format of surface must match that of the
905 // surfaces in vc->output_surfaces
read_output_surface(struct vo * vo,VdpOutputSurface surface)906 static struct mp_image *read_output_surface(struct vo *vo,
907 VdpOutputSurface surface)
908 {
909 struct vdpctx *vc = vo->priv;
910 VdpStatus vdp_st;
911 struct vdp_functions *vdp = vc->vdp;
912 if (!vo->params)
913 return NULL;
914
915 VdpRGBAFormat fmt;
916 uint32_t w, h;
917 vdp_st = vdp->output_surface_get_parameters(surface, &fmt, &w, &h);
918 if (vdp_st != VDP_STATUS_OK)
919 return NULL;
920
921 assert(fmt == OUTPUT_RGBA_FORMAT);
922
923 struct mp_image *image = mp_image_alloc(IMGFMT_BGR0, w, h);
924 if (!image)
925 return NULL;
926
927 void *dst_planes[] = { image->planes[0] };
928 uint32_t dst_pitches[] = { image->stride[0] };
929 vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes,
930 dst_pitches);
931 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_get_bits_native");
932
933 return image;
934 }
935
get_window_screenshot(struct vo * vo)936 static struct mp_image *get_window_screenshot(struct vo *vo)
937 {
938 struct vdpctx *vc = vo->priv;
939 int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces);
940 VdpOutputSurface screen = vc->output_surfaces[last_surface];
941 struct mp_image *image = read_output_surface(vo, screen);
942 if (image && image->w >= vo->dwidth && image->h >= vo->dheight)
943 mp_image_set_size(image, vo->dwidth, vo->dheight);
944 return image;
945 }
946
query_format(struct vo * vo,int format)947 static int query_format(struct vo *vo, int format)
948 {
949 struct vdpctx *vc = vo->priv;
950
951 if (mp_vdpau_get_format(format, NULL, NULL))
952 return 1;
953 if (!vc->force_yuv && mp_vdpau_get_rgb_format(format, NULL))
954 return 1;
955 return 0;
956 }
957
destroy_vdpau_objects(struct vo * vo)958 static void destroy_vdpau_objects(struct vo *vo)
959 {
960 struct vdpctx *vc = vo->priv;
961 struct vdp_functions *vdp = vc->vdp;
962
963 VdpStatus vdp_st;
964
965 free_video_specific(vo);
966
967 if (vc->flip_queue != VDP_INVALID_HANDLE) {
968 vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
969 CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_destroy");
970 }
971
972 if (vc->flip_target != VDP_INVALID_HANDLE) {
973 vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
974 CHECK_VDP_WARNING(vo, "Error when calling "
975 "vdp_presentation_queue_target_destroy");
976 }
977
978 for (int i = 0; i < vc->num_output_surfaces; i++) {
979 if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
980 continue;
981 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
982 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
983 }
984 if (vc->rotation_surface != VDP_INVALID_HANDLE) {
985 vdp_st = vdp->output_surface_destroy(vc->rotation_surface);
986 CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
987 }
988
989 for (int i = 0; i < MAX_OSD_PARTS; i++) {
990 struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
991 if (sfc->surface != VDP_INVALID_HANDLE) {
992 vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
993 CHECK_VDP_WARNING(vo, "Error when calling vdp_bitmap_surface_destroy");
994 }
995 }
996
997 mp_vdpau_destroy(vc->mpvdp);
998 vc->mpvdp = NULL;
999 }
1000
uninit(struct vo * vo)1001 static void uninit(struct vo *vo)
1002 {
1003 struct vdpctx *vc = vo->priv;
1004
1005 hwdec_devices_remove(vo->hwdec_devs, &vc->mpvdp->hwctx);
1006 hwdec_devices_destroy(vo->hwdec_devs);
1007
1008 /* Destroy all vdpau objects */
1009 mp_vdpau_mixer_destroy(vc->video_mixer);
1010 destroy_vdpau_objects(vo);
1011
1012 vo_x11_uninit(vo);
1013 }
1014
preinit(struct vo * vo)1015 static int preinit(struct vo *vo)
1016 {
1017 struct vdpctx *vc = vo->priv;
1018
1019 if (!vo_x11_init(vo))
1020 return -1;
1021
1022 if (!vo_x11_create_vo_window(vo, NULL, "vdpau")) {
1023 vo_x11_uninit(vo);
1024 return -1;
1025 }
1026
1027 vc->mpvdp = mp_vdpau_create_device_x11(vo->log, vo->x11->display, false);
1028 if (!vc->mpvdp) {
1029 vo_x11_uninit(vo);
1030 return -1;
1031 }
1032
1033 vo->hwdec_devs = hwdec_devices_create();
1034 hwdec_devices_add(vo->hwdec_devs, &vc->mpvdp->hwctx);
1035
1036 vc->video_mixer = mp_vdpau_mixer_create(vc->mpvdp, vo->log);
1037 vc->video_mixer->video_eq = mp_csp_equalizer_create(vo, vo->global);
1038
1039 if (mp_vdpau_guess_if_emulated(vc->mpvdp)) {
1040 MP_WARN(vo, "VDPAU is most likely emulated via VA-API.\n"
1041 "This is inefficient. Use --vo=gpu instead.\n");
1042 }
1043
1044 // Mark everything as invalid first so uninit() can tell what has been
1045 // allocated
1046 mark_vdpau_objects_uninitialized(vo);
1047
1048 mp_vdpau_handle_preemption(vc->mpvdp, &vc->preemption_counter);
1049
1050 vc->vdp_device = vc->mpvdp->vdp_device;
1051 vc->vdp = &vc->mpvdp->vdp;
1052
1053 vc->vdp->bitmap_surface_query_capabilities(vc->vdp_device, VDP_RGBA_FORMAT_A8,
1054 &vc->supports_a8, &(uint32_t){0}, &(uint32_t){0});
1055
1056 MP_WARN(vo, "Warning: this compatibility VO is low quality and may "
1057 "have issues with OSD, scaling, screenshots and more.\n"
1058 "vo=gpu is the preferred choice in any case and "
1059 "includes VDPAU support via hwdec=vdpau or vdpau-copy.\n");
1060
1061 return 0;
1062 }
1063
checked_resize(struct vo * vo)1064 static void checked_resize(struct vo *vo)
1065 {
1066 if (!status_ok(vo))
1067 return;
1068 resize(vo);
1069 }
1070
control(struct vo * vo,uint32_t request,void * data)1071 static int control(struct vo *vo, uint32_t request, void *data)
1072 {
1073 check_preemption(vo);
1074
1075 switch (request) {
1076 case VOCTRL_SET_PANSCAN:
1077 checked_resize(vo);
1078 return VO_TRUE;
1079 case VOCTRL_SET_EQUALIZER:
1080 vo->want_redraw = true;
1081 return true;
1082 case VOCTRL_RESET:
1083 forget_frames(vo, true);
1084 return true;
1085 case VOCTRL_SCREENSHOT_WIN:
1086 if (!status_ok(vo))
1087 return false;
1088 *(struct mp_image **)data = get_window_screenshot(vo);
1089 return true;
1090 }
1091
1092 int events = 0;
1093 int r = vo_x11_control(vo, &events, request, data);
1094
1095 if (events & VO_EVENT_RESIZE) {
1096 checked_resize(vo);
1097 } else if (events & VO_EVENT_EXPOSE) {
1098 vo->want_redraw = true;
1099 }
1100 vo_event(vo, events);
1101
1102 return r;
1103 }
1104
1105 #define OPT_BASE_STRUCT struct vdpctx
1106
1107 const struct vo_driver video_out_vdpau = {
1108 .description = "VDPAU with X11",
1109 .name = "vdpau",
1110 .caps = VO_CAP_FRAMEDROP | VO_CAP_ROTATE90,
1111 .preinit = preinit,
1112 .query_format = query_format,
1113 .reconfig = reconfig,
1114 .control = control,
1115 .draw_frame = draw_frame,
1116 .flip_page = flip_page,
1117 .wakeup = vo_x11_wakeup,
1118 .wait_events = vo_x11_wait_events,
1119 .uninit = uninit,
1120 .priv_size = sizeof(struct vdpctx),
1121 .options = (const struct m_option []){
1122 {"chroma-deint", OPT_FLAG(chroma_deint), OPTDEF_INT(1)},
1123 {"pullup", OPT_FLAG(pullup)},
1124 {"denoise", OPT_FLOAT(denoise), M_RANGE(0, 1)},
1125 {"sharpen", OPT_FLOAT(sharpen), M_RANGE(-1, 1)},
1126 {"hqscaling", OPT_INT(hqscaling), M_RANGE(0, 9)},
1127 {"fps", OPT_FLOAT(user_fps)},
1128 {"composite-detect", OPT_FLAG(composite_detect), OPTDEF_INT(1)},
1129 {"queuetime-windowed", OPT_INT(flip_offset_window), OPTDEF_INT(50)},
1130 {"queuetime-fs", OPT_INT(flip_offset_fs), OPTDEF_INT(50)},
1131 {"output-surfaces", OPT_INT(num_output_surfaces),
1132 M_RANGE(2, MAX_OUTPUT_SURFACES), OPTDEF_INT(3)},
1133 {"colorkey", OPT_COLOR(colorkey),
1134 .defval = &(const struct m_color){.r = 2, .g = 5, .b = 7, .a = 255}},
1135 {"force-yuv", OPT_FLAG(force_yuv)},
1136 {"queuetime_windowed", OPT_REPLACED("queuetime-windowed")},
1137 {"queuetime_fs", OPT_REPLACED("queuetime-fs")},
1138 {"output_surfaces", OPT_REPLACED("output-surfaces")},
1139 {NULL},
1140 },
1141 .options_prefix = "vo-vdpau",
1142 };
1143