Commit e8534473 authored by Laurent Aimar's avatar Laurent Aimar

Converted vout xcb to "vout display" API.

parent c48d9cfc
......@@ -34,11 +34,27 @@
#include <xcb/shm.h>
#include <vlc_common.h>
#include <vlc_vout.h>
#include <vlc_vout_window.h>
#include <vlc_vout_display.h>
#include "xcb_vlc.h"
/**
* Check for an error
*/
int CheckError (vout_display_t *vd, xcb_connection_t *conn,
const char *str, xcb_void_cookie_t ck)
{
xcb_generic_error_t *err;
err = xcb_request_check (conn, ck);
if (err)
{
msg_Err (vd, "%s: X11 error %d", str, err->error_code);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/**
* Connect to the X server.
*/
......@@ -62,7 +78,7 @@ xcb_connection_t *Connect (vlc_object_t *obj)
* Create a VLC video X window object, find the corresponding X server screen,
* and probe the MIT-SHM extension.
*/
vout_window_t *GetWindow (vout_thread_t *obj,
vout_window_t *GetWindow (vout_display_t *vd,
xcb_connection_t *conn,
const xcb_screen_t **restrict pscreen,
bool *restrict pshm)
......@@ -73,14 +89,13 @@ vout_window_t *GetWindow (vout_thread_t *obj,
memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
wnd_cfg.width = obj->i_window_width;
wnd_cfg.height = obj->i_window_height;
vout_window_t *wnd = vout_window_New (VLC_OBJECT(obj), NULL, &wnd_cfg);
wnd_cfg.width = vd->cfg->display.width;
wnd_cfg.height = vd->cfg->display.height;
vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
if (wnd == NULL)
{
msg_Err (obj, "parent window not available");
msg_Err (vd, "parent window not available");
return NULL;
}
else
......@@ -92,7 +107,7 @@ vout_window_t *GetWindow (vout_thread_t *obj,
geo = xcb_get_geometry_reply (conn, ck, NULL);
if (geo == NULL)
{
msg_Err (obj, "parent window not valid");
msg_Err (vd, "parent window not valid");
goto error;
}
root = geo->root;
......@@ -116,13 +131,13 @@ vout_window_t *GetWindow (vout_thread_t *obj,
if (screen == NULL)
{
msg_Err (obj, "parent window screen not found");
msg_Err (vd, "parent window screen not found");
goto error;
}
msg_Dbg (obj, "using screen 0x%"PRIx32, root);
msg_Dbg (vd, "using screen 0x%"PRIx32, root);
/* Check MIT-SHM shared memory support */
bool shm = var_CreateGetBool (obj, "x11-shm") > 0;
bool shm = var_CreateGetBool (vd, "x11-shm") > 0;
if (shm)
{
xcb_shm_query_version_cookie_t ck;
......@@ -132,8 +147,8 @@ vout_window_t *GetWindow (vout_thread_t *obj,
r = xcb_shm_query_version_reply (conn, ck, NULL);
if (!r)
{
msg_Err (obj, "shared memory (MIT-SHM) not available");
msg_Warn (obj, "display will be slow");
msg_Err (vd, "shared memory (MIT-SHM) not available");
msg_Warn (vd, "display will be slow");
shm = false;
}
free (r);
......@@ -144,7 +159,7 @@ vout_window_t *GetWindow (vout_thread_t *obj,
return wnd;
error:
vout_window_Delete (wnd);
vout_display_DeleteWindow (vd, wnd);
return NULL;
}
......@@ -168,19 +183,22 @@ int GetWindowSize (struct vout_window_t *wnd, xcb_connection_t *conn,
/**
* Initialize a picture buffer as shared memory, according to the video output
* format. If a XCB connection pointer is supplied, the segment is attached to
* format. If a attach is true, the segment is attached to
* the X server (MIT-SHM extension).
*/
int PictureAlloc (vout_thread_t *vout, picture_t *pic, size_t size,
xcb_connection_t *conn)
int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
xcb_connection_t *conn, bool attach)
{
assert (pic->i_status == FREE_PICTURE);
res->p_sys = malloc (sizeof(*res->p_sys));
if (!res->p_sys)
return VLC_EGENERIC;
/* Allocate shared memory segment */
int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
if (id == -1)
{
msg_Err (vout, "shared memory allocation error: %m");
msg_Err (vd, "shared memory allocation error: %m");
free (res->p_sys);
return VLC_EGENERIC;
}
......@@ -188,13 +206,14 @@ int PictureAlloc (vout_thread_t *vout, picture_t *pic, size_t size,
void *shm = shmat (id, NULL, 0 /* read/write */);
if (-1 == (intptr_t)shm)
{
msg_Err (vout, "shared memory attachment error: %m");
msg_Err (vd, "shared memory attachment error: %m");
shmctl (id, IPC_RMID, 0);
free (res->p_sys);
return VLC_EGENERIC;
}
xcb_shm_seg_t segment;
if (conn != NULL)
if (attach)
{
/* Attach the segment to X */
xcb_void_cookie_t ck;
......@@ -202,9 +221,9 @@ int PictureAlloc (vout_thread_t *vout, picture_t *pic, size_t size,
segment = xcb_generate_id (conn);
ck = xcb_shm_attach_checked (conn, segment, id, 1);
if (CheckError (vout, "shared memory server-side error", ck))
if (CheckError (vd, conn, "shared memory server-side error", ck))
{
msg_Info (vout, "using buggy X11 server - SSH proxying?");
msg_Info (vd, "using buggy X11 server - SSH proxying?");
segment = 0;
}
}
......@@ -212,67 +231,23 @@ int PictureAlloc (vout_thread_t *vout, picture_t *pic, size_t size,
segment = 0;
shmctl (id, IPC_RMID, 0);
pic->p_sys = (void *)(uintptr_t)segment;
pic->p->p_pixels = shm;
pic->i_status = DESTROYED_PICTURE;
pic->i_type = DIRECT_PICTURE;
res->p_sys->segment = segment;
res->p->p_pixels = shm;
return VLC_SUCCESS;
}
/**
* Release picture private data: detach the shared memory segment.
*/
void PictureFree (picture_t *pic, xcb_connection_t *conn)
void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
{
xcb_shm_seg_t segment = (uintptr_t)pic->p_sys;
xcb_shm_seg_t segment = res->p_sys->segment;
if (segment != 0)
{
assert (conn != NULL);
xcb_shm_detach (conn, segment);
}
shmdt (pic->p->p_pixels);
shmdt (res->p->p_pixels);
}
/**
* Video output thread management stuff.
* FIXME: Much of this should move to core
*/
void CommonManage (vout_thread_t *vout)
{
if (vout->i_changes & VOUT_SCALE_CHANGE)
{
vout->b_autoscale = var_GetBool (vout, "autoscale");
vout->i_zoom = ZOOM_FP_FACTOR;
vout->i_changes &= ~VOUT_SCALE_CHANGE;
vout->i_changes |= VOUT_SIZE_CHANGE;
}
if (vout->i_changes & VOUT_ZOOM_CHANGE)
{
vout->b_autoscale = false;
vout->i_zoom = var_GetFloat (vout, "scale") * ZOOM_FP_FACTOR;
vout->i_changes &= ~VOUT_ZOOM_CHANGE;
vout->i_changes |= VOUT_SIZE_CHANGE;
}
if (vout->i_changes & VOUT_CROP_CHANGE)
{
vout->fmt_out.i_x_offset = vout->fmt_in.i_x_offset;
vout->fmt_out.i_y_offset = vout->fmt_in.i_y_offset;
vout->fmt_out.i_visible_width = vout->fmt_in.i_visible_width;
vout->fmt_out.i_visible_height = vout->fmt_in.i_visible_height;
vout->i_changes &= ~VOUT_CROP_CHANGE;
vout->i_changes |= VOUT_SIZE_CHANGE;
}
if (vout->i_changes & VOUT_ASPECT_CHANGE)
{
vout->fmt_out.i_aspect = vout->fmt_in.i_aspect;
vout->fmt_out.i_sar_num = vout->fmt_in.i_sar_num;
vout->fmt_out.i_sar_den = vout->fmt_in.i_sar_den;
vout->output.i_aspect = vout->fmt_in.i_aspect;
vout->i_changes &= ~VOUT_ASPECT_CHANGE;
vout->i_changes |= VOUT_SIZE_CHANGE;
}
}
......@@ -30,7 +30,7 @@
#include <xcb/xcb.h>
#include <vlc_common.h>
#include <vlc_vout.h>
#include <vlc_vout_display.h>
#include "xcb_vlc.h"
......@@ -39,76 +39,67 @@
* Otherwise, we'd var_OrInteger() and var_NandInteger() functions...
*/
static void HandleButtonPress (vout_thread_t *vout,
/* FIXME we assume direct mapping between XCB and VLC */
static void HandleButtonPress (vout_display_t *vd,
xcb_button_press_event_t *ev)
{
unsigned buttons = var_GetInteger (vout, "mouse-button-down");
buttons |= (1 << (ev->detail - 1));
var_SetInteger (vout, "mouse-button-down", buttons);
vout_display_SendEventMousePressed (vd, ev->detail - 1);
}
static void HandleButtonRelease (vout_thread_t *vout,
static void HandleButtonRelease (vout_display_t *vd,
xcb_button_release_event_t *ev)
{
unsigned buttons = var_GetInteger (vout, "mouse-button-down");
buttons &= ~(1 << (ev->detail - 1));
var_SetInteger (vout, "mouse-button-down", buttons);
switch (ev->detail)
{
case 1: /* left mouse button */
var_SetBool (vout, "mouse-clicked", true);
var_SetBool (vout->p_libvlc, "intf-popupmenu", false);
break;
case 3:
var_SetBool (vout->p_libvlc, "intf-popupmenu", true);
break;
}
vout_display_SendEventMouseReleased (vd, ev->detail - 1);
}
static void HandleMotionNotify (vout_thread_t *vout,
static void HandleMotionNotify (vout_display_t *vd,
xcb_motion_notify_event_t *ev)
{
unsigned x, y, width, height;
int v;
vout_PlacePicture (vout, vout->output.i_width, vout->output.i_height,
&x, &y, &width, &height);
v = vout->fmt_in.i_x_offset
+ ((ev->event_x - x) * vout->fmt_in.i_visible_width / width);
if (v < 0)
v = 0; /* to the left of the picture */
else if ((unsigned)v > vout->fmt_in.i_width)
v = vout->fmt_in.i_width; /* to the right of the picture */
var_SetInteger (vout, "mouse-x", v);
v = vout->fmt_in.i_y_offset
+ ((ev->event_y - y) * vout->fmt_in.i_visible_height / height);
if (v < 0)
v = 0; /* above the picture */
else if ((unsigned)v > vout->fmt_in.i_height)
v = vout->fmt_in.i_height; /* below the picture */
var_SetInteger (vout, "mouse-y", v);
vout_display_place_t place;
/* TODO it could be saved */
vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
if (place.width <= 0 || place.height <= 0)
return;
const int x = vd->source.i_x_offset +
(int64_t)(ev->event_x -0*place.x) * vd->source.i_visible_width / place.width;
const int y = vd->source.i_y_offset +
(int64_t)(ev->event_y -0*place.y) * vd->source.i_visible_height/ place.height;
/* TODO show the cursor ? */
if (x >= vd->source.i_x_offset && x < vd->source.i_x_offset + vd->source.i_visible_width &&
y >= vd->source.i_y_offset && y < vd->source.i_y_offset + vd->source.i_visible_height)
vout_display_SendEventMouseMoved (vd, x, y);
}
static void
HandleParentStructure (vout_display_t *vd, xcb_configure_notify_event_t *ev)
{
if (ev->width != vd->cfg->display.width ||
ev->height != vd->cfg->display.height)
vout_display_SendEventDisplaySize (vd, ev->width, ev->height);
}
/**
* Process an X11 event.
*/
int ProcessEvent (vout_thread_t *vout, xcb_connection_t *conn,
xcb_window_t window, xcb_generic_event_t *ev)
static int ProcessEvent (vout_display_t *vd,
xcb_window_t window, xcb_generic_event_t *ev)
{
switch (ev->response_type & 0x7f)
{
case XCB_BUTTON_PRESS:
HandleButtonPress (vout, (xcb_button_press_event_t *)ev);
HandleButtonPress (vd, (xcb_button_press_event_t *)ev);
break;
case XCB_BUTTON_RELEASE:
HandleButtonRelease (vout, (xcb_button_release_event_t *)ev);
HandleButtonRelease (vd, (xcb_button_release_event_t *)ev);
break;
case XCB_MOTION_NOTIFY:
HandleMotionNotify (vout, (xcb_motion_notify_event_t *)ev);
HandleMotionNotify (vd, (xcb_motion_notify_event_t *)ev);
break;
case XCB_CONFIGURE_NOTIFY:
......@@ -117,14 +108,41 @@ int ProcessEvent (vout_thread_t *vout, xcb_connection_t *conn,
(xcb_configure_notify_event_t *)ev;
assert (cn->window != window);
HandleParentStructure (vout, conn, window, cn);
HandleParentStructure (vd, cn);
break;
}
/* FIXME I am not sure it is the right one */
case XCB_DESTROY_NOTIFY:
vout_display_SendEventClose (vd);
break;
default:
msg_Dbg (vout, "unhandled event %"PRIu8, ev->response_type);
msg_Dbg (vd, "unhandled event %"PRIu8, ev->response_type);
}
free (ev);
return VLC_SUCCESS;
}
/**
* Process incoming X events.
*/
int ManageEvent (vout_display_t *vd, xcb_connection_t *conn, xcb_window_t window)
{
xcb_generic_event_t *ev;
while ((ev = xcb_poll_for_event (conn)) != NULL)
ProcessEvent (vd, window, ev);
if (xcb_connection_has_error (conn))
{
msg_Err (vd, "X server failure");
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
......@@ -32,8 +32,8 @@
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout.h>
#include <vlc_vout_window.h>
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>
#include "xcb_vlc.h"
......@@ -57,7 +57,7 @@ vlc_module_begin ()
set_description (N_("(Experimental) XCB video output"))
set_category (CAT_VIDEO)
set_subcategory (SUBCAT_VIDEO_VOUT)
set_capability ("video output", 0)
set_capability ("vout display", 0)
set_callbacks (Open, Close)
add_string ("x11-display", NULL, NULL,
......@@ -65,7 +65,13 @@ vlc_module_begin ()
add_bool ("x11-shm", true, NULL, SHM_TEXT, SHM_LONGTEXT, true)
vlc_module_end ()
struct vout_sys_t
/* It must be large enough to absorb the server display jitter but it is
* useless to used a too large value, direct rendering cannot be used with
* xcb x11
*/
#define MAX_PICTURES (3)
struct vout_display_sys_t
{
xcb_connection_t *conn;
vout_window_t *embed; /* VLC window (when windowed) */
......@@ -77,40 +83,29 @@ struct vout_sys_t
uint8_t pad; /* scanline pad */
uint8_t depth; /* useful bits per pixel */
uint8_t byte_order; /* server byte order */
};
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)
{
xcb_generic_error_t *err;
picture_pool_t *pool; /* picture pool */
picture_resource_t resource[MAX_PICTURES];
};
err = xcb_request_check (vout->p_sys->conn, ck);
if (err)
{
msg_Err (vout, "%s: X11 error %d", str, err->error_code);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static picture_t *Get (vout_display_t *);
static void Display (vout_display_t *, picture_t *);
static int Control (vout_display_t *, int, va_list);
static void Manage (vout_display_t *);
#define p_vout vout
static void ResetPictures (vout_display_t *);
/**
* Probe the X server.
*/
static int Open (vlc_object_t *obj)
{
vout_thread_t *vout = (vout_thread_t *)obj;
vout_sys_t *p_sys = malloc (sizeof (*p_sys));
vout_display_t *vd = (vout_display_t *)obj;
vout_display_sys_t *p_sys = malloc (sizeof (*p_sys));
if (p_sys == NULL)
return VLC_ENOMEM;
vout->p_sys = p_sys;
vd->sys = p_sys;
/* Connect to X */
p_sys->conn = Connect (obj);
......@@ -122,7 +117,7 @@ static int Open (vlc_object_t *obj)
/* Get window */
const xcb_screen_t *scr;
p_sys->embed = GetWindow (vout, p_sys->conn, &scr, &p_sys->shm);
p_sys->embed = GetWindow (vd, p_sys->conn, &scr, &p_sys->shm);
if (p_sys->embed == NULL)
{
xcb_disconnect (p_sys->conn);
......@@ -133,6 +128,9 @@ static int Open (vlc_object_t *obj)
const xcb_setup_t *setup = xcb_get_setup (p_sys->conn);
p_sys->byte_order = setup->image_byte_order;
/* */
video_format_t fmt_pic = vd->fmt;
/* Determine our video format. Normally, this is done in pf_init(), but
* this plugin always uses the same format for a given X11 screen. */
xcb_visualid_t vid = 0;
......@@ -213,12 +211,12 @@ static int Open (vlc_object_t *obj)
if (!vid)
continue; /* The screen does not *really* support this depth */
vout->fmt_out.i_chroma = vout->output.i_chroma = chroma;
fmt_pic.i_chroma = chroma;
if (!gray)
{
vout->fmt_out.i_rmask = vout->output.i_rmask = vt->red_mask;
vout->fmt_out.i_gmask = vout->output.i_gmask = vt->green_mask;
vout->fmt_out.i_bmask = vout->output.i_bmask = vt->blue_mask;
fmt_pic.i_rmask = vt->red_mask;
fmt_pic.i_gmask = vt->green_mask;
fmt_pic.i_bmask = vt->blue_mask;
}
p_sys->bpp = fmt->bits_per_pixel;
p_sys->pad = fmt->scanline_pad;
......@@ -227,12 +225,12 @@ static int Open (vlc_object_t *obj)
if (depth == 0)
{
msg_Err (vout, "no supported pixmap formats or visual types");
msg_Err (vd, "no supported pixmap formats or visual types");
goto error;
}
msg_Dbg (vout, "using X11 visual ID 0x%"PRIx32, vid);
msg_Dbg (vout, " %"PRIu8" bits per pixels, %"PRIu8" bits line pad",
msg_Dbg (vd, "using X11 visual ID 0x%"PRIx32, vid);
msg_Dbg (vd, " %"PRIu8" bits per pixels, %"PRIu8" bits line pad",
p_sys->bpp, p_sys->pad);
/* Create colormap (needed to select non-default visual) */
......@@ -263,23 +261,41 @@ static int Open (vlc_object_t *obj)
p_sys->embed->handle.xid, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
vid, mask, values);
if (CheckError (vout, "cannot create X11 window", c))
if (CheckError (vd, p_sys->conn, "cannot create X11 window", c))
goto error;
p_sys->window = window;
msg_Dbg (vout, "using X11 window %08"PRIx32, p_sys->window);
msg_Dbg (vd, "using X11 window %08"PRIx32, p_sys->window);
xcb_map_window (p_sys->conn, window);
}
/* Create graphic context (I wonder why the heck do we need this) */
p_sys->gc = xcb_generate_id (p_sys->conn);
xcb_create_gc (p_sys->conn, p_sys->gc, p_sys->window, 0, NULL);
msg_Dbg (vout, "using X11 graphic context %08"PRIx32, p_sys->gc);
msg_Dbg (vd, "using X11 graphic context %08"PRIx32, p_sys->gc);
/* */
p_sys->pool = NULL;
/* */
vout_display_info_t info = vd->info;
info.has_pictures_invalid = true;
/* Setup vout_display_t once everything is fine */
vd->fmt = fmt_pic;
vd->info = info;
vd->get = Get;
vd->prepare = NULL;
vd->display = Display;
vd->control = Control;
vd->manage = Manage;
/* */
unsigned width, height;
if (!GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
vout_display_SendEventDisplaySize (vd, width, height);
vout_display_SendEventFullscreen (vd, false);
vout->pf_init = Init;
vout->pf_end = Deinit;
vout->pf_display = Display;
vout->pf_manage = Manage;
vout->pf_control = Control;
return VLC_SUCCESS;
error:
......@@ -293,174 +309,219 @@ error:
*/
static void Close (vlc_object_t *obj)
{
vout_thread_t *vout = (vout_thread_t *)obj;
vout_sys_t *p_sys = vout->p_sys;
vout_display_t *vd = (vout_display_t *)obj;
vout_display_sys_t *p_sys = vd->sys;
vout_window_Delete (p_sys->embed);
ResetPictures (vd);
vout_display_DeleteWindow (vd, p_sys->embed);
/* colormap and window are garbage-collected by X */
xcb_disconnect (p_sys->conn);
free (p_sys);
}
/**
* Allocate drawable window and picture buffers.
* Return a direct buffer
*/
static int Init (vout_thread_t *vout)
static picture_t *Get (vout_display_t *vd)
{
vout_sys_t *p_sys = vout->p_sys;
unsigned x, y, width, height;
vout_display_sys_t *p_sys = vd->sys;
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);
/* FIXME: I don't get the subtlety between output and fmt_out here */
vout->fmt_out.i_visible_width = width;
vout->fmt_out.i_visible_height = height;
vout->fmt_out.i_sar_num = vout->fmt_out.i_sar_den = 1;
vout->output.i_width = vout->fmt_out.i_width =
width * vout->fmt_in.i_width / vout->fmt_in.i_visible_width;
vout->output.i_height = vout->fmt_out.i_height =
height * vout->fmt_in.i_height / vout->fmt_in.i_visible_height;
vout->fmt_out.i_x_offset =
width * vout->fmt_in.i_x_offset / vout->fmt_in.i_visible_width;
p_vout->fmt_out.i_y_offset =
height * vout->fmt_in.i_y_offset / vout->fmt_in.i_visible_height;
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++)
if (!p_sys->pool)
{
picture_t *pic = vout->p_picture + index;
vout_display_place_t place;
if (index > sizeof (vout->p_picture) / sizeof (pic))
break;
if (pic->i_status != FREE_PICTURE)
continue;
vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
picture_Setup (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;
/* */
const uint32_t values[] = { place.x, place.y, place.width, place.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);
picture_t *pic = picture_NewFromFormat (&vd->fmt);
if (!pic)
return NULL;
assert (pic->i_planes == 1);
memset (p_sys->resource, 0, sizeof(p_sys->resource));
unsigned count;
picture_t *pic_array[MAX_PICTURES];
for (count = 0; count < MAX_PICTURES; count++)
{
picture_resource_t *res = &p_sys->resource[count];
res->p->i_lines = pic->p->i_lines;
res->p->i_pitch = pic->p->i_pitch;
if (PictureResourceAlloc (vd, res, res->p->i_pitch * res->p->i_lines,
p_sys->conn, p_sys->shm))
break;
pic_array[count] = picture_NewFromResource (&vd->fmt, res);
if (!pic_array[count])
{
PictureResourceFree (res, p_sys->conn);
memset (res, 0, sizeof(*res));
break;
}
}
picture_Release (pic);
if (count == 0)
return NULL;
p_sys->pool = picture_pool_New (count, pic_array);
if (!p_sys->pool)
{
/* TODO release picture resources */
return NULL;
}
/* FIXME should also do it in case of error ? */
xcb_flush (p_sys->conn);
}
xcb_flush (p_sys->conn);
return VLC_SUCCESS;
}
/**
* Free picture buffers.
*/
static void Deinit (vout_thread_t *vout)
{
for (int i = 0; i < I_OUTPUTPICTURES; i++)
PictureFree (PP_OUTPUTPICTURE[i], vout->p_sys->conn);
return picture_pool_Get (p_sys->pool);
}
/**
* Sends an image to the X server.
*/
static void Display (vout_thread_t *vout, picture_t *pic)
static void Display (vout_display_t *vd, picture_t *pic)
{
vout_sys_t *p_sys = vout->p_sys;
xcb_shm_seg_t segment = (uintptr_t)pic->p_sys;
vout_display_sys_t *p_sys = vd->sys;
xcb_shm_seg_t segment = pic->p_sys->segment;
if (segment != 0)
xcb_shm_put_image (p_sys->conn, p_sys->window, p_sys->gc,
/* real width */ pic->p->i_pitch / pic->p->i_pixel_pitch,
/* real height */ pic->p->i_lines,
/* x */ vout->fmt_out.i_x_offset,
/* y */ vout->fmt_out.i_y_offset,
/* width */ vout->fmt_out.i_visible_width,
/* height */ vout->fmt_out.i_visible_height,
/* x */ vd->fmt.i_x_offset,
/* y */ vd->fmt.i_y_offset,
/* width */ vd->fmt.i_visible_width,
/* height */ vd->fmt.i_visible_height,
0, 0, p_sys->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
0, segment, 0);
else
{
const size_t offset = vout->fmt_out.i_y_offset * pic->p->i_pitch;
const unsigned lines = pic->p->i_lines - vout->fmt_out.i_y_offset;
const size_t offset = vd->fmt.i_y_offset * pic->p->i_pitch;
const unsigned lines = pic->p->i_lines - vd->fmt.i_y_offset;
xcb_put_image (p_sys->conn, XCB_IMAGE_FORMAT_Z_PIXMAP,
p_sys->window, p_sys->gc,
pic->p->i_pitch / pic->p->i_pixel_pitch,
lines, -vout->fmt_out.i_x_offset, 0, 0, p_sys->depth,
lines, -vd->fmt.i_x_offset, 0, 0, p_sys->depth,
pic->p->i_pitch * lines, pic->p->p_pixels + offset);
}
xcb_flush (p_sys->conn);
/* FIXME might be WAY better to wait in some case (be carefull with
* VOUT_DISPLAY_RESET_PICTURES if done) + does not work with
* vout_display wrapper. */
picture_Release (pic);
}
/**
* Process incoming X events.
*/
static int Manage (vout_thread_t *vout)
static int Control (vout_display_t *vd, int query, va_list ap)
{
vout_sys_t *p_sys = vout->p_sys;
xcb_generic_event_t *ev;
while ((ev = xcb_poll_for_event (p_sys->conn)) != NULL)
ProcessEvent (vout, p_sys->conn, p_sys->window, ev);
vout_display_sys_t *p_sys = vd->sys;
if (xcb_connection_has_error (p_sys->conn))
switch (query)
{
msg_Err (vout, "X server failure");
return VLC_EGENERIC;
}
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
{
const vout_display_cfg_t *p_cfg =
(const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
CommonManage (vout); /* FIXME: <-- move that to core */
return VLC_SUCCESS;
}
if (vout_window_SetSize (p_sys->embed,
p_cfg->display.width,
p_cfg->display.height))
return VLC_EGENERIC;
void
HandleParentStructure (vout_thread_t *vout, xcb_connection_t *conn,
xcb_window_t xid, xcb_configure_notify_event_t *ev)
{
unsigned width, height, x, y;
vout_display_place_t place;
vout_display_PlacePicture (&place, &vd->source, p_cfg, false);
vout_PlacePicture (vout, ev->width, ev->height, &x, &y, &width, &height);
if (width != vout->fmt_out.i_visible_width
|| height != vout->fmt_out.i_visible_height)
if (place.width != vd->fmt.i_visible_width ||
place.height != vd->fmt.i_visible_height)
{
vout_display_SendEventPicturesInvalid (vd);
return VLC_SUCCESS;
}
/* Move the picture within the window */
const uint32_t values[] = { place.x, place.y };
xcb_configure_window (p_sys->conn, p_sys->window,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
values);
return VLC_SUCCESS;
}
case VOUT_DISPLAY_CHANGE_ON_TOP:
{
vout->i_changes |= VOUT_SIZE_CHANGE;
return; /* vout will be reinitialized */
int b_on_top = (int)va_arg (ap, int);
return vout_window_SetOnTop (p_sys->embed, b_on_top);
}
/* Move the picture within the window */
const uint32_t values[] = { x, y, };
xcb_configure_window (conn, xid,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
values);
}
case VOUT_DISPLAY_CHANGE_ZOOM:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
/* I am not sure it is always necessary, but it is way simpler ... */
vout_display_SendEventPicturesInvalid (vd);
return VLC_SUCCESS;
static int Control (vout_thread_t *vout, int query, va_list ap)
{
switch (query)
case VOUT_DISPLAY_RESET_PICTURES:
{
case VOUT_SET_SIZE:
{
const unsigned width = va_arg (ap, unsigned);
const unsigned height = va_arg (ap, unsigned);
return vout_window_SetSize (vout->p_sys->embed, width, height);
}
case VOUT_SET_STAY_ON_TOP:
{
const bool is_on_top = va_arg (ap, int);
return vout_window_SetOnTop (vout->p_sys->embed, is_on_top);
ResetPictures (vd);
vout_display_place_t place;
vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
vd->fmt.i_width = vd->source.i_width * place.width / vd->source.i_visible_width;
vd->fmt.i_height = vd->source.i_height * place.height / vd->source.i_visible_height;
vd->fmt.i_visible_width = place.width;
vd->fmt.i_visible_height = place.height;
vd->fmt.i_x_offset = vd->source.i_x_offset * place.width / vd->source.i_visible_width;
vd->fmt.i_y_offset = vd->source.i_y_offset * place.height / vd->source.i_visible_height;
return VLC_SUCCESS;
}
/* TODO */
#if 0
/* Hide the mouse. It will be send when
* vout_display_t::info.b_hide_mouse is false */
VOUT_DISPLAY_HIDE_MOUSE,
/* Ask the module to acknowledge/refuse the fullscreen state change after
* being requested (externaly or by VOUT_DISPLAY_EVENT_FULLSCREEN */
VOUT_DISPLAY_CHANGE_FULLSCREEN, /* const vout_display_cfg_t *p_cfg */
#endif
default:
msg_Err (vd, "Unknown request in XCB vout display");
return VLC_EGENERIC;
}
}
static void Manage (vout_display_t *vd)
{
vout_display_sys_t *p_sys = vd->sys;
ManageEvent (vd, p_sys->conn, p_sys->window);
}
static void ResetPictures (vout_display_t *vd)
{
vout_display_sys_t *p_sys = vd->sys;
if (!p_sys->pool)
return;
for (unsigned i = 0; i < MAX_PICTURES; i++)
{
picture_resource_t *res = &p_sys->resource[i];
if (!res->p->p_pixels)
break;
PictureResourceFree (res, p_sys->conn);
}
picture_pool_Delete (p_sys->pool);
p_sys->pool = NULL;
}
......@@ -26,12 +26,10 @@
# define ORDER XCB_IMAGE_ORDER_LSB_FIRST
#endif
int CheckError (vout_thread_t *, const char *str, xcb_void_cookie_t);
int ProcessEvent (vout_thread_t *, xcb_connection_t *, xcb_window_t,
xcb_generic_event_t *);
void HandleParentStructure (vout_thread_t *vout, xcb_connection_t *conn,
xcb_window_t xid, xcb_configure_notify_event_t *ev);
#include <vlc_picture.h>
#include <vlc_vout_display.h>
int ManageEvent (vout_display_t *vd, xcb_connection_t *conn, xcb_window_t window);
/* keys.c */
typedef struct key_handler_t key_handler_t;
......@@ -40,15 +38,25 @@ void DestroyKeyHandler (key_handler_t *);
int ProcessKeyEvent (key_handler_t *, xcb_generic_event_t *);
/* common.c */
struct vout_window_t;
xcb_connection_t *Connect (vlc_object_t *obj);
struct vout_window_t *GetWindow (vout_thread_t *obj,
struct vout_window_t *GetWindow (vout_display_t *obj,
xcb_connection_t *pconn,
const xcb_screen_t **restrict pscreen,
bool *restrict pshm);
int GetWindowSize (struct vout_window_t *wnd, xcb_connection_t *conn,
unsigned *restrict width, unsigned *restrict height);
int PictureAlloc (vout_thread_t *, picture_t *, size_t, xcb_connection_t *);
void PictureFree (picture_t *pic, xcb_connection_t *conn);
void CommonManage (vout_thread_t *);
int CheckError (vout_display_t *, xcb_connection_t *conn,
const char *str, xcb_void_cookie_t);
/* FIXME
* maybe it would be better to split this header in 2 */
#include <xcb/shm.h>
struct picture_sys_t
{
xcb_shm_seg_t segment;
};
int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
xcb_connection_t *conn, bool attach);
void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn);
......@@ -33,8 +33,8 @@
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout.h>
#include <vlc_vout_window.h>
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>
#include "xcb_vlc.h"
......@@ -58,7 +58,7 @@ vlc_module_begin ()
set_description (N_("(Experimental) XVideo output"))
set_category (CAT_VIDEO)
set_subcategory (SUBCAT_VIDEO_VOUT)
set_capability ("video output", 0)
set_capability ("vout display", 0)
set_callbacks (Open, Close)
add_string ("x11-display", NULL, NULL,
......@@ -67,7 +67,9 @@ vlc_module_begin ()
add_shortcut ("xcb-xv")
vlc_module_end ()
struct vout_sys_t
#define MAX_PICTURES (VOUT_MAX_PICTURES)
struct vout_display_sys_t
{
xcb_connection_t *conn;
xcb_xv_query_adaptors_reply_t *adaptors;
......@@ -81,31 +83,21 @@ struct vout_sys_t
uint16_t height; /* display height */
uint32_t data_size; /* picture byte size (for non-SHM) */
bool shm; /* whether to use MIT-SHM */
};
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)
{
xcb_generic_error_t *err;
xcb_xv_query_image_attributes_reply_t *att;
picture_pool_t *pool; /* picture pool */
picture_resource_t resource[MAX_PICTURES];
};
err = xcb_request_check (vout->p_sys->conn, ck);
if (err)
{
msg_Err (vout, "%s: X11 error %d", str, err->error_code);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static picture_t *Get (vout_display_t *);
static void Display (vout_display_t *, picture_t *);
static int Control (vout_display_t *, int, va_list);
static void Manage (vout_display_t *);
/**
* Check that the X server supports the XVideo extension.
*/
static bool CheckXVideo (vout_thread_t *vout, xcb_connection_t *conn)
static bool CheckXVideo (vout_display_t *vd, xcb_connection_t *conn)
{
xcb_xv_query_extension_reply_t *r;
xcb_xv_query_extension_cookie_t ck = xcb_xv_query_extension (conn);
......@@ -116,129 +108,21 @@ static bool CheckXVideo (vout_thread_t *vout, xcb_connection_t *conn)
{ /* We need XVideo 2.2 for PutImage */
if ((r->major > 2) || (r->major == 2 && r->minor >= 2))
{
msg_Dbg (vout, "using XVideo extension v%"PRIu8".%"PRIu8,
msg_Dbg (vd, "using XVideo extension v%"PRIu8".%"PRIu8,
r->major, r->minor);
ok = true;
}
else
msg_Dbg (vout, "XVideo extension too old (v%"PRIu8".%"PRIu8,
msg_Dbg (vd, "XVideo extension too old (v%"PRIu8".%"PRIu8,
r->major, r->minor);
free (r);
}
else
msg_Dbg (vout, "XVideo extension not available");
msg_Dbg (vd, "XVideo extension not available");
return ok;
}
/**
* Get a list of XVideo adaptors for a given window.
*/
static xcb_xv_query_adaptors_reply_t *GetAdaptors (vout_window_t *wnd,
xcb_connection_t *conn)
{
xcb_xv_query_adaptors_cookie_t ck;
ck = xcb_xv_query_adaptors (conn, wnd->handle.xid);
return xcb_xv_query_adaptors_reply (conn, ck, NULL);
}
#define p_vout vout
/**
* Probe the X server.
*/
static int Open (vlc_object_t *obj)
{
vout_thread_t *vout = (vout_thread_t *)obj;
vout_sys_t *p_sys = malloc (sizeof (*p_sys));
if (p_sys == NULL)
return VLC_ENOMEM;
vout->p_sys = p_sys;
/* Connect to X */
p_sys->conn = Connect (obj);
if (p_sys->conn == NULL)
{
free (p_sys);
return VLC_EGENERIC;
}
if (!CheckXVideo (vout, p_sys->conn))
{
msg_Warn (vout, "Please enable XVideo 2.2 for faster video display");
xcb_disconnect (p_sys->conn);
free (p_sys);
return VLC_EGENERIC;
}
const xcb_screen_t *screen;
p_sys->embed = GetWindow (vout, p_sys->conn, &screen, &p_sys->shm);
if (p_sys->embed == NULL)
{
xcb_disconnect (p_sys->conn);
free (p_sys);
return VLC_EGENERIC;
}
/* Cache adaptors infos */
p_sys->adaptors = GetAdaptors (p_sys->embed, p_sys->conn);
if (p_sys->adaptors == NULL)
goto error;
/* Create window */
{
const uint32_t mask =
/* XCB_CW_EVENT_MASK */
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION;
xcb_void_cookie_t c;
xcb_window_t window = xcb_generate_id (p_sys->conn);
c = xcb_create_window_checked (p_sys->conn, screen->root_depth, window,
p_sys->embed->handle.xid, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
XCB_CW_EVENT_MASK, &mask);
if (CheckError (vout, "cannot create X11 window", c))
goto error;
p_sys->window = window;
msg_Dbg (vout, "using X11 window %08"PRIx32, p_sys->window);
xcb_map_window (p_sys->conn, window);
}
p_sys->gc = xcb_generate_id (p_sys->conn);
xcb_create_gc (p_sys->conn, p_sys->gc, p_sys->window, 0, NULL);
msg_Dbg (vout, "using X11 graphic context %08"PRIx32, p_sys->gc);
vout->pf_init = Init;
vout->pf_end = Deinit;
vout->pf_display = Display;
vout->pf_manage = Manage;
vout->pf_control = Control;
return VLC_SUCCESS;
error:
Close (obj);
return VLC_EGENERIC;
}
/**
* Disconnect from the X server.
*/
static void Close (vlc_object_t *obj)
{
vout_thread_t *vout = (vout_thread_t *)obj;
vout_sys_t *p_sys = vout->p_sys;
free (p_sys->adaptors);
vout_window_Delete (p_sys->embed);
xcb_disconnect (p_sys->conn);
free (p_sys);
}
static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
static vlc_fourcc_t ParseFormat (vout_display_t *vd,
const xcb_xv_image_format_info_t *restrict f)
{
if (f->byte_order != ORDER && f->bpp != 8)
......@@ -273,9 +157,9 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
}
break;
}
msg_Err (vout, "unknown XVideo RGB format %"PRIx32" (%.4s)",
msg_Err (vd, "unknown XVideo RGB format %"PRIx32" (%.4s)",
f->id, f->guid);
msg_Dbg (vout, " %"PRIu8" planes, %"PRIu8" bits/pixel, "
msg_Dbg (vd, " %"PRIu8" planes, %"PRIu8" bits/pixel, "
"depth %"PRIu8, f->num_planes, f->bpp, f->depth);
break;
......@@ -321,16 +205,16 @@ static vlc_fourcc_t ParseFormat (vout_thread_t *vout,
break;
}
bad:
msg_Err (vout, "unknown XVideo YUV format %"PRIx32" (%.4s)", f->id,
msg_Err (vd, "unknown XVideo YUV format %"PRIx32" (%.4s)", f->id,
f->guid);
msg_Dbg (vout, " %"PRIu8" planes, %"PRIu32" bits/pixel, "
msg_Dbg (vd, " %"PRIu8" planes, %"PRIu32" bits/pixel, "
"%"PRIu32"/%"PRIu32"/%"PRIu32" bits/sample", f->num_planes,
f->bpp, f->y_sample_bits, f->u_sample_bits, f->v_sample_bits);
msg_Dbg (vout, " period: %"PRIu32"/%"PRIu32"/%"PRIu32"x"
msg_Dbg (vd, " period: %"PRIu32"/%"PRIu32"/%"PRIu32"x"
"%"PRIu32"/%"PRIu32"/%"PRIu32,
f->vhorz_y_period, f->vhorz_u_period, f->vhorz_v_period,
f->vvert_y_period, f->vvert_u_period, f->vvert_v_period);
msg_Warn (vout, " order: %.32s", f->vcomp_order);
msg_Warn (vd, " order: %.32s", f->vcomp_order);
break;
}
return 0;
......@@ -338,11 +222,13 @@ 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,
FindFormat (vout_display_t *vd,
vlc_fourcc_t chroma, const video_format_t *fmt,
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;
xcb_connection_t *conn = vd->sys->conn;
const xcb_xv_image_format_info_t *f, *end;
#ifndef XCB_XV_OLD
......@@ -353,21 +239,21 @@ FindFormat (vout_thread_t *vout, vlc_fourcc_t chroma, xcb_xv_port_t port,
end = f + xcb_xv_list_image_formats_format_length (list);
for (; f < end; f++)
{
if (chroma != ParseFormat (vout, f))
if (chroma != ParseFormat (vd, 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);
fmt->i_width, fmt->i_height), NULL);
if (i == NULL)
continue;
if (i->width != vout->fmt_in.i_width
|| i->height != vout->fmt_in.i_height)
if (i->width != fmt->i_width
|| i->height != fmt->i_height)
{
msg_Warn (vout, "incompatible size %ux%u -> %"PRIu32"x%"PRIu32,
vout->fmt_in.i_width, vout->fmt_in.i_height,
msg_Warn (vd, "incompatible size %ux%u -> %"PRIu32"x%"PRIu32,
fmt->i_width, fmt->i_height,
i->width, i->height);
free (i);
continue;
......@@ -378,14 +264,67 @@ FindFormat (vout_thread_t *vout, vlc_fourcc_t chroma, xcb_xv_port_t port,
return NULL;
}
/**
* Allocate drawable window and picture buffers.
* Get a list of XVideo adaptors for a given window.
*/
static int Init (vout_thread_t *vout)
static xcb_xv_query_adaptors_reply_t *GetAdaptors (vout_window_t *wnd,
xcb_connection_t *conn)
{
vout_sys_t *p_sys = vout->p_sys;
xcb_xv_query_image_attributes_reply_t *att = NULL;
bool swap_planes = false; /* whether X wants V before U */
xcb_xv_query_adaptors_cookie_t ck;
ck = xcb_xv_query_adaptors (conn, wnd->handle.xid);
return xcb_xv_query_adaptors_reply (conn, ck, NULL);
}
/**
* Probe the X server.
*/
static int Open (vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *)obj;
vout_display_sys_t *p_sys = malloc (sizeof (*p_sys));
if (p_sys == NULL)
return VLC_ENOMEM;
vd->sys = p_sys;
/* Connect to X */
p_sys->conn = Connect (obj);
if (p_sys->conn == NULL)
{
free (p_sys);
return VLC_EGENERIC;
}
if (!CheckXVideo (vd, p_sys->conn))
{
msg_Warn (vd, "Please enable XVideo 2.2 for faster video display");
xcb_disconnect (p_sys->conn);
free (p_sys);
return VLC_EGENERIC;
}
const xcb_screen_t *screen;
p_sys->embed = GetWindow (vd, p_sys->conn, &screen, &p_sys->shm);
if (p_sys->embed == NULL)
{
xcb_disconnect (p_sys->conn);
free (p_sys);
return VLC_EGENERIC;
}
/* Cache adaptors infos */
p_sys->adaptors = GetAdaptors (p_sys->embed, p_sys->conn);
if (p_sys->adaptors == NULL)
goto error;
/* */
video_format_t fmt = vd->fmt;
// TODO !
#if 1
p_sys->att = NULL;
bool found_adaptor = false;
/* FIXME: check max image size */
xcb_xv_adaptor_info_iterator_t it;
......@@ -405,11 +344,11 @@ static int Init (vout_thread_t *vout)
if (r == NULL)
continue;
const xcb_xv_image_format_info_t *fmt;
const xcb_xv_image_format_info_t *xfmt;
/* Video chroma in preference order */
const vlc_fourcc_t chromas[] = {
vout->fmt_in.i_chroma,
fmt.i_chroma,
VLC_CODEC_YUYV,
VLC_CODEC_RGB24,
VLC_CODEC_RGB15,
......@@ -417,10 +356,10 @@ static int Init (vout_thread_t *vout)
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)
xfmt = FindFormat (vd, chroma, &fmt, a->base_id, r, &p_sys->att);
if (xfmt != NULL)
{
vout->output.i_chroma = chroma;
fmt.i_chroma = chroma;
goto found_format;
}
}
......@@ -430,203 +369,306 @@ static int Init (vout_thread_t *vout)
found_format:
/* TODO: grab port */
p_sys->port = a->base_id;
msg_Dbg (vout, "using port %"PRIu32, p_sys->port);
msg_Dbg (vd, "using port %"PRIu32, p_sys->port);
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)
p_sys->id = xfmt->id;
msg_Dbg (vd, "using image format 0x%"PRIx32, p_sys->id);
if (xfmt->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;
fmt.i_rmask = xfmt->red_mask;
fmt.i_gmask = xfmt->green_mask;
fmt.i_bmask = xfmt->blue_mask;
}
else
if (fmt->num_planes == 3)
swap_planes = !strcmp ((const char *)fmt->vcomp_order, "YVU");
if (xfmt->num_planes == 3
&& !strcmp ((const char *)xfmt->vcomp_order, "YVU"))
fmt.i_chroma = VLC_CODEC_YV12;
free (r);
goto found_adaptor;
found_adaptor = true;
break;
}
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;
if (!found_adaptor)
{
msg_Err (vd, "no available XVideo adaptor");
goto error;
}
#endif
I_OUTPUTPICTURES = 0;
for (size_t index = 0; I_OUTPUTPICTURES < 2; index++)
/* Create window */
{
picture_t *pic = vout->p_picture + index;
const uint32_t mask =
/* XCB_CW_EVENT_MASK */
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION;
xcb_void_cookie_t c;
xcb_window_t window = xcb_generate_id (p_sys->conn);
if (index > sizeof (vout->p_picture) / sizeof (pic))
break;
if (pic->i_status != FREE_PICTURE)
continue;
c = xcb_create_window_checked (p_sys->conn, screen->root_depth, window,
p_sys->embed->handle.xid, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
XCB_CW_EVENT_MASK, &mask);
if (CheckError (vd, p_sys->conn, "cannot create X11 window", c))
goto error;
p_sys->window = window;
msg_Dbg (vd, "using X11 window %08"PRIx32, p_sys->window);
xcb_map_window (p_sys->conn, window);
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;
vout_display_place_t place;
vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
p_sys->width = place.width;
p_sys->height = place.height;
/* */
const uint32_t values[] = { place.x, place.y, place.width, place.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);
}
free (att);
unsigned x, y, width, height;
/* Create graphic context */
p_sys->gc = xcb_generate_id (p_sys->conn);
xcb_create_gc (p_sys->conn, p_sys->gc, p_sys->window, 0, NULL);
msg_Dbg (vd, "using X11 graphic context %08"PRIx32, p_sys->gc);
if (GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
return VLC_EGENERIC;
vout_PlacePicture (vout, width, height, &x, &y, &width, &height);
/* */
p_sys->pool = NULL;
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;
/* */
vout_display_info_t info = vd->info;
info.has_pictures_invalid = false;
vout->fmt_out.i_chroma = vout->output.i_chroma;
vout->fmt_out.i_visible_width = vout->fmt_in.i_visible_width;
vout->fmt_out.i_visible_height = vout->fmt_in.i_visible_height;
vout->fmt_out.i_sar_num = vout->fmt_out.i_sar_den = 1;
/* Setup vout_display_t once everything is fine */
vd->fmt = fmt;
vd->info = info;
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;
vout->fmt_out.i_y_offset = vout->fmt_in.i_y_offset;
vd->get = Get;
vd->prepare = NULL;
vd->display = Display;
vd->control = Control;
vd->manage = Manage;
assert (height > 0);
vout->output.i_aspect = vout->fmt_out.i_aspect =
width * VOUT_ASPECT_FACTOR / height;
/* */
unsigned width, height;
if (!GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
vout_display_SendEventDisplaySize (vd, width, height);
vout_display_SendEventFullscreen (vd, false);
return VLC_SUCCESS;
error:
Close (obj);
return VLC_EGENERIC;
}
/**
* Disconnect from the X server.
*/
static void Close (vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *)obj;
vout_display_sys_t *p_sys = vd->sys;
if (p_sys->pool)
{
for (unsigned i = 0; i < MAX_PICTURES; i++)
{
picture_resource_t *res = &p_sys->resource[i];
if (!res->p->p_pixels)
break;
PictureResourceFree (res, p_sys->conn);
}
picture_pool_Delete (p_sys->pool);
}
free (p_sys->att);
free (p_sys->adaptors);
vout_display_DeleteWindow (vd, p_sys->embed);
xcb_disconnect (p_sys->conn);
free (p_sys);
}
/**
* Free picture buffers.
* Return a direct buffer
*/
static void Deinit (vout_thread_t *vout)
static picture_t *Get (vout_display_t *vd)
{
vout_sys_t *p_sys = vout->p_sys;
vout_display_sys_t *p_sys = vd->sys;
if (!p_sys->pool)
{
picture_t *pic = picture_New (vd->fmt.i_chroma,
p_sys->att->width, p_sys->att->height, 0);
if (!pic)
return NULL;
memset (p_sys->resource, 0, sizeof(p_sys->resource));
const uint32_t *offsets =
xcb_xv_query_image_attributes_offsets (p_sys->att);
p_sys->data_size = p_sys->att->data_size;
unsigned count;
picture_t *pic_array[MAX_PICTURES];
for (count = 0; count < MAX_PICTURES; count++)
{
picture_resource_t *res = &p_sys->resource[count];
for (int i = 0; i < pic->i_planes; i++)
{
res->p[i].i_lines = pic->p[i].i_lines; /* FIXME seems wrong*/
res->p[i].i_pitch = pic->p[i].i_pitch;
}
if (PictureResourceAlloc (vd, res, p_sys->att->data_size,
p_sys->conn, p_sys->shm))
break;
/* Allocate further planes as specified by XVideo */
/* We assume that offsets[0] is zero */
for (int i = 1; i < pic->i_planes; i++)
res->p[i].p_pixels = res->p[0].p_pixels + offsets[i];
pic_array[count] = picture_NewFromResource (&vd->fmt, res);
if (!pic_array[count])
{
PictureResourceFree (res, p_sys->conn);
memset (res, 0, sizeof(*res));
break;
}
}
picture_Release (pic);
if (count == 0)
return NULL;
p_sys->pool = picture_pool_New (count, pic_array);
if (!p_sys->pool)
{
/* TODO release picture resources */
return NULL;
}
/* FIXME should also do it in case of error ? */
xcb_flush (p_sys->conn);
}
for (int i = 0; i < I_OUTPUTPICTURES; i++)
PictureFree (PP_OUTPUTPICTURE[i], p_sys->conn);
return picture_pool_Get (p_sys->pool);
}
/**
* Sends an image to the X server.
*/
static void Display (vout_thread_t *vout, picture_t *pic)
static void Display (vout_display_t *vd, picture_t *pic)
{
vout_sys_t *p_sys = vout->p_sys;
xcb_shm_seg_t segment = (uintptr_t)pic->p_sys;
vout_display_sys_t *p_sys = vd->sys;
xcb_shm_seg_t segment = pic->p_sys->segment;
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: */ 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,
/* Src: */ vd->source.i_x_offset,
vd->source.i_y_offset,
vd->source.i_visible_width,
vd->source.i_visible_height,
/* Dst: */ 0, 0, p_sys->width, p_sys->height,
/* Memory: */ pic->p->i_pitch / pic->p->i_pixel_pitch,
pic->p->i_lines, false);
else
xcb_xv_put_image (p_sys->conn, p_sys->port, p_sys->window,
p_sys->gc, p_sys->id,
vout->fmt_out.i_x_offset, vout->fmt_out.i_y_offset,
vout->fmt_out.i_visible_width,
vout->fmt_out.i_visible_height,
vd->source.i_x_offset,
vd->source.i_y_offset,
vd->source.i_visible_width,
vd->source.i_visible_height,
0, 0, p_sys->width, p_sys->height,
vout->fmt_out.i_width, vout->fmt_out.i_height,
vd->source.i_width, vd->source.i_height,
p_sys->data_size, pic->p->p_pixels);
xcb_flush (p_sys->conn);
picture_Release (pic);
}
/**
* Process incoming X events.
*/
static int Manage (vout_thread_t *vout)
static int Control (vout_display_t *vd, int query, va_list ap)
{
vout_sys_t *p_sys = vout->p_sys;
xcb_generic_event_t *ev;
vout_display_sys_t *p_sys = vd->sys;
while ((ev = xcb_poll_for_event (p_sys->conn)) != NULL)
ProcessEvent (vout, p_sys->conn, p_sys->window, ev);
if (xcb_connection_has_error (p_sys->conn))
switch (query)
{
msg_Err (vout, "X server failure");
return VLC_EGENERIC;
}
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_ZOOM:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
{
const vout_display_cfg_t *cfg;
const video_format_t *source;
CommonManage (vout);
if (vout->i_changes & VOUT_SIZE_CHANGE)
{ /* TODO: factor this code with XV and X11 Init() */
unsigned x, y, width, height;
if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
|| query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
{
source = (const video_format_t *)va_arg (ap, const video_format_t *);
cfg = vd->cfg;
}
else
{
source = &vd->source;
cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
}
if (GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
/* */
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
&& (cfg->display.width != vd->cfg->display.width
||cfg->display.height != vd->cfg->display.height)
&& vout_window_SetSize (p_sys->embed,
cfg->display.width,
cfg->display.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;
}
void
HandleParentStructure (vout_thread_t *vout, xcb_connection_t *conn,
xcb_window_t xid, xcb_configure_notify_event_t *ev)
{
unsigned width, height, x, y;
vout_PlacePicture (vout, ev->width, ev->height, &x, &y, &width, &height);
/* Move the picture within the window */
const uint32_t values[] = { x, y, width, height, };
xcb_configure_window (conn, xid,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
values);
vout->p_sys->width = width;
vout->p_sys->height = height;
}
static int Control (vout_thread_t *vout, int query, va_list ap)
{
/* FIXME it can be shared between x11 and xvideo */
switch (query)
{
case VOUT_SET_SIZE:
{
const unsigned width = va_arg (ap, unsigned);
const unsigned height = va_arg (ap, unsigned);
return vout_window_SetSize (vout->p_sys->embed, width, height);
vout_display_place_t place;
vout_display_PlacePicture (&place, source, cfg, false);
p_sys->width = place.width;
p_sys->height = place.height;
/* Move the picture within the window */
const uint32_t values[] = { place.x, place.y,
place.width, place.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);
return VLC_SUCCESS;
}
case VOUT_SET_STAY_ON_TOP:
case VOUT_DISPLAY_CHANGE_ON_TOP:
{
const bool is_on_top = va_arg (ap, int);
return vout_window_SetOnTop (vout->p_sys->embed, is_on_top);
int on_top = (int)va_arg (ap, int);
return vout_window_SetOnTop (p_sys->embed, on_top);
}
/* TODO */
#if 0
/* Hide the mouse. It will be send when
* vout_display_t::info.b_hide_mouse is false */
VOUT_DISPLAY_HIDE_MOUSE,
/* Ask the module to acknowledge/refuse the fullscreen state change after
* being requested (externaly or by VOUT_DISPLAY_EVENT_FULLSCREEN */
VOUT_DISPLAY_CHANGE_FULLSCREEN, /* const vout_display_cfg_t *p_cfg */
#endif
case VOUT_DISPLAY_RESET_PICTURES:
assert(0);
default:
msg_Err (vd, "Unknown request in XCB vout display");
return VLC_EGENERIC;
}
}
static void Manage (vout_display_t *vd)
{
vout_display_sys_t *p_sys = vd->sys;
ManageEvent (vd, p_sys->conn, p_sys->window);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment