3 * @brief VDPAU video display module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2009-2013 Rémi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_vout_display.h>
35 #include <vlc_picture_pool.h>
38 #include "vlc_vdpau.h"
41 static int Open(vlc_object_t *);
42 static void Close(vlc_object_t *);
45 set_shortname(N_("VDPAU"))
46 set_description(N_("VDPAU output"))
47 set_category(CAT_VIDEO)
48 set_subcategory(SUBCAT_VIDEO_VOUT)
49 set_capability("vout display", 300)
50 set_callbacks(Open, Close)
52 add_shortcut("vdpau", "xid")
55 #define MAX_PICTURES (32)
57 struct vout_display_sys_t
59 xcb_connection_t *conn; /**< XCB connection */
60 vout_window_t *embed; /**< parent window */
61 vdp_t *vdp; /**< VDPAU back-end */
62 picture_t *current; /**< Currently visible picture */
64 xcb_window_t window; /**< target window (owned by VDPAU back-end) */
65 xcb_cursor_t cursor; /**< blank cursor */
66 VdpDevice device; /**< VDPAU device handle */
67 VdpPresentationQueueTarget target; /**< VDPAU presentation queue target */
68 VdpPresentationQueue queue; /**< VDPAU presentation queue */
69 VdpRGBAFormat rgb_fmt; /**< Output surface format */
71 picture_pool_t *pool; /**< pictures pool */
72 picture_sys_t *pics[MAX_PICTURES];
75 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
77 vout_display_sys_t *sys = vd->sys;
78 const video_format_t *fmt = &vd->fmt;
79 picture_t *pics[MAX_PICTURES];
80 picture_resource_t res = { .p_sys = NULL };
82 if (requested_count > MAX_PICTURES)
83 requested_count = MAX_PICTURES;
86 while (count < requested_count)
88 picture_sys_t *psys = malloc(sizeof (*psys));
89 if (unlikely(psys == NULL))
92 VdpStatus err = vdp_output_surface_create(sys->vdp, sys->device,
93 sys->rgb_fmt, fmt->i_visible_width, fmt->i_visible_height,
95 if (err != VDP_STATUS_OK)
98 msg_Err(vd, "%s creation failure: %s", "output surface",
99 vdp_get_error_string(sys->vdp, err));
102 psys->device = sys->device;
103 psys->vdp = sys->vdp;
106 pics[count] = picture_NewFromResource(&vd->fmt, &res);
107 if (pics[count] == NULL)
112 sys->pics[count++] = res.p_sys;
115 return count ? picture_pool_New(count, pics) : NULL;
118 static void PoolFree(vout_display_t *vd, picture_pool_t *pool)
120 vout_display_sys_t *sys = vd->sys;
122 for (unsigned count = 0; count < MAX_PICTURES; count++)
124 picture_sys_t *psys = sys->pics[count];
128 VdpStatus err = vdp_output_surface_destroy(sys->vdp, psys->surface);
129 if (err != VDP_STATUS_OK)
130 msg_Err(vd, "%s destruction failure: %s", "output surface",
131 vdp_get_error_string(sys->vdp, err));
133 if (sys->current != NULL)
134 picture_Release(sys->current);
135 picture_pool_Delete(pool);
138 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
140 vout_display_sys_t *sys = vd->sys;
142 if (sys->pool == NULL)
143 sys->pool = PoolAlloc(vd, requested_count);
147 static void Queue(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
149 vout_display_sys_t *sys = vd->sys;
150 VdpOutputSurface surface = pic->p_sys->surface;
155 /* Compute picture presentation time */
156 mtime_t now = mdate();
159 err = vdp_presentation_queue_get_time(sys->vdp, sys->queue, &pts);
160 if (err != VDP_STATUS_OK)
162 msg_Err(vd, "presentation queue time failure: %s",
163 vdp_get_error_string(sys->vdp, err));
167 mtime_t delay = pic->date - now;
169 delay = 0; /* core bug: date is not updated during pause */
170 if (unlikely(delay > CLOCK_FREQ))
171 { /* We would get stuck if the delay was too long. */
172 msg_Dbg(vd, "picture date corrupt: delay of %"PRId64" us", delay);
173 delay = CLOCK_FREQ / 50;
178 err = vdp_presentation_queue_display(sys->vdp, sys->queue, surface, 0, 0,
180 if (err != VDP_STATUS_OK)
181 msg_Err(vd, "presentation queue display failure: %s",
182 vdp_get_error_string(sys->vdp, err));
185 static void Wait(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
187 vout_display_sys_t *sys = vd->sys;
189 picture_t *current = sys->current;
192 picture_sys_t *psys = current->p_sys;
196 err = vdp_presentation_queue_block_until_surface_idle(sys->vdp,
197 sys->queue, psys->surface, &pts);
198 if (err != VDP_STATUS_OK)
200 msg_Err(vd, "presentation queue blocking error: %s",
201 vdp_get_error_string(sys->vdp, err));
202 picture_Release(pic);
205 picture_Release(current);
212 static int Control(vout_display_t *vd, int query, va_list ap)
214 vout_display_sys_t *sys = vd->sys;
218 case VOUT_DISPLAY_HIDE_MOUSE:
219 xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
220 XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
222 case VOUT_DISPLAY_RESET_PICTURES:
224 msg_Dbg(vd, "resetting pictures");
225 if (sys->pool != NULL)
227 PoolFree(vd, sys->pool);
231 const video_format_t *src= &vd->source;
232 video_format_t *fmt = &vd->fmt;
233 vout_display_place_t place;
235 vout_display_PlacePicture(&place, src, vd->cfg, false);
237 fmt->i_width = src->i_width * place.width / src->i_visible_width;
238 fmt->i_height = src->i_height * place.height / src->i_visible_height;
239 fmt->i_visible_width = place.width;
240 fmt->i_visible_height = place.height;
241 fmt->i_x_offset = src->i_x_offset * place.width / src->i_visible_width;
242 fmt->i_y_offset = src->i_y_offset * place.height / src->i_visible_height;
244 const uint32_t values[] = { place.x, place.y,
245 place.width, place.height, };
246 xcb_configure_window(sys->conn, sys->window,
247 XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
248 XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
252 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
254 const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
255 return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
257 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
259 unsigned state = va_arg(ap, unsigned);
260 return vout_window_SetState(sys->embed, state);
262 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
264 const vout_display_cfg_t *cfg = va_arg(ap, const vout_display_cfg_t *);
265 bool forced = va_arg(ap, int);
268 vout_window_SetSize(sys->embed,
269 cfg->display.width, cfg->display.height);
270 return VLC_EGENERIC; /* Always fail. See x11.c for rationale. */
273 vout_display_place_t place;
274 vout_display_PlacePicture(&place, &vd->source, cfg, false);
275 if (place.width != vd->fmt.i_visible_width
276 || place.height != vd->fmt.i_visible_height)
278 vout_display_SendEventPicturesInvalid(vd);
282 const uint32_t values[] = { place.x, place.y,
283 place.width, place.height, };
284 xcb_configure_window(sys->conn, sys->window,
285 XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
286 XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
290 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
291 case VOUT_DISPLAY_CHANGE_ZOOM:
292 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
293 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
294 vout_display_SendEventPicturesInvalid (vd);
297 msg_Err(vd, "unknown control request %d", query);
300 xcb_flush (sys->conn);
304 static void Manage(vout_display_t *vd)
306 vout_display_sys_t *sys = vd->sys;
309 XCB_Manage(vd, sys->conn, &visible);
312 static int xcb_screen_num(xcb_connection_t *conn, const xcb_screen_t *screen)
314 const xcb_setup_t *setup = xcb_get_setup(conn);
317 for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
318 i.rem > 0; xcb_screen_next(&i))
320 if (i.data->root == screen->root)
327 static int Open(vlc_object_t *obj)
329 if (!vlc_xlib_init(obj))
332 vout_display_t *vd = (vout_display_t *)obj;
333 vout_display_sys_t *sys = malloc(sizeof (*sys));
334 if (unlikely(sys == NULL))
337 const xcb_screen_t *screen;
338 uint16_t width, height;
339 sys->embed = XCB_parent_Create(vd, &sys->conn, &screen, &width, &height);
340 if (sys->embed == NULL)
346 /* Load the VDPAU back-end and create a device instance */
347 VdpStatus err = vdp_get_x11(sys->embed->display.x11,
348 xcb_screen_num(sys->conn, screen),
349 &sys->vdp, &sys->device);
350 if (err != VDP_STATUS_OK)
352 msg_Dbg(obj, "device creation failure: error %d", (int)err);
353 xcb_disconnect(sys->conn);
354 vout_display_DeleteWindow(vd, sys->embed);
360 if (vdp_get_information_string(sys->vdp, &info) == VDP_STATUS_OK)
361 msg_Dbg(vd, "using back-end %s", info);
363 /* Check source format */
364 VdpChromaType chroma;
365 VdpYCbCrFormat format;
366 if (vlc_fourcc_to_vdp_ycc(vd->fmt.i_chroma, &chroma, &format))
371 err = vdp_video_surface_query_capabilities(sys->vdp, sys->device,
372 chroma, &ok, &w, &h);
373 if (err != VDP_STATUS_OK)
375 msg_Err(vd, "%s capabilities query failure: %s", "video surface",
376 vdp_get_error_string(sys->vdp, err));
379 if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
381 msg_Err(vd, "source video %s not supported", "chroma type");
385 err = vdp_video_surface_query_get_put_bits_y_cb_cr_capabilities(
386 sys->vdp, sys->device, chroma, format, &ok);
387 if (err != VDP_STATUS_OK)
389 msg_Err(vd, "%s capabilities query failure: %s", "video surface",
390 vdp_get_error_string(sys->vdp, err));
395 msg_Err(vd, "source video %s not supported", "YCbCr format");
402 /* Check video mixer capabilities */
406 err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
407 sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
409 if (err != VDP_STATUS_OK)
411 msg_Err(vd, "%s capabilities query failure: %s",
412 "video mixer surface width",
413 vdp_get_error_string(sys->vdp, err));
416 if (min > vd->fmt.i_width || vd->fmt.i_width > max)
418 msg_Err(vd, "source video %s not supported", "width");
422 err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
423 sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
425 if (err != VDP_STATUS_OK)
427 msg_Err(vd, "%s capabilities query failure: %s",
428 "video mixer surface width",
429 vdp_get_error_string(sys->vdp, err));
432 if (min > vd->fmt.i_height || vd->fmt.i_height > max)
434 msg_Err(vd, "source video %s not supported", "height");
439 /* Select surface format */
440 static const VdpRGBAFormat rgb_fmts[] = {
441 VDP_RGBA_FORMAT_R10G10B10A2, VDP_RGBA_FORMAT_B10G10R10A2,
442 VDP_RGBA_FORMAT_B8G8R8A8, VDP_RGBA_FORMAT_R8G8B8A8,
446 for (i = 0; i < sizeof (rgb_fmts) / sizeof (rgb_fmts[0]); i++)
451 err = vdp_output_surface_query_capabilities(sys->vdp, sys->device,
452 rgb_fmts[i], &ok, &w, &h);
453 if (err != VDP_STATUS_OK)
455 msg_Err(vd, "%s capabilities query failure: %s", "output surface",
456 vdp_get_error_string(sys->vdp, err));
459 /* NOTE: Wrong! No warranties that zoom <= 100%! */
460 if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
463 sys->rgb_fmt = rgb_fmts[i];
464 msg_Dbg(vd, "using RGBA format %u", sys->rgb_fmt);
467 if (i == sizeof (rgb_fmts) / sizeof (rgb_fmts[0]))
469 msg_Err(vd, "no supported output surface format");
473 /* VDPAU-X11 requires a window dedicated to the back-end */
475 xcb_pixmap_t pix = xcb_generate_id(sys->conn);
476 xcb_create_pixmap(sys->conn, screen->root_depth, pix,
480 XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL |
481 XCB_CW_BORDER_PIXMAP | XCB_CW_BORDER_PIXEL |
482 XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
483 const uint32_t values[] = {
484 pix, screen->black_pixel, pix, screen->black_pixel,
485 XCB_EVENT_MASK_VISIBILITY_CHANGE, screen->default_colormap
487 vout_display_place_t place;
489 vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
490 sys->window = xcb_generate_id(sys->conn);
492 xcb_void_cookie_t c =
493 xcb_create_window_checked(sys->conn, screen->root_depth,
494 sys->window, sys->embed->handle.xid, place.x, place.y,
495 place.width, place.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
496 screen->root_visual, mask, values);
497 if (XCB_error_Check(vd, sys->conn, "window creation failure", c))
499 msg_Dbg(vd, "using X11 window 0x%08"PRIx32, sys->window);
500 xcb_map_window(sys->conn, sys->window);
503 /* Initialize VDPAU queue */
504 err = vdp_presentation_queue_target_create_x11(sys->vdp, sys->device,
505 sys->window, &sys->target);
506 if (err != VDP_STATUS_OK)
508 msg_Err(vd, "%s creation failure: %s", "presentation queue target",
509 vdp_get_error_string(sys->vdp, err));
513 err = vdp_presentation_queue_create(sys->vdp, sys->device, sys->target,
515 if (err != VDP_STATUS_OK)
517 msg_Err(vd, "%s creation failure: %s", "presentation queue",
518 vdp_get_error_string(sys->vdp, err));
519 vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
523 VdpColor black = { 0.f, 0.f, 0.f, 1.f };
524 vdp_presentation_queue_set_background_color(sys->vdp, sys->queue, &black);
526 sys->cursor = XCB_cursor_Create(sys->conn, screen);
528 for (unsigned count = 0; count < MAX_PICTURES; count++)
529 sys->pics[count] = NULL;
533 vd->info.has_pictures_invalid = true;
534 vd->info.has_event_thread = true;
535 vd->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
540 vd->control = Control;
544 bool is_fullscreen = vd->cfg->is_fullscreen;
545 if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
546 is_fullscreen = false;
547 vout_display_SendEventFullscreen(vd, is_fullscreen);
548 vout_display_SendEventDisplaySize(vd, width, height, is_fullscreen);
553 vdp_release_x11(sys->vdp);
554 xcb_disconnect(sys->conn);
555 vout_display_DeleteWindow(vd, sys->embed);
560 static void Close(vlc_object_t *obj)
562 vout_display_t *vd = (vout_display_t *)obj;
563 vout_display_sys_t *sys = vd->sys;
565 /* Restore cursor explicitly (parent window connection will survive) */
566 xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
567 XCB_CW_CURSOR, &(uint32_t) { XCB_CURSOR_NONE });
568 xcb_flush(sys->conn);
570 vdp_presentation_queue_destroy(sys->vdp, sys->queue);
571 vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
573 if (sys->pool != NULL)
574 PoolFree(vd, sys->pool);
576 vdp_release_x11(sys->vdp);
577 xcb_disconnect(sys->conn);
578 vout_display_DeleteWindow(vd, sys->embed);