]> git.sesse.net Git - vlc/blobdiff - modules/video_output/xcb/xvideo.c
Ahem
[vlc] / modules / video_output / xcb / xvideo.c
index 6d908b04e73d126a93805256f73d6c19551f4e55..11ea8b586957ce0cc2ed3a4999995e96db8c1895 100644 (file)
@@ -79,6 +79,7 @@ struct vout_sys_t
     uint32_t id;         /* XVideo format */
     uint16_t width;      /* display width */
     uint16_t height;     /* display height */
+    uint32_t data_size;  /* picture byte size (for non-SHM) */
     bool shm;            /* whether to use MIT-SHM */
 };
 
@@ -86,6 +87,7 @@ static int Init (vout_thread_t *);
 static void Deinit (vout_thread_t *);
 static void Display (vout_thread_t *, picture_t *);
 static int Manage (vout_thread_t *);
+static int Control (vout_thread_t *, int, va_list);
 
 int CheckError (vout_thread_t *vout, const char *str, xcb_void_cookie_t ck)
 {
@@ -208,6 +210,7 @@ static int Open (vlc_object_t *obj)
     vout->pf_end = Deinit;
     vout->pf_display = Display;
     vout->pf_manage = Manage;
+    vout->pf_control = Control;
     return VLC_SUCCESS;
 
 error:
@@ -230,30 +233,6 @@ static void Close (vlc_object_t *obj)
     free (p_sys);
 }
 
-static int QueryBestSize (vout_thread_t *vout,
-                          unsigned *restrict width, unsigned *restrict height)
-{
-    vout_sys_t *p_sys = vout->p_sys;
-    xcb_xv_query_best_size_reply_t *r;
-    xcb_xv_query_best_size_cookie_t ck;
-
-    ck = xcb_xv_query_best_size (p_sys->conn, p_sys->port,
-                                 vout->fmt_in.i_visible_width,
-                                 vout->fmt_in.i_visible_height,
-                                 *width, *height, false);
-    r = xcb_xv_query_best_size_reply (p_sys->conn, ck, NULL);
-    if (r == NULL)
-       return VLC_EGENERIC;
-
-    msg_Dbg (vout, "best size: %ux%u -> %ux%u", *width, *height,
-             r->actual_width, r->actual_height);
-    *width = r->actual_width;
-    *height = r->actual_height;
-    free (r);
-    return VLC_SUCCESS;
-}
-
-
 static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
                                  const xcb_xv_image_format_info_t *restrict f)
 {
@@ -270,21 +249,21 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
             {
               case 32:
                 if (f->depth == 24)
-                    return VLC_FOURCC ('R', 'V', '3', '2');
+                    return VLC_CODEC_RGB32;
                 break;
               case 24:
                 if (f->depth == 24)
-                    return VLC_FOURCC ('R', 'V', '2', '4');
+                    return VLC_CODEC_RGB24;
                 break;
               case 16:
                 if (f->depth == 16)
-                    return VLC_FOURCC ('R', 'V', '1', '6');
+                    return VLC_CODEC_RGB16;
                 if (f->depth == 15)
-                    return VLC_FOURCC ('R', 'V', '1', '5');
+                    return VLC_CODEC_RGB15;
                 break;
               case 8:
                 if (f->depth == 8)
-                    return VLC_FOURCC ('R', 'G', 'B', '2');
+                    return VLC_CODEC_RGB8;
                 break;
             }
             break;
@@ -309,15 +288,15 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
             {
               /*untested: case 24:
                 if (f->vhorz_u_period == 1 && f->vvert_u_period == 1)
-                    return VLC_FOURCC ('I', '4', '4', '4');
+                    return VLC_CODEC_I444;
                 break;*/
               case 16:
                 if (f->vhorz_u_period == 2 && f->vvert_u_period == 1)
                 {
                     if (!strcmp ((const char *)f->vcomp_order, "YUYV"))
-                        return VLC_FOURCC ('Y', 'U', 'Y', '2');
+                        return VLC_CODEC_YUYV;
                     if (!strcmp ((const char *)f->vcomp_order, "UYVY"))
-                        return VLC_FOURCC ('U', 'Y', 'V', 'Y');
+                        return VLC_CODEC_UYVY;
                 }
                 break;
             }
@@ -329,9 +308,9 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
                 if (f->vhorz_u_period == 2 && f->vvert_u_period == 2)
                 {
                     if (!strcmp ((const char *)f->vcomp_order, "YVU"))
-                        return VLC_FOURCC ('Y', 'V', '1', '2');
+                        return VLC_CODEC_YV12;
                     if (!strcmp ((const char *)f->vcomp_order, "YUV"))
-                        return VLC_FOURCC ('I', '4', '2', '0');
+                        return VLC_CODEC_I420;
                 }
             }
             break;
