]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/xvideo.c
XCB/XVideo: missing break
[vlc] / modules / video_output / xcb / xvideo.c
1 /**
2  * @file xvideo.c
3  * @brief X C Bindings video output module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2009 Rémi Denis-Courmont
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <limits.h>
29 #include <assert.h>
30
31 #include <xcb/xcb.h>
32 #include <xcb/shm.h>
33 #include <xcb/xv.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_vout_display.h>
38 #include <vlc_picture_pool.h>
39 #include <vlc_dialog.h>
40
41 #include "pictures.h"
42 #include "events.h"
43
44 #define ADAPTOR_TEXT N_("XVideo adaptor number")
45 #define ADAPTOR_LONGTEXT N_( \
46     "XVideo hardware adaptor to use. By default, VLC will " \
47     "use the first functional adaptor.")
48
49 #define FORMAT_TEXT N_("XVideo format id")
50 #define FORMAT_LONGTEXT N_( \
51     "XVideo image format id to use. By default, VLC will " \
52     "try to use the best match for the video being played.")
53
54 static int  Open (vlc_object_t *);
55 static void Close (vlc_object_t *);
56 static int EnumAdaptors (vlc_object_t *, const char *, int64_t **, char ***);
57
58 /*
59  * Module descriptor
60  */
61 vlc_module_begin ()
62     set_shortname (N_("XVideo"))
63     set_description (N_("XVideo output (XCB)"))
64     set_category (CAT_VIDEO)
65     set_subcategory (SUBCAT_VIDEO_VOUT)
66     set_capability ("vout display", 200)
67     set_callbacks (Open, Close)
68
69     add_integer ("xvideo-adaptor", -1,
70                  ADAPTOR_TEXT, ADAPTOR_LONGTEXT, true)
71         change_integer_cb (EnumAdaptors)
72     add_integer ("xvideo-format-id", 0,
73                  FORMAT_TEXT, FORMAT_LONGTEXT, true)
74     add_obsolete_bool ("xvideo-shm") /* removed in 2.0.0 */
75     add_shortcut ("xcb-xv", "xv", "xvideo", "xid")
76 vlc_module_end ()
77
78 #define MAX_PICTURES (128)
79
80 struct vout_display_sys_t
81 {
82     xcb_connection_t *conn;
83     vout_window_t *embed;/* VLC window */
84
85     xcb_cursor_t cursor; /* blank cursor */
86     xcb_window_t window; /* drawable X window */
87     xcb_gcontext_t gc;   /* context to put images */
88     xcb_xv_port_t port;  /* XVideo port */
89     uint32_t id;         /* XVideo format */
90     uint16_t width;      /* display width */
91     uint16_t height;     /* display height */
92     uint32_t data_size;  /* picture byte size (for non-SHM) */
93     bool     swap_uv;    /* U/V pointer must be swapped in a picture */
94     bool shm;            /* whether to use MIT-SHM */
95     bool visible;        /* whether it makes sense to draw at all */
96
97     xcb_xv_query_image_attributes_reply_t *att;
98     picture_pool_t *pool; /* picture pool */
99 };
100
101 static picture_pool_t *Pool (vout_display_t *, unsigned);
102 static void Display (vout_display_t *, picture_t *, subpicture_t *subpicture);
103 static int Control (vout_display_t *, int, va_list);
104 static void Manage (vout_display_t *);
105
106 /**
107  * Check that the X server supports the XVideo extension.
108  */
109 static bool CheckXVideo (vout_display_t *vd, xcb_connection_t *conn)
110 {
111     xcb_xv_query_extension_reply_t *r;
112     xcb_xv_query_extension_cookie_t ck = xcb_xv_query_extension (conn);
113     bool ok = false;
114
115     /* We need XVideo 2.2 for PutImage */
116     r = xcb_xv_query_extension_reply (conn, ck, NULL);
117     if (r == NULL)
118         msg_Dbg (vd, "XVideo extension not available");
119     else
120     if (r->major != 2)
121         msg_Dbg (vd, "XVideo extension v%"PRIu8".%"PRIu8" unknown",
122                  r->major, r->minor);
123     else
124     if (r->minor < 2)
125         msg_Dbg (vd, "XVideo extension v%"PRIu8".%"PRIu8" too old",
126                  r->major, r->minor);
127     else
128     {
129         msg_Dbg (vd, "using XVideo extension v%"PRIu8".%"PRIu8,
130                  r->major, r->minor);
131         ok = true;
132     }
133     free (r);
134     return ok;
135 }
136
137 static vlc_fourcc_t ParseFormat (vlc_object_t *obj,
138                                  const xcb_xv_image_format_info_t *restrict f)
139 {
140     switch (f->type)
141     {
142       case XCB_XV_IMAGE_FORMAT_INFO_TYPE_RGB:
143         switch (f->num_planes)
144         {
145           case 1:
146             switch (popcount (f->red_mask | f->green_mask | f->blue_mask))
147             {
148               case 24:
149                 if (f->bpp == 32 && f->depth == 32)
150                     return VLC_CODEC_ARGB;
151                 if (f->bpp == 32 && f->depth == 24)
152                     return VLC_CODEC_RGB32;
153                 if (f->bpp == 24 && f->depth == 24)
154                     return VLC_CODEC_RGB24;
155                 break;
156               case 16:
157                 if (f->byte_order != ORDER)
158                     return 0; /* Mixed endian! */
159                 if (f->bpp == 16 && f->depth == 16)
160                     return VLC_CODEC_RGB16;
161                 break;
162               case 15:
163                 if (f->byte_order != ORDER)
164                     return 0; /* Mixed endian! */
165                 if (f->bpp == 16 && f->depth == 16)
166                     return VLC_CODEC_RGBT;
167                 if (f->bpp == 16 && f->depth == 15)
168                     return VLC_CODEC_RGB15;
169                 break;
170               case 12:
171                 if (f->bpp == 16 && f->depth == 16)
172                     return VLC_CODEC_RGBA16;
173                 if (f->bpp == 16 && f->depth == 12)
174                     return VLC_CODEC_RGB12;
175                 break;
176               case 8:
177                 if (f->bpp == 8 && f->depth == 8)
178                     return VLC_CODEC_RGB8;
179                 break;
180             }
181             break;
182         }
183         msg_Err (obj, "unknown XVideo RGB format %"PRIx32" (%.4s)",
184                  f->id, f->guid);
185         msg_Dbg (obj, " %"PRIu8" planes, %"PRIu8" bits/pixel, "
186                  "depth %"PRIu8, f->num_planes, f->bpp, f->depth);
187         break;
188
189       case XCB_XV_IMAGE_FORMAT_INFO_TYPE_YUV:
190         if (f->u_sample_bits != f->v_sample_bits
191          || f->vhorz_u_period != f->vhorz_v_period
192          || f->vvert_u_period != f->vvert_v_period
193          || f->y_sample_bits != 8 || f->u_sample_bits != 8
194          || f->vhorz_y_period != 1 || f->vvert_y_period != 1)
195             goto bad;
196         switch (f->num_planes)
197         {
198           case 1:
199             switch (f->bpp)
200             {
201               /*untested: case 24:
202                 if (f->vhorz_u_period == 1 && f->vvert_u_period == 1)
203                     return VLC_CODEC_I444;
204                 break;*/
205               case 16:
206                 if (f->vhorz_u_period == 2 && f->vvert_u_period == 1)
207                 {
208                     if (!strcmp ((const char *)f->vcomp_order, "YUYV"))
209                         return VLC_CODEC_YUYV;
210                     if (!strcmp ((const char *)f->vcomp_order, "UYVY"))
211                         return VLC_CODEC_UYVY;
212                 }
213                 break;
214             }
215             break;
216           case 3:
217             switch (f->bpp)
218             {
219               case 12:
220                 if (f->vhorz_u_period == 2 && f->vvert_u_period == 2)
221                 {
222                     if (!strcmp ((const char *)f->vcomp_order, "YVU"))
223                         return VLC_CODEC_YV12;
224                     if (!strcmp ((const char *)f->vcomp_order, "YUV"))
225                         return VLC_CODEC_I420;
226                 }
227             }
228             break;
229         }
230     bad:
231         msg_Err (obj, "unknown XVideo YUV format %"PRIx32" (%.4s)", f->id,
232                  f->guid);
233         msg_Dbg (obj, " %"PRIu8" planes, %"PRIu32" bits/pixel, "
234                  "%"PRIu32"/%"PRIu32"/%"PRIu32" bits/sample", f->num_planes,
235                  f->bpp, f->y_sample_bits, f->u_sample_bits, f->v_sample_bits);
236         msg_Dbg (obj, " period: %"PRIu32"/%"PRIu32"/%"PRIu32"x"
237                  "%"PRIu32"/%"PRIu32"/%"PRIu32,
238                  f->vhorz_y_period, f->vhorz_u_period, f->vhorz_v_period,
239                  f->vvert_y_period, f->vvert_u_period, f->vvert_v_period);
240         msg_Warn (obj, " order: %.32s", f->vcomp_order);
241         break;
242     }
243     return 0;
244 }
245
246 static bool BetterFormat (vlc_fourcc_t a, const vlc_fourcc_t *tab,
247                           unsigned *rankp)
248 {
249     for (unsigned i = 0, max = *rankp; i < max && tab[i] != 0; i++)
250         if (tab[i] == a)
251         {
252             *rankp = i;
253             return true;
254         }
255     return false;
256 }
257
258 static xcb_xv_query_image_attributes_reply_t *
259 FindFormat (vlc_object_t *obj, xcb_connection_t *conn, video_format_t *fmt,
260             const xcb_xv_adaptor_info_t *a, uint32_t *idp)
261 {
262     /* Order chromas by preference */
263     vlc_fourcc_t tab[7];
264     const vlc_fourcc_t *chromav = tab;
265
266     vlc_fourcc_t chroma = var_InheritInteger (obj, "xvideo-format-id");
267     if (chroma != 0) /* Forced chroma */
268     {
269         tab[0] = chroma;
270         tab[1] = 0;
271     }
272     else if (vlc_fourcc_IsYUV (fmt->i_chroma)) /* YUV chroma */
273     {
274         chromav = vlc_fourcc_GetYUVFallback (fmt->i_chroma);
275     }
276     else /* RGB chroma */
277     {
278         tab[0] = fmt->i_chroma;
279         tab[1] = VLC_CODEC_RGB32;
280         tab[2] = VLC_CODEC_RGB24;
281         tab[3] = VLC_CODEC_RGB16;
282         tab[4] = VLC_CODEC_RGB15;
283         tab[5] = VLC_CODEC_YUYV;
284         tab[6] = 0;
285     }
286
287     /* Get available image formats */
288     xcb_xv_list_image_formats_reply_t *list =
289         xcb_xv_list_image_formats_reply (conn,
290             xcb_xv_list_image_formats (conn, a->base_id), NULL);
291     if (list == NULL)
292         return NULL;
293
294     /* Check available XVideo chromas */
295     xcb_xv_query_image_attributes_reply_t *attr = NULL;
296     unsigned rank = UINT_MAX;
297
298     for (const xcb_xv_image_format_info_t *f =
299              xcb_xv_list_image_formats_format (list),
300                                           *f_end =
301              f + xcb_xv_list_image_formats_format_length (list);
302          f < f_end;
303          f++)
304     {
305         chroma = ParseFormat (obj, f);
306         if (chroma == 0)
307             continue;
308
309         /* Oink oink! */
310         if ((chroma == VLC_CODEC_I420 || chroma == VLC_CODEC_YV12)
311          && a->name_size >= 4
312          && !memcmp ("OMAP", xcb_xv_adaptor_info_name (a), 4))
313         {
314             msg_Dbg (obj, "skipping slow I420 format");
315             continue; /* OMAP framebuffer sucks at YUV 4:2:0 */
316         }
317
318         if (!BetterFormat (chroma, chromav, &rank))
319             continue;
320
321         /* VLC pads scanline to 16 pixels internally */
322         unsigned width = (fmt->i_width + 31) & ~31;
323         unsigned height = (fmt->i_height + 15) & ~15;
324         xcb_xv_query_image_attributes_reply_t *i;
325         i = xcb_xv_query_image_attributes_reply (conn,
326             xcb_xv_query_image_attributes (conn, a->base_id, f->id,
327                                            width, height), NULL);
328         if (i == NULL)
329             continue;
330
331         if (i->width != width || i->height != height)
332         {
333             msg_Warn (obj, "incompatible size %ux%u -> %"PRIu32"x%"PRIu32,
334                       fmt->i_width, fmt->i_height,
335                       i->width, i->height);
336             var_Create (obj->p_libvlc, "xvideo-res-error", VLC_VAR_BOOL);
337             if (!var_GetBool (obj->p_libvlc, "xvideo-res-error"))
338             {
339                 dialog_FatalWait (obj, _("Video acceleration not available"),
340                     _("The XVideo rendering acceleration driver does not "
341                       "support the required resolution of %ux%u pixels but "
342                       "%"PRIu32"x%"PRIu32" pixels instead.\n"
343                       "Acceleration will thus be disabled. Performance may "
344                       "be degraded severely if the resolution is large."),
345                                   width, height, i->width, i->height);
346                 var_SetBool (obj->p_libvlc, "xvideo-res-error", true);
347             }
348             free (i);
349             continue;
350         }
351
352         fmt->i_chroma = chroma;
353         if (f->type == XCB_XV_IMAGE_FORMAT_INFO_TYPE_RGB)
354         {
355             fmt->i_rmask = f->red_mask;
356             fmt->i_gmask = f->green_mask;
357             fmt->i_bmask = f->blue_mask;
358         }
359         *idp = f->id;
360         free (attr);
361         attr = i;
362         if (rank == 0)
363             break; /* shortcut for perfect match */
364     }
365
366     free (list);
367     return attr;
368 }
369
370
371 /**
372  * Probe the X server.
373  */
374 static int Open (vlc_object_t *obj)
375 {
376     vout_display_t *vd = (vout_display_t *)obj;
377     vout_display_sys_t *p_sys;
378
379     if (!var_InheritBool (obj, "overlay"))
380         return VLC_EGENERIC;
381     p_sys = malloc (sizeof (*p_sys));
382     if (p_sys == NULL)
383         return VLC_ENOMEM;
384
385     vd->sys = p_sys;
386
387     /* Connect to X */
388     xcb_connection_t *conn;
389     const xcb_screen_t *screen;
390     uint16_t width, height;
391     p_sys->embed = XCB_parent_Create (vd, &conn, &screen, &width, &height);
392     if (p_sys->embed == NULL)
393     {
394         free (p_sys);
395         return VLC_EGENERIC;
396     }
397
398     p_sys->conn = conn;
399     p_sys->att = NULL;
400     p_sys->pool = NULL;
401
402     if (!CheckXVideo (vd, conn))
403     {
404         msg_Warn (vd, "Please enable XVideo 2.2 for faster video display");
405         goto error;
406     }
407
408     p_sys->window = xcb_generate_id (conn);
409     xcb_pixmap_t pixmap = xcb_generate_id (conn);
410
411     /* Cache adaptors infos */
412     xcb_xv_query_adaptors_reply_t *adaptors =
413         xcb_xv_query_adaptors_reply (conn,
414             xcb_xv_query_adaptors (conn, p_sys->embed->handle.xid), NULL);
415     if (adaptors == NULL)
416         goto error;
417
418     int adaptor_selected = var_InheritInteger (obj, "xvideo-adaptor");
419     int adaptor_current = -1;
420
421     /* */
422     video_format_t fmt;
423     vout_display_place_t place;
424
425     p_sys->port = 0;
426     vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
427     p_sys->width  = place.width;
428     p_sys->height = place.height;
429
430     xcb_xv_adaptor_info_iterator_t it;
431     for (it = xcb_xv_query_adaptors_info_iterator (adaptors);
432          it.rem > 0;
433          xcb_xv_adaptor_info_next (&it))
434     {
435         const xcb_xv_adaptor_info_t *a = it.data;
436
437         adaptor_current++;
438         if (adaptor_selected != -1 && adaptor_selected != adaptor_current)
439             continue;
440         if (!(a->type & XCB_XV_TYPE_INPUT_MASK)
441          || !(a->type & XCB_XV_TYPE_IMAGE_MASK))
442             continue;
443
444         /* Look for an image format */
445         video_format_ApplyRotation(&fmt, &vd->fmt);
446         free (p_sys->att);
447         p_sys->att = FindFormat (obj, conn, &fmt, a, &p_sys->id);
448         if (p_sys->att == NULL) /* No acceptable image formats */
449             continue;
450
451         /* Grab a port */
452         for (unsigned i = 0; i < a->num_ports; i++)
453         {
454              xcb_xv_port_t port = a->base_id + i;
455              xcb_xv_grab_port_reply_t *gr =
456                  xcb_xv_grab_port_reply (conn,
457                      xcb_xv_grab_port (conn, port, XCB_CURRENT_TIME), NULL);
458              uint8_t result = gr ? gr->result : 0xff;
459
460              free (gr);
461              if (result == 0)
462              {
463                  p_sys->port = port;
464                  goto grabbed_port;
465              }
466              msg_Dbg (vd, "cannot grab port %"PRIu32": Xv error %"PRIu8, port,
467                       result);
468         }
469         continue; /* No usable port */
470
471     grabbed_port:
472         /* Found port - initialize selected format */
473         msg_Dbg (vd, "using adaptor %.*s", (int)a->name_size,
474                  xcb_xv_adaptor_info_name (a));
475         msg_Dbg (vd, "using port %"PRIu32, p_sys->port);
476         msg_Dbg (vd, "using image format 0x%"PRIx32, p_sys->id);
477
478         /* Look for an X11 visual, create a window */
479         xcb_xv_format_t *f = xcb_xv_adaptor_info_formats (a);
480         for (uint_fast16_t i = a->num_formats; i > 0; i--, f++)
481         {
482             if (f->depth != screen->root_depth)
483                 continue; /* this would fail anyway */
484
485             uint32_t mask =
486                 XCB_CW_BACK_PIXMAP |
487                 XCB_CW_BACK_PIXEL |
488                 XCB_CW_BORDER_PIXMAP |
489                 XCB_CW_BORDER_PIXEL |
490                 XCB_CW_EVENT_MASK |
491                 XCB_CW_COLORMAP;
492             const uint32_t list[] = {
493                 /* XCB_CW_BACK_PIXMAP */
494                 pixmap,
495                 /* XCB_CW_BACK_PIXEL */
496                 screen->black_pixel,
497                 /* XCB_CW_BORDER_PIXMAP */
498                 pixmap,
499                 /* XCB_CW_BORDER_PIXEL */
500                 screen->black_pixel,
501                 /* XCB_CW_EVENT_MASK */
502                 XCB_EVENT_MASK_VISIBILITY_CHANGE,
503                 /* XCB_CW_COLORMAP */
504                 screen->default_colormap,
505             };
506             xcb_void_cookie_t c;
507
508             xcb_create_pixmap (conn, f->depth, pixmap, screen->root, 1, 1);
509             c = xcb_create_window_checked (conn, f->depth, p_sys->window,
510                  p_sys->embed->handle.xid, place.x, place.y,
511                  place.width, place.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
512                  f->visual, mask, list);
513             xcb_map_window (conn, p_sys->window);
514
515             if (!XCB_error_Check (vd, conn, "cannot create X11 window", c))
516             {
517                 msg_Dbg (vd, "using X11 visual ID 0x%"PRIx32
518                          " (depth: %"PRIu8")", f->visual, f->depth);
519                 msg_Dbg (vd, "using X11 window 0x%08"PRIx32, p_sys->window);
520                 goto created_window;
521             }
522         }
523         xcb_xv_ungrab_port (conn, p_sys->port, XCB_CURRENT_TIME);
524         p_sys->port = 0;
525         msg_Dbg (vd, "no usable X11 visual");
526         continue; /* No workable XVideo format (visual/depth) */
527
528     created_window:
529         break;
530     }
531     free (adaptors);
532     if (!p_sys->port)
533     {
534         msg_Err (vd, "no available XVideo adaptor");
535         goto error;
536     }
537
538     /* Create graphic context */
539     p_sys->gc = xcb_generate_id (conn);
540     xcb_create_gc (conn, p_sys->gc, p_sys->window, 0, NULL);
541     msg_Dbg (vd, "using X11 graphic context 0x%08"PRIx32, p_sys->gc);
542
543     /* Disable color keying if applicable */
544     {
545         xcb_intern_atom_reply_t *r =
546             xcb_intern_atom_reply (conn,
547                 xcb_intern_atom (conn, 1, 21, "XV_AUTOPAINT_COLORKEY"), NULL);
548         if (r != NULL && r->atom != 0)
549             xcb_xv_set_port_attribute(conn, p_sys->port, r->atom, 1);
550         free(r);
551     }
552
553     /* Colour space */
554     {
555         xcb_intern_atom_reply_t *r =
556             xcb_intern_atom_reply (conn,
557                 xcb_intern_atom (conn, 1, 13, "XV_ITURBT_709"), NULL);
558         if (r != NULL && r->atom != 0)
559             xcb_xv_set_port_attribute(conn, p_sys->port, r->atom,
560                                       fmt.i_height > 576);
561         free(r);
562     }
563
564     /* Create cursor */
565     p_sys->cursor = XCB_cursor_Create (conn, screen);
566
567     p_sys->shm = XCB_shm_Check (obj, conn);
568     p_sys->visible = false;
569
570     /* */
571     vout_display_info_t info = vd->info;
572     info.has_pictures_invalid = false;
573     info.has_event_thread = true;
574
575     /* Setup vout_display_t once everything is fine */
576     p_sys->swap_uv = vlc_fourcc_AreUVPlanesSwapped (fmt.i_chroma,
577                                                     vd->fmt.i_chroma);
578     if (p_sys->swap_uv)
579         fmt.i_chroma = vd->fmt.i_chroma;
580     vd->fmt = fmt;
581     vd->info = info;
582
583     vd->pool = Pool;
584     vd->prepare = NULL;
585     vd->display = Display;
586     vd->control = Control;
587     vd->manage = Manage;
588
589     /* */
590     bool is_fullscreen = vd->cfg->is_fullscreen;
591     if (is_fullscreen && vout_window_SetFullScreen (p_sys->embed, true))
592         is_fullscreen = false;
593     vout_display_SendEventFullscreen (vd, is_fullscreen);
594     vout_display_SendEventDisplaySize (vd, width, height, is_fullscreen);
595
596     return VLC_SUCCESS;
597
598 error:
599     Close (obj);
600     return VLC_EGENERIC;
601 }
602
603
604 /**
605  * Disconnect from the X server.
606  */
607 static void Close (vlc_object_t *obj)
608 {
609     vout_display_t *vd = (vout_display_t *)obj;
610     vout_display_sys_t *p_sys = vd->sys;
611
612     if (p_sys->pool)
613         picture_pool_Delete (p_sys->pool);
614
615     /* show the default cursor */
616     xcb_change_window_attributes (p_sys->conn, p_sys->embed->handle.xid, XCB_CW_CURSOR,
617                                   &(uint32_t) { XCB_CURSOR_NONE });
618     xcb_flush (p_sys->conn);
619
620     free (p_sys->att);
621     xcb_disconnect (p_sys->conn);
622     vout_display_DeleteWindow (vd, p_sys->embed);
623     free (p_sys);
624 }
625
626 static void PoolAlloc (vout_display_t *vd, unsigned requested_count)
627 {
628     vout_display_sys_t *p_sys = vd->sys;
629
630     const uint32_t *pitches= xcb_xv_query_image_attributes_pitches (p_sys->att);
631     const uint32_t *offsets= xcb_xv_query_image_attributes_offsets (p_sys->att);
632     const unsigned num_planes= __MIN(p_sys->att->num_planes, PICTURE_PLANE_MAX);
633     p_sys->data_size = p_sys->att->data_size;
634
635     picture_resource_t res = { NULL };
636     for (unsigned i = 0; i < num_planes; i++)
637     {
638         uint32_t data_size;
639         data_size = (i < num_planes - 1) ? offsets[i+1] : p_sys->data_size;
640
641         res.p[i].i_lines = (data_size - offsets[i]) / pitches[i];
642         res.p[i].i_pitch = pitches[i];
643     }
644
645     picture_t *pic_array[MAX_PICTURES];
646     requested_count = __MIN(requested_count, MAX_PICTURES);
647
648     unsigned count;
649     for (count = 0; count < requested_count; count++)
650     {
651         xcb_shm_seg_t seg = p_sys->shm ? xcb_generate_id (p_sys->conn) : 0;
652
653         if (XCB_picture_Alloc (vd, &res, p_sys->data_size, p_sys->conn, seg))
654             break;
655
656         /* Allocate further planes as specified by XVideo */
657         /* We assume that offsets[0] is zero */
658         for (unsigned i = 1; i < num_planes; i++)
659             res.p[i].p_pixels = res.p[0].p_pixels + offsets[i];
660
661         if (p_sys->swap_uv)
662         {   /* YVU: swap U and V planes */
663             uint8_t *buf = res.p[2].p_pixels;
664             res.p[2].p_pixels = res.p[1].p_pixels;
665             res.p[1].p_pixels = buf;
666         }
667
668         pic_array[count] = XCB_picture_NewFromResource (&vd->fmt, &res);
669         if (unlikely(pic_array[count] == NULL))
670             break;
671     }
672     xcb_flush (p_sys->conn);
673
674     if (count == 0)
675         return;
676
677     p_sys->pool = picture_pool_New (count, pic_array);
678     if (unlikely(p_sys->pool == NULL))
679         while (count > 0)
680             picture_Release(pic_array[--count]);
681 }
682
683 /**
684  * Return a direct buffer
685  */
686 static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
687 {
688     vout_display_sys_t *p_sys = vd->sys;
689
690     if (!p_sys->pool)
691         PoolAlloc (vd, requested_count);
692
693     return p_sys->pool;
694 }
695
696 /**
697  * Sends an image to the X server.
698  */
699 static void Display (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
700 {
701     vout_display_sys_t *p_sys = vd->sys;
702     xcb_shm_seg_t segment = XCB_picture_GetSegment(pic);
703     xcb_void_cookie_t ck;
704     video_format_t fmt;
705
706     if (!p_sys->visible)
707         goto out;
708
709     video_format_ApplyRotation(&fmt, &vd->source);
710
711     if (segment)
712         ck = xcb_xv_shm_put_image_checked (p_sys->conn, p_sys->port,
713                               p_sys->window, p_sys->gc, segment, p_sys->id, 0,
714                    /* Src: */ fmt.i_x_offset, fmt.i_y_offset,
715                               fmt.i_visible_width, fmt.i_visible_height,
716                    /* Dst: */ 0, 0, p_sys->width, p_sys->height,
717                 /* Memory: */ pic->p->i_pitch / pic->p->i_pixel_pitch,
718                               pic->p->i_lines, false);
719     else
720         ck = xcb_xv_put_image_checked (p_sys->conn, p_sys->port, p_sys->window,
721                           p_sys->gc, p_sys->id,
722                           fmt.i_x_offset, fmt.i_y_offset,
723                           fmt.i_visible_width, fmt.i_visible_height,
724                           0, 0, p_sys->width, p_sys->height,
725                           pic->p->i_pitch / pic->p->i_pixel_pitch,
726                           pic->p->i_lines,
727                           p_sys->data_size, pic->p->p_pixels);
728
729     /* Wait for reply. See x11.c for rationale. */
730     xcb_generic_error_t *e = xcb_request_check (p_sys->conn, ck);
731     if (e != NULL)
732     {
733         msg_Dbg (vd, "%s: X11 error %d", "cannot put image", e->error_code);
734         free (e);
735     }
736 out:
737     picture_Release (pic);
738     (void)subpicture;
739 }
740
741 static int Control (vout_display_t *vd, int query, va_list ap)
742 {
743     vout_display_sys_t *p_sys = vd->sys;
744
745     switch (query)
746     {
747     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
748     {
749         const vout_display_cfg_t *c = va_arg (ap, const vout_display_cfg_t *);
750         return vout_window_SetFullScreen (p_sys->embed, c->is_fullscreen);
751     }
752
753     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
754     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
755     case VOUT_DISPLAY_CHANGE_ZOOM:
756     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
757     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
758     {
759         const vout_display_cfg_t *cfg;
760         const video_format_t *source;
761
762         if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
763          || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
764         {
765             source = (const video_format_t *)va_arg (ap, const video_format_t *);
766             cfg = vd->cfg;
767         }
768         else
769         {
770             source = &vd->source;
771             cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
772         }
773
774         if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE && va_arg (ap, int))
775         {
776             vout_window_SetSize (p_sys->embed,
777                                  cfg->display.width, cfg->display.height);
778             return VLC_EGENERIC; /* Always fail. See x11.c for rationale. */
779         }
780
781         vout_display_place_t place;
782         vout_display_PlacePicture (&place, source, cfg, false);
783         p_sys->width  = place.width;
784         p_sys->height = place.height;
785
786         /* Move the picture within the window */
787         const uint32_t values[] = { place.x, place.y,
788                                     place.width, place.height, };
789         xcb_configure_window (p_sys->conn, p_sys->window,
790                               XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
791                             | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
792                               values);
793         xcb_flush (p_sys->conn);
794         return VLC_SUCCESS;
795     }
796     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
797     {
798         unsigned state = va_arg (ap, unsigned);
799         return vout_window_SetState (p_sys->embed, state);
800     }
801
802     /* Hide the mouse. It will be send when
803      * vout_display_t::info.b_hide_mouse is false */
804     case VOUT_DISPLAY_HIDE_MOUSE:
805         xcb_change_window_attributes (p_sys->conn, p_sys->embed->handle.xid,
806                                   XCB_CW_CURSOR, &(uint32_t){ p_sys->cursor });
807         xcb_flush (p_sys->conn);
808         return VLC_SUCCESS;
809     case VOUT_DISPLAY_RESET_PICTURES:
810         assert(0);
811     default:
812         msg_Err (vd, "Unknown request in XCB vout display");
813         return VLC_EGENERIC;
814     }
815 }
816
817 static void Manage (vout_display_t *vd)
818 {
819     vout_display_sys_t *p_sys = vd->sys;
820
821     XCB_Manage (vd, p_sys->conn, &p_sys->visible);
822 }
823
824 static int EnumAdaptors (vlc_object_t *obj, const char *var,
825                          int64_t **vp, char ***tp)
826 {
827     size_t n = 0;
828
829     /* Connect to X */
830     char *display = var_InheritString (obj, "x11-display");
831     xcb_connection_t *conn;
832     int snum;
833
834     conn = xcb_connect (display, &snum);
835     free (display);
836     if (xcb_connection_has_error (conn) /*== NULL*/)
837         goto error;
838
839     /* Find configured screen */
840     const xcb_setup_t *setup = xcb_get_setup (conn);
841     const xcb_screen_t *scr = NULL;
842     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
843          i.rem > 0; xcb_screen_next (&i))
844     {
845         if (snum == 0)
846         {
847             scr = i.data;
848             break;
849         }
850         snum--;
851     }
852     if (scr == NULL)
853         goto error;
854
855     xcb_xv_query_adaptors_reply_t *adaptors =
856         xcb_xv_query_adaptors_reply (conn,
857             xcb_xv_query_adaptors (conn, scr->root), NULL);
858     if (adaptors == NULL)
859         goto error;
860
861     xcb_xv_adaptor_info_iterator_t it;
862
863     for (it = xcb_xv_query_adaptors_info_iterator (adaptors);
864          it.rem > 0;
865          xcb_xv_adaptor_info_next (&it))
866         n++;
867
868     int64_t *values = xmalloc ((n + 1) * sizeof (*values));
869     char **texts = xmalloc ((n + 1) * sizeof (*texts));
870     *vp = values;
871     *tp = texts;
872     n = 0;
873
874     *(values++) = -1;
875     *(texts++) = strdup (N_("Auto"));
876     n++;
877
878     for (it = xcb_xv_query_adaptors_info_iterator (adaptors);
879          it.rem > 0;
880          xcb_xv_adaptor_info_next (&it))
881     {
882         const xcb_xv_adaptor_info_t *a = it.data;
883
884         n++;
885
886         if (!(a->type & XCB_XV_TYPE_INPUT_MASK)
887          || !(a->type & XCB_XV_TYPE_IMAGE_MASK))
888             continue;
889
890         *(values++) = n - 2;
891         *(texts++) = strndup (xcb_xv_adaptor_info_name (a), a->name_size);
892     }
893     free (adaptors);
894 error:
895     xcb_disconnect (conn);
896     (void) obj; (void) var;
897     return n;
898 }