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 struct vout_display_sys_t
57 xcb_connection_t *conn; /**< XCB connection */
58 vout_window_t *embed; /**< parent window */
59 vdp_t *vdp; /**< VDPAU back-end */
60 picture_t *current; /**< Currently visible picture */
62 xcb_window_t window; /**< target window (owned by VDPAU back-end) */
63 xcb_cursor_t cursor; /**< blank cursor */
64 VdpDevice device; /**< VDPAU device handle */
65 VdpPresentationQueueTarget target; /**< VDPAU presentation queue target */
66 VdpPresentationQueue queue; /**< VDPAU presentation queue */
67 VdpRGBAFormat rgb_fmt; /**< Output surface format */
69 picture_pool_t *pool; /**< pictures pool */
72 static void pictureSys_DestroyVDPAU(picture_sys_t *psys)
74 vdp_output_surface_destroy(psys->vdp, psys->surface);
75 vdp_release_x11(psys->vdp);
79 static void PictureDestroyVDPAU(picture_t *pic)
81 pictureSys_DestroyVDPAU(pic->p_sys);
85 static VdpStatus picture_NewVDPAU(vdp_t *vdp, VdpRGBAFormat rgb_fmt,
86 const video_format_t *restrict fmt,
87 picture_t **restrict picp)
89 picture_sys_t *psys = malloc(sizeof (*psys));
90 if (unlikely(psys == NULL))
91 return VDP_STATUS_RESOURCES;
93 psys->vdp = vdp_hold_x11(vdp, &psys->device);
95 VdpStatus err = vdp_output_surface_create(psys->vdp, psys->device,
96 rgb_fmt, fmt->i_visible_width, fmt->i_visible_height,
98 if (err != VDP_STATUS_OK)
100 vdp_release_x11(psys->vdp);
105 picture_resource_t res = {
107 .pf_destroy = PictureDestroyVDPAU,
110 picture_t *pic = picture_NewFromResource(fmt, &res);
111 if (unlikely(pic == NULL))
113 pictureSys_DestroyVDPAU(psys);
114 return VDP_STATUS_RESOURCES;
117 return VDP_STATUS_OK;
120 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
122 vout_display_sys_t *sys = vd->sys;
123 picture_t *pics[requested_count];
126 while (count < requested_count)
128 VdpStatus err = picture_NewVDPAU(sys->vdp, sys->rgb_fmt, &vd->fmt,
130 if (err != VDP_STATUS_OK)
132 msg_Err(vd, "%s creation failure: %s", "output surface",
133 vdp_get_error_string(sys->vdp, err));
143 picture_pool_t *pool = picture_pool_New(count, pics);
144 if (unlikely(pool == NULL))
146 picture_Release(pics[--count]);
150 static void PoolFree(vout_display_t *vd, picture_pool_t *pool)
152 vout_display_sys_t *sys = vd->sys;
154 if (sys->current != NULL)
155 picture_Release(sys->current);
156 picture_pool_Delete(pool);
159 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
161 vout_display_sys_t *sys = vd->sys;
163 if (sys->pool == NULL)
164 sys->pool = PoolAlloc(vd, requested_count);
168 static void RenderRegion(vout_display_t *vd, VdpOutputSurface target,
169 const subpicture_region_t *reg, int alpha)
171 vout_display_sys_t *sys = vd->sys;
172 VdpBitmapSurface surface;
173 VdpRGBAFormat fmt = VDP_RGBA_FORMAT_R8G8B8A8; /* TODO? YUVA */
176 /* Create GPU surface for sub-picture */
177 err = vdp_bitmap_surface_create(sys->vdp, sys->device, fmt,
178 reg->fmt.i_visible_width, reg->fmt.i_visible_height, VDP_FALSE,
180 if (err != VDP_STATUS_OK)
182 msg_Err(vd, "%s creation failure: %s", "bitmap surface",
183 vdp_get_error_string(sys->vdp, err));
187 /* Upload sub-picture to GPU surface */
188 picture_t *subpic = reg->p_picture;
189 const void *data = subpic->p[0].p_pixels;
190 uint32_t pitch = subpic->p[0].i_pitch;
192 err = vdp_bitmap_surface_put_bits_native(sys->vdp, surface, &data, &pitch,
194 if (err != VDP_STATUS_OK)
196 msg_Err(vd, "subpicture upload failure: %s",
197 vdp_get_error_string(sys->vdp, err));
201 /* Render onto main surface */
205 reg->i_x + reg->fmt.i_visible_width,
206 reg->i_y + reg->fmt.i_visible_height,
208 VdpColor color = { 1.f, 1.f, 1.f, reg->i_alpha * alpha / 65535.f };
209 VdpOutputSurfaceRenderBlendState state = {
210 .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
211 .blend_factor_source_color =
212 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
213 .blend_factor_destination_color =
214 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
215 .blend_factor_source_alpha =
216 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
217 .blend_factor_destination_alpha =
218 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
219 .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
220 .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
221 .blend_constant = { 0.f, 0.f, 0.f, 0.f },
224 err = vdp_output_surface_render_bitmap_surface(sys->vdp, target, &area,
225 surface, NULL, &color, &state, 0);
226 if (err != VDP_STATUS_OK)
227 msg_Err(vd, "blending failure: %s",
228 vdp_get_error_string(sys->vdp, err));
230 out:/* Destroy GPU surface */
231 vdp_bitmap_surface_destroy(sys->vdp, surface);
234 static void Queue(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
236 vout_display_sys_t *sys = vd->sys;
237 VdpOutputSurface surface = pic->p_sys->surface;
240 VdpPresentationQueueStatus status;
242 err = vdp_presentation_queue_query_surface_status(sys->vdp, sys->queue,
243 surface, &status, &ts);
244 if (err == VDP_STATUS_OK && status != VDP_PRESENTATION_QUEUE_STATUS_IDLE)
245 msg_Dbg(vd, "surface status: %u", status);
248 for (subpicture_region_t *r = subpic->p_region; r != NULL;
250 RenderRegion(vd, surface, r, subpic->i_alpha);
252 /* Compute picture presentation time */
253 mtime_t now = mdate();
256 err = vdp_presentation_queue_get_time(sys->vdp, sys->queue, &pts);
257 if (err != VDP_STATUS_OK)
259 msg_Err(vd, "presentation queue time failure: %s",
260 vdp_get_error_string(sys->vdp, err));
261 if (err == VDP_STATUS_DISPLAY_PREEMPTED)
262 vout_display_SendEventPicturesInvalid(vd);
266 mtime_t delay = pic->date - now;
268 delay = 0; /* core bug: date is not updated during pause */
269 if (unlikely(delay > CLOCK_FREQ))
270 { /* We would get stuck if the delay was too long. */
271 msg_Dbg(vd, "picture date corrupt: delay of %"PRId64" us", delay);
272 delay = CLOCK_FREQ / 50;
277 err = vdp_presentation_queue_display(sys->vdp, sys->queue, surface, 0, 0,
279 if (err != VDP_STATUS_OK)
280 msg_Err(vd, "presentation queue display failure: %s",
281 vdp_get_error_string(sys->vdp, err));
284 static void Wait(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
286 vout_display_sys_t *sys = vd->sys;
288 picture_t *current = sys->current;
291 picture_sys_t *psys = current->p_sys;
295 err = vdp_presentation_queue_block_until_surface_idle(sys->vdp,
296 sys->queue, psys->surface, &pts);
297 if (err != VDP_STATUS_OK)
299 msg_Err(vd, "presentation queue blocking error: %s",
300 vdp_get_error_string(sys->vdp, err));
301 picture_Release(pic);
304 picture_Release(current);
311 static int Control(vout_display_t *vd, int query, va_list ap)
313 vout_display_sys_t *sys = vd->sys;
317 case VOUT_DISPLAY_HIDE_MOUSE:
318 xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
319 XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
321 case VOUT_DISPLAY_RESET_PICTURES:
323 msg_Dbg(vd, "resetting pictures");
324 if (sys->pool != NULL)
326 PoolFree(vd, sys->pool);
330 const video_format_t *src= &vd->source;
331 video_format_t *fmt = &vd->fmt;
332 vout_display_place_t place;
334 vout_display_PlacePicture(&place, src, vd->cfg, false);
336 fmt->i_width = src->i_width * place.width / src->i_visible_width;
337 fmt->i_height = src->i_height * place.height / src->i_visible_height;
338 fmt->i_visible_width = place.width;
339 fmt->i_visible_height = place.height;
340 fmt->i_x_offset = src->i_x_offset * place.width / src->i_visible_width;
341 fmt->i_y_offset = src->i_y_offset * place.height / src->i_visible_height;
343 const uint32_t values[] = { place.x, place.y,
344 place.width, place.height, };
345 xcb_configure_window(sys->conn, sys->window,
346 XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
347 XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
351 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
353 const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
354 return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
356 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
358 unsigned state = va_arg(ap, unsigned);
359 return vout_window_SetState(sys->embed, state);
361 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
363 const vout_display_cfg_t *cfg = va_arg(ap, const vout_display_cfg_t *);
364 bool forced = va_arg(ap, int);
367 vout_window_SetSize(sys->embed,
368 cfg->display.width, cfg->display.height);
369 return VLC_EGENERIC; /* Always fail. See x11.c for rationale. */
372 vout_display_place_t place;
373 vout_display_PlacePicture(&place, &vd->source, cfg, false);
374 if (place.width != vd->fmt.i_visible_width
375 || place.height != vd->fmt.i_visible_height)
377 vout_display_SendEventPicturesInvalid(vd);
381 const uint32_t values[] = { place.x, place.y,
382 place.width, place.height, };
383 xcb_configure_window(sys->conn, sys->window,
384 XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
385 XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
389 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
390 case VOUT_DISPLAY_CHANGE_ZOOM:
391 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
392 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
393 vout_display_SendEventPicturesInvalid (vd);
396 msg_Err(vd, "unknown control request %d", query);
399 xcb_flush (sys->conn);
403 static void Manage(vout_display_t *vd)
405 vout_display_sys_t *sys = vd->sys;
408 XCB_Manage(vd, sys->conn, &visible);
411 static int xcb_screen_num(xcb_connection_t *conn, const xcb_screen_t *screen)
413 const xcb_setup_t *setup = xcb_get_setup(conn);
416 for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
417 i.rem > 0; xcb_screen_next(&i))
419 if (i.data->root == screen->root)
426 static int Open(vlc_object_t *obj)
428 if (!vlc_xlib_init(obj))
431 vout_display_t *vd = (vout_display_t *)obj;
432 vout_display_sys_t *sys = malloc(sizeof (*sys));
433 if (unlikely(sys == NULL))
436 const xcb_screen_t *screen;
437 uint16_t width, height;
438 sys->embed = XCB_parent_Create(vd, &sys->conn, &screen, &width, &height);
439 if (sys->embed == NULL)
445 /* Load the VDPAU back-end and create a device instance */
446 VdpStatus err = vdp_get_x11(sys->embed->display.x11,
447 xcb_screen_num(sys->conn, screen),
448 &sys->vdp, &sys->device);
449 if (err != VDP_STATUS_OK)
451 msg_Dbg(obj, "device creation failure: error %d", (int)err);
452 xcb_disconnect(sys->conn);
453 vout_display_DeleteWindow(vd, sys->embed);
459 if (vdp_get_information_string(sys->vdp, &info) == VDP_STATUS_OK)
460 msg_Dbg(vd, "using back-end %s", info);
462 /* Check source format */
463 VdpChromaType chroma;
464 VdpYCbCrFormat format;
465 if (vd->fmt.i_chroma == VLC_CODEC_VDPAU_VIDEO_420
466 || vd->fmt.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
469 if (vlc_fourcc_to_vdp_ycc(vd->fmt.i_chroma, &chroma, &format))
474 err = vdp_video_surface_query_capabilities(sys->vdp, sys->device,
475 chroma, &ok, &w, &h);
476 if (err != VDP_STATUS_OK)
478 msg_Err(vd, "%s capabilities query failure: %s", "video surface",
479 vdp_get_error_string(sys->vdp, err));
482 if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
484 msg_Err(vd, "source video %s not supported", "chroma type");
488 err = vdp_video_surface_query_get_put_bits_y_cb_cr_capabilities(
489 sys->vdp, sys->device, chroma, format, &ok);
490 if (err != VDP_STATUS_OK)
492 msg_Err(vd, "%s capabilities query failure: %s", "video surface",
493 vdp_get_error_string(sys->vdp, err));
498 msg_Err(vd, "source video %s not supported", "YCbCr format");
505 /* Check video mixer capabilities */
509 err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
510 sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
512 if (err != VDP_STATUS_OK)
514 msg_Err(vd, "%s capabilities query failure: %s",
515 "video mixer surface width",
516 vdp_get_error_string(sys->vdp, err));
519 if (min > vd->fmt.i_width || vd->fmt.i_width > max)
521 msg_Err(vd, "source video %s not supported", "width");
525 err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
526 sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
528 if (err != VDP_STATUS_OK)
530 msg_Err(vd, "%s capabilities query failure: %s",
531 "video mixer surface width",
532 vdp_get_error_string(sys->vdp, err));
535 if (min > vd->fmt.i_height || vd->fmt.i_height > max)
537 msg_Err(vd, "source video %s not supported", "height");
542 /* Select surface format */
543 static const VdpRGBAFormat rgb_fmts[] = {
544 VDP_RGBA_FORMAT_R10G10B10A2, VDP_RGBA_FORMAT_B10G10R10A2,
545 VDP_RGBA_FORMAT_B8G8R8A8, VDP_RGBA_FORMAT_R8G8B8A8,
549 for (i = 0; i < sizeof (rgb_fmts) / sizeof (rgb_fmts[0]); i++)
554 err = vdp_output_surface_query_capabilities(sys->vdp, sys->device,
555 rgb_fmts[i], &ok, &w, &h);
556 if (err != VDP_STATUS_OK)
558 msg_Err(vd, "%s capabilities query failure: %s", "output surface",
559 vdp_get_error_string(sys->vdp, err));
562 /* NOTE: Wrong! No warranties that zoom <= 100%! */
563 if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
566 sys->rgb_fmt = rgb_fmts[i];
567 msg_Dbg(vd, "using RGBA format %u", sys->rgb_fmt);
570 if (i == sizeof (rgb_fmts) / sizeof (rgb_fmts[0]))
572 msg_Err(vd, "no supported output surface format");
576 /* VDPAU-X11 requires a window dedicated to the back-end */
578 xcb_pixmap_t pix = xcb_generate_id(sys->conn);
579 xcb_create_pixmap(sys->conn, screen->root_depth, pix,
583 XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL |
584 XCB_CW_BORDER_PIXMAP | XCB_CW_BORDER_PIXEL |
585 XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
586 const uint32_t values[] = {
587 pix, screen->black_pixel, pix, screen->black_pixel,
588 XCB_EVENT_MASK_VISIBILITY_CHANGE, screen->default_colormap
590 vout_display_place_t place;
592 vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
593 sys->window = xcb_generate_id(sys->conn);
595 xcb_void_cookie_t c =
596 xcb_create_window_checked(sys->conn, screen->root_depth,
597 sys->window, sys->embed->handle.xid, place.x, place.y,
598 place.width, place.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
599 screen->root_visual, mask, values);
600 if (XCB_error_Check(vd, sys->conn, "window creation failure", c))
602 msg_Dbg(vd, "using X11 window 0x%08"PRIx32, sys->window);
603 xcb_map_window(sys->conn, sys->window);
606 /* Check bitmap capabilities (for SPU) */
607 const vlc_fourcc_t *spu_chromas = NULL;
609 static const vlc_fourcc_t subpicture_chromas[] = { VLC_CODEC_RGBA, 0 };
613 err = vdp_bitmap_surface_query_capabilities(sys->vdp, sys->device,
614 VDP_RGBA_FORMAT_R8G8B8A8, &ok, &w, &h);
615 if (err != VDP_STATUS_OK)
617 msg_Err(vd, "%s capabilities query failure: %s", "output surface",
618 vdp_get_error_string(sys->vdp, err));
622 spu_chromas = subpicture_chromas;
625 /* Initialize VDPAU queue */
626 err = vdp_presentation_queue_target_create_x11(sys->vdp, sys->device,
627 sys->window, &sys->target);
628 if (err != VDP_STATUS_OK)
630 msg_Err(vd, "%s creation failure: %s", "presentation queue target",
631 vdp_get_error_string(sys->vdp, err));
635 err = vdp_presentation_queue_create(sys->vdp, sys->device, sys->target,
637 if (err != VDP_STATUS_OK)
639 msg_Err(vd, "%s creation failure: %s", "presentation queue",
640 vdp_get_error_string(sys->vdp, err));
641 vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
645 VdpColor black = { 0.f, 0.f, 0.f, 1.f };
646 vdp_presentation_queue_set_background_color(sys->vdp, sys->queue, &black);
648 sys->cursor = XCB_cursor_Create(sys->conn, screen);
653 vd->info.has_pictures_invalid = true;
654 vd->info.has_event_thread = true;
655 vd->info.subpicture_chromas = spu_chromas;
656 vd->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
661 vd->control = Control;
665 bool is_fullscreen = vd->cfg->is_fullscreen;
666 if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
667 is_fullscreen = false;
668 vout_display_SendEventFullscreen(vd, is_fullscreen);
669 vout_display_SendEventDisplaySize(vd, width, height, is_fullscreen);
674 vdp_release_x11(sys->vdp);
675 xcb_disconnect(sys->conn);
676 vout_display_DeleteWindow(vd, sys->embed);
681 static void Close(vlc_object_t *obj)
683 vout_display_t *vd = (vout_display_t *)obj;
684 vout_display_sys_t *sys = vd->sys;
686 /* Restore cursor explicitly (parent window connection will survive) */
687 xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
688 XCB_CW_CURSOR, &(uint32_t) { XCB_CURSOR_NONE });
689 xcb_flush(sys->conn);
691 vdp_presentation_queue_destroy(sys->vdp, sys->queue);
692 vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
694 if (sys->pool != NULL)
695 PoolFree(vd, sys->pool);
697 vdp_release_x11(sys->vdp);
698 xcb_disconnect(sys->conn);
699 vout_display_DeleteWindow(vd, sys->embed);