@@ -353,13 +332,55 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
 }
 
 
+static const xcb_xv_image_format_info_t *
+FindFormat (vout_thread_t *vout, vlc_fourcc_t chroma, xcb_xv_port_t port,
+            const xcb_xv_list_image_formats_reply_t *list,
+            xcb_xv_query_image_attributes_reply_t **restrict pa)
+{
+    xcb_connection_t *conn = vout->p_sys->conn;
+    const xcb_xv_image_format_info_t *f, *end;
+
+#ifndef XCB_XV_OLD
+    f = xcb_xv_list_image_formats_format (list);
+#else
+    f = (xcb_xv_image_format_info_t *) (list + 1);
+#endif
+    end = f + xcb_xv_list_image_formats_format_length (list);
+    for (; f < end; f++)
+    {
+        if (chroma != ParseFormat (vout, f))
+            continue;
+
+        xcb_xv_query_image_attributes_reply_t *i;
+        i = xcb_xv_query_image_attributes_reply (conn,
+            xcb_xv_query_image_attributes (conn, port, f->id,
+                vout->fmt_in.i_width, vout->fmt_in.i_height), NULL);
+        if (i == NULL)
+            continue;
+
+        if (i->width != vout->fmt_in.i_width
+         || i->height != vout->fmt_in.i_height)
+        {
+            msg_Warn (vout, "incompatible size %ux%u -> %"PRIu32"x%"PRIu32,
+                      vout->fmt_in.i_width, vout->fmt_in.i_height,
+                      i->width, i->height);
+            free (i);
+            continue;
+        }
+        *pa = i;
+        return f;
+    }
+    return NULL;
+}
+
 /**
  * Allocate drawable window and picture buffers.
  */
 static int Init (vout_thread_t *vout)
 {
     vout_sys_t *p_sys = vout->p_sys;
-    unsigned x, y, width, height;
+    xcb_xv_query_image_attributes_reply_t *att = NULL;
+    bool swap_planes = false; /* whether X wants V before U */
 
     /* FIXME: check max image size */
     xcb_xv_adaptor_info_iterator_t it;
@@ -379,48 +400,93 @@ static int Init (vout_thread_t *vout)
         if (r == NULL)
             continue;
 
-        const xcb_xv_image_format_info_t *f, *end;
-        f = xcb_xv_list_image_formats_format (r);
-        end = f + xcb_xv_list_image_formats_format_length (r);
-        for (; f < end; f++)
-        {
-            vlc_fourcc_t chroma = ParseFormat (vout, f);
-            if (!chroma)
-                continue;
+        const xcb_xv_image_format_info_t *fmt;
 
-            if (chroma == vout->fmt_in.i_chroma)
+        /* Video chroma in preference order */
+        const vlc_fourcc_t chromas[] = {
+            vout->fmt_in.i_chroma,
+            VLC_CODEC_YUYV,
+            VLC_CODEC_RGB24,
+            VLC_CODEC_RGB15,
+        };
+        for (size_t i = 0; i < sizeof (chromas) / sizeof (chromas[0]); i++)
+        {
+            vlc_fourcc_t chroma = chromas[i];
+            fmt = FindFormat (vout, chroma, a->base_id, r, &att);
+            if (fmt != NULL)
             {
                 vout->output.i_chroma = chroma;
-                p_sys->id = f->id;
-                break;
+                goto found_format;
             }
-            /* TODO: RGB masks */
         }
-
         free (r);
-        /* TODO: grab port */
+        continue;
 
-        msg_Dbg (vout, "using image format 0x%"PRIx32, p_sys->id);
+    found_format:
+        /* TODO: grab port */
         p_sys->port = a->base_id;
         msg_Dbg (vout, "using port %"PRIu32, p_sys->port);
-        break;
+
+        p_sys->id = fmt->id;
+        msg_Dbg (vout, "using image format 0x%"PRIx32, p_sys->id);
+        if (fmt->type == XCB_XV_IMAGE_FORMAT_INFO_TYPE_RGB)
+        {
+            vout->fmt_out.i_rmask = vout->output.i_rmask = fmt->red_mask;
+            vout->fmt_out.i_gmask = vout->output.i_gmask = fmt->green_mask;
+            vout->fmt_out.i_bmask = vout->output.i_bmask = fmt->blue_mask;
+        }
+        else
+        if (fmt->num_planes == 3)
+            swap_planes = !strcmp ((const char *)fmt->vcomp_order, "YVU");
+        free (r);
+        goto found_adaptor;
+    }
+    msg_Err (vout, "no available XVideo adaptor");
+    return VLC_EGENERIC; /* no usable adaptor */
+
+    /* Allocate picture buffers */
+    const uint32_t *offsets;
+found_adaptor:
+    offsets = xcb_xv_query_image_attributes_offsets (att);
+    p_sys->data_size = att->data_size;
+
+    I_OUTPUTPICTURES = 0;
+    for (size_t index = 0; I_OUTPUTPICTURES < 2; index++)
+    {
+        picture_t *pic = vout->p_picture + index;
+
+        if (index > sizeof (vout->p_picture) / sizeof (pic))
+            break;
+        if (pic->i_status != FREE_PICTURE)
+            continue;
+
+        picture_Setup (pic, vout->output.i_chroma,
+                       att->width, att->height,
+                       vout->fmt_in.i_aspect);
+        if (PictureAlloc (vout, pic, att->data_size,
+                          p_sys->shm ? p_sys->conn : NULL))
+            break;
+        /* Allocate further planes as specified by XVideo */
+        /* We assume that offsets[0] is zero */
+        for (int i = 1; i < pic->i_planes; i++)
+             pic->p[i].p_pixels =
+                 pic->p->p_pixels + offsets[swap_planes ? (3 - i) : i];
+        PP_OUTPUTPICTURE[I_OUTPUTPICTURES++] = pic;
     }
+    free (att);
 
-    /* TODO: fallback to RV24 or I420 */
-    if (!vout->output.i_chroma)
-        return VLC_EGENERIC; /* no usable adaptor */
+    unsigned x, y, width, height;
 
     if (GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
         return VLC_EGENERIC;
     vout_PlacePicture (vout, width, height, &x, &y, &width, &height);
-    if (QueryBestSize (vout, &width, &height))
-        return VLC_EGENERIC;
 
     const uint32_t values[] = { x, y, width, height, };
     xcb_configure_window (p_sys->conn, p_sys->window,
                           XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
                           XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
                           values);
+    xcb_flush (p_sys->conn);
     p_sys->height = height;
     p_sys->width = width;
 
@@ -432,32 +498,12 @@ static int Init (vout_thread_t *vout)
     vout->output.i_width = vout->fmt_out.i_width = vout->fmt_in.i_width;
     vout->output.i_height = vout->fmt_out.i_height = vout->fmt_in.i_height;
     vout->fmt_out.i_x_offset = vout->fmt_in.i_x_offset;
-    p_vout->fmt_out.i_y_offset = vout->fmt_in.i_y_offset;
+    vout->fmt_out.i_y_offset = vout->fmt_in.i_y_offset;
 
     assert (height > 0);
     vout->output.i_aspect = vout->fmt_out.i_aspect =
         width * VOUT_ASPECT_FACTOR / height;
 
-    /* Allocate picture buffers */
-    I_OUTPUTPICTURES = 0;
-    for (size_t index = 0; I_OUTPUTPICTURES < 2; index++)
-    {
-        picture_t *pic = vout->p_picture + index;
-
-        if (index > sizeof (vout->p_picture) / sizeof (pic))
-            break;
-        if (pic->i_status != FREE_PICTURE)
-            continue;
-
-        vout_InitPicture (vout, pic, vout->output.i_chroma,
-                          vout->output.i_width, vout->output.i_height,
-                          vout->output.i_aspect);
-        if (PictureAlloc (vout, pic, pic->p->i_pitch * pic->p->i_lines,
-                          p_sys->shm ? p_sys->conn : NULL))
-            break;
-        PP_OUTPUTPICTURE[I_OUTPUTPICTURES++] = pic;
-    }
-    xcb_flush (p_sys->conn);
     return VLC_SUCCESS;
 }
 
@@ -483,9 +529,10 @@ static void Display (vout_thread_t *vout, picture_t *pic)
     if (segment)
         xcb_xv_shm_put_image (p_sys->conn, p_sys->port, p_sys->window,
                               p_sys->gc, segment, p_sys->id, 0,
-                              /* Src: */ 0, 0,
-                              pic->p->i_visible_pitch / pic->p->i_pixel_pitch,
-                              pic->p->i_visible_lines,
+                              /* Src: */ vout->fmt_out.i_x_offset,
+                              vout->fmt_out.i_y_offset,
+                              vout->fmt_out.i_visible_width,
+                              vout->fmt_out.i_visible_height,
                               /* Dst: */ 0, 0, p_sys->width, p_sys->height,
                               /* Memory: */
                               pic->p->i_pitch / pic->p->i_pixel_pitch,
@@ -493,13 +540,12 @@ static void Display (vout_thread_t *vout, picture_t *pic)
     else
         xcb_xv_put_image (p_sys->conn, p_sys->port, p_sys->window,
                           p_sys->gc, p_sys->id,
-                          0, 0,
-                          pic->p->i_visible_pitch / pic->p->i_pixel_pitch,
-                          pic->p->i_visible_lines,
+                          vout->fmt_out.i_x_offset, vout->fmt_out.i_y_offset,
+                          vout->fmt_out.i_visible_width,
+                          vout->fmt_out.i_visible_height,
                           0, 0, p_sys->width, p_sys->height,
-                          pic->p->i_pitch / pic->p->i_pixel_pitch,
-                          pic->p->i_lines,
-                          pic->p->i_pitch * pic->p->i_lines, pic->p->p_pixels);
+                          vout->fmt_out.i_width, vout->fmt_out.i_height,
+                          p_sys->data_size, pic->p->p_pixels);
     xcb_flush (p_sys->conn);
 }
 
@@ -519,6 +565,24 @@ static int Manage (vout_thread_t *vout)
         msg_Err (vout, "X server failure");
         return VLC_EGENERIC;
     }
+
+    CommonManage (vout);
+    if (vout->i_changes & VOUT_SIZE_CHANGE)
+    {   /* TODO: factor this code with XV and X11 Init() */
+        unsigned x, y, width, height;
+
+        if (GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
+            return VLC_EGENERIC;
+        vout_PlacePicture (vout, width, height, &x, &y, &width, &height);
+
+        const uint32_t values[] = { x, y, width, height, };
+        xcb_configure_window (p_sys->conn, p_sys->window, XCB_CONFIG_WINDOW_X |
+                              XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
+                              XCB_CONFIG_WINDOW_HEIGHT, values);
+        vout->p_sys->width = width; // XXX: <-- this is useless, as the zoom is
+        vout->p_sys->height = height; // handled with VOUT_SET_SIZE anyway.
+        vout->i_changes &= ~VOUT_SIZE_CHANGE;
+    }
     return VLC_SUCCESS;
 }
 
@@ -539,3 +603,8 @@ HandleParentStructure (vout_thread_t *vout, xcb_connection_t *conn,
     vout->p_sys->width = width;
     vout->p_sys->height = height;
 }
+
+static int Control (vout_thread_t *vout, int query, va_list ap)
+{
+    return vout_ControlWindow (vout->p_sys->embed, query, ap);
+}