Commit 50eebc56 authored by Jean-Paul Saman's avatar Jean-Paul Saman

VAAPI-XCB: Adding GLX based video output.

Reuse as much code as possible from vaapi-xcb implementation. Most of
the routines can be shared and are placed inside common.{c,h}. The
video output has been moved to modules/video_output/vaapi/, but is
still build from modules/codec/avcodec/Modules.am. This is because
the video output shares the same libva connection as the decoder.
Since this is mandatory for some GPU implementations such as AMD Fusion.
parent a941d753
......@@ -3061,6 +3061,7 @@ AC_ARG_ENABLE(glx,
])
have_xcb="no"
have_glx="no"
AS_IF([test "${enable_xcb}" != "no"], [
dnl libxcb
PKG_CHECK_MODULES(XCB, [xcb >= 1.6])
......@@ -3093,7 +3094,7 @@ AS_IF([test "${enable_xcb}" != "no"], [
AC_MSG_WARN([${XCB_KEYSYMS_PKG_ERRORS}. Hotkeys will not work.])
])
dnl avcodec (vaapi-x11)
dnl avcodec (vaapi-xcb)
AS_IF([test "${enable_libva}" != "no"], [
AS_IF([test "${have_avcodec}" != "no"], [
PKG_CHECK_MODULES(XLIB_XCB, [x11-xcb])
......@@ -3108,10 +3109,24 @@ AS_IF([test "${enable_xcb}" != "no"], [
AS_IF([test "${have_gl}" != "yes"], [
AC_MSG_ERROR([${GL_PKG_ERRORS}. Pass --disable-glx if you do not need OpenGL X11 support.])
])
have_glx="yes"
dnl avcodec (vaapi-glx)
AS_IF([test "${enable_libva}" != "no"], [
AS_IF([test "${have_avcodec}" != "no"], [
PKG_CHECK_MODULES(LIBVA, [libva libva-x11 libva-glx], [
VLC_ADD_CFLAGS([avcodec], [${GL_CFLAGS} ${GL_CFLAGS}])
VLC_ADD_LIBS([avcodec], [${GL_LIBS} ${GL_LIBS} -lva-glx])
AC_DEFINE(HAVE_GLX, 1, [Define if avcodec has to be built with GL support.])
], [
AC_MSG_WARN([libva XCB and GLX present, but not libva-glx.])
])
])
])
VLC_ADD_PLUGIN([xcb_glx])
])
])
AM_CONDITIONAL([HAVE_XCB], [test "${have_xcb}" = "yes"])
AM_CONDITIONAL([HAVE_GLX], [test "${have_glx}" = "yes"])
dnl
dnl SDL module
......
......@@ -19,13 +19,24 @@ libavcodec_plugin_la_SOURCES = \
if HAVE_XCB
libavcodec_plugin_la_SOURCES += \
vasub.h \
vasub.c \
vaapi_x11.c \
../../video_output/vaapi/spu.h \
../../video_output/vaapi/spu.c \
../../video_output/vaapi/common.h \
../../video_output/vaapi/common.c \
../../video_output/vaapi/xcb.h \
../../video_output/vaapi/xcb.c \
../../video_output/xcb/events.c \
../../video_output/xcb/xcb_events_vlc.h \
$(NULL)
endif
if HAVE_GLX
libavcodec_plugin_la_SOURCES += \
../../video_output/vaapi/glx.h \
../../video_output/vaapi/glx.c \
../../video_output/opengl.h \
../../video_output/opengl.c
$(NULL)
endif
if ENABLE_SOUT
libavcodec_plugin_la_SOURCES += \
......
......@@ -81,6 +81,13 @@ static const char *const enc_hq_list_text[] = {
# include "../../access/avio.h"
#endif
#if defined(HAVE_AVCODEC_VAAPI) && defined(HAVE_XCB)
# include "../../video_output/vaapi/xcb.h"
# ifdef HAVE_GLX
# include "../../video_output/vaapi/glx.h"
# endif
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
......@@ -216,17 +223,33 @@ vlc_module_begin ()
#endif
#if defined(HAVE_AVCODEC_VAAPI) && defined(HAVE_XCB)
/* vaapi_x11 submodule */
/* vaapi_xcb submodule */
add_submodule()
set_description (N_("VAAPI X11 video output (XCB)"))
set_category (CAT_VIDEO)
set_subcategory (SUBCAT_VIDEO_VOUT)
set_capability ("vout display", 275)
set_callbacks (OpenVaapiX11, CloseVaapiX11)
set_callbacks (OpenVaapiXCB, CloseVaapiXCB)
add_shortcut ("vaapi-x11", "vaapi-xcb", "xid")
add_bool( VOUT_CFG_PREFIX "recycle", false,
VOUT_RECYCLE_TEXT, VOUT_RECYCLE_LONGTEXT, true )
/* vaapi_glx submodule */
# ifdef HAVE_GLX
add_submodule()
set_description (N_("VAAPI GLX video output (XCB)"))
set_category (CAT_VIDEO)
set_subcategory (SUBCAT_VIDEO_VOUT)
set_capability ("vout display", 250)
set_callbacks (OpenVaapiGLX, CloseVaapiGLX)
add_shortcut ("vaapi-glx", "xid")
add_bool( VOUT_CFG_PREFIX "recycle", false,
VOUT_RECYCLE_TEXT, VOUT_RECYCLE_LONGTEXT, true )
# endif
#endif
vlc_module_end ()
......
......@@ -61,12 +61,6 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
AVCodec *p_codec, int i_codec_id, const char *psz_namecodec );
void EndVideoDec( decoder_t *p_dec );
/* VAAPI X11 Video Output */
#ifdef HAVE_AVCODEC_VAAPI
int OpenVaapiX11 (vlc_object_t *);
void CloseVaapiX11 (vlc_object_t *);
#endif
/* Audio Decoder */
int InitAudioDec( decoder_t *p_dec, AVCodecContext *p_context,
AVCodec *p_codec, int i_codec_id, const char *psz_namecodec );
......
......@@ -42,6 +42,9 @@
# include <libavcodec/vaapi.h>
# include <va/va.h>
# include <va/va_x11.h>
# if HAVE_GLX
# include <va/va_glx.h>
# endif
#endif
#include "va.h"
#include "vaapi.h"
......@@ -468,11 +471,17 @@ vlc_va_conn_t *vlc_va_Initialize( const char *display )
goto error;
/* Create a VA display */
/* FIXME: allow for runtime choice of OpenGL or not */
#warning "fix the non-openGL case"
#if HAVE_GLX
conn->p_display = vaGetDisplayGLX(conn->x11);
#else
conn->p_display = vaGetDisplay(conn->x11);
#endif
if( !conn->p_display )
goto error;
if( vaInitialize( conn->p_display, &conn->i_version_major, &conn->i_version_minor ) )
if( vaInitialize(conn->p_display, &conn->i_version_major, &conn->i_version_minor) )
goto error;
conn->i_ref_count++;
......
/*****************************************************************************
* Copyright © 2011 - 2013, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_vout_window.h>
#include <vlc_picture_pool.h>
#include <vlc_arrays.h>
#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <vlc_xlib.h>
#include <X11/Xutil.h>
#include "../../video_output/xcb/xcb_events_vlc.h"
#if defined(HAVE_AVCODEC_VAAPI) && defined(HAVE_XCB)
#ifdef HAVE_LIBAVCODEC_AVCODEC_H
# include <libavcodec/avcodec.h>
#else
# include <avcodec.h>
#endif
#include "../../codec/avcodec/avcodec.h"
#include <libavcodec/vaapi.h>
#include <va/va.h>
#include <va/va_x11.h>
#include "../../codec/avcodec/va.h"
#include "../../codec/avcodec/vaapi.h"
#include "common.h"
#include "spu.h"
vout_window_t *MakeWindow(vout_display_t *vd)
{
vout_window_cfg_t wnd_cfg;
memset (&wnd_cfg, 0, sizeof (wnd_cfg));
wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
wnd_cfg.x = var_InheritInteger(vd, "video-x");
wnd_cfg.y = var_InheritInteger(vd, "video-y");
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(vd, "parent window not available");
return wnd;
}
const xcb_screen_t *FindWindow(vout_display_t *vd,
vout_window_t *embed, xcb_connection_t *conn,
unsigned *restrict pnum, uint8_t *restrict pdepth,
uint16_t *restrict pwidth, uint16_t *restrict pheight)
{
xcb_get_geometry_reply_t *geo =
xcb_get_geometry_reply(conn,
xcb_get_geometry(conn, embed->handle.xid), NULL);
if (geo == NULL)
{
msg_Err (vd, "parent window not valid");
return NULL;
}
xcb_window_t root = geo->root;
*pdepth = geo->depth;
*pwidth = geo->width;
*pheight = geo->height;
free (geo);
/* Find the selected screen */
const xcb_setup_t *setup = xcb_get_setup(conn);
const xcb_screen_t *screen = NULL;
unsigned num = 0;
for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
i.rem > 0;
xcb_screen_next (&i))
{
if (i.data->root == root)
{
screen = i.data;
break;
}
num++;
}
if (screen == NULL)
{
msg_Err(vd, "parent window screen not found");
return NULL;
}
msg_Dbg(vd, "using screen 0x%"PRIx32 " (number: %u)", root, num);
*pnum = num;
return screen;
}
int CreateWindow(vout_display_t *vd,
vout_window_t *embed, xcb_connection_t *conn,
uint_fast8_t depth, xcb_visualid_t vid,
uint_fast16_t width, uint_fast16_t height,
xcb_window_t *restrict window)
{
const uint32_t mask = XCB_CW_EVENT_MASK;
const uint32_t values[] = {
/* XCB_CW_EVENT_MASK */
XCB_EVENT_MASK_VISIBILITY_CHANGE,
};
xcb_void_cookie_t cc, cm;
cc = xcb_create_window_checked(conn, depth, *window,
embed->handle.xid, 0, 0,
width, height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
vid, mask, values);
cm = xcb_map_window_checked(conn, *window);
if (CheckError(vd, conn, "cannot create XCB window", cc) ||
CheckError(vd, conn, "cannot map XCB window", cm))
return VLC_EGENERIC;
msg_Dbg (vd, "using VAAPI XCB window %08"PRIx32, *window);
xcb_flush(conn);
return VLC_SUCCESS;
}
/**
* PictureLock and PictureUnlock will be called by picture_pool_*
* when the picture is held and released. Both are called only
* once. PictureLock when the picture is retrieved from the pool
* and PictureUnlock when the picture is returned to the pool.
*/
static int PictureLock(picture_t *picture)
{
vlc_va_surface_t *surface = picture->p_sys;
if (surface == NULL)
abort();
vlc_va_SurfaceHold(surface);
return VLC_SUCCESS;
}
static void PictureUnlock(picture_t *picture)
{
assert(picture->p_sys);
vlc_va_surface_t *surface = picture->p_sys;
if (surface == NULL)
abort();
vlc_va_SurfaceRelease(surface);
}
/* PictureRelease is called when the picture pool is destroyed. */
static void PictureRelease(picture_t *picture)
{
if (picture->i_refcount > 0)
picture->i_refcount--;
picture->p_sys = NULL;
}
/**
* Create a pool of direct buffers to use.
*/
picture_pool_t *vout_vaapi_GetPool(vout_display_t *vd, vlc_va_conn_t *va, unsigned requested)
{
picture_pool_t *pool = NULL;
assert(va);
assert(vd);
assert(requested > 0);
/* Create surfaces */
va->lock();
unsigned int alloc_count = __MAX(vlc_va_conn_SurfacesCount(va), requested);
va->unlock();
picture_t **pic_array = (picture_t **) calloc(alloc_count, sizeof(picture_t *));
if (!pic_array)
return NULL;
unsigned int count;
for (count = 0; count < alloc_count; count++)
{
picture_t *pic = (picture_t*) picture_NewFromFormat(&vd->fmt);
if (!pic)
break;
assert(pic->p_sys == NULL);
pic->format.i_chroma = VLC_CODEC_VAAPI_SURFACE;
pic_array[count] = pic;
pic_array[count]->b_progressive = true;
pic_array[count]->pf_release = PictureRelease;
}
assert( count > 0 );
if (count == 0)
goto error;
unsigned int i_surface_count = __MIN(count, alloc_count);
assert(i_surface_count == count);
msg_Info(vd, "Requested %d/%d surfaces for picture pool.",
i_surface_count, vlc_va_conn_SurfacesCount(va));
/* Create surfaces */
va->lock();
if (!vlc_va_conn_CreateSurface(va,
vd->fmt.i_visible_width,
vd->fmt.i_visible_height,
VA_RT_FORMAT_YUV420, i_surface_count))
{
/* FAIL */
va->unlock();
goto error;
}
for (unsigned int i = 0; i < i_surface_count; i++)
{
vlc_va_surface_t *surface = vlc_va_conn_GetSurface(va);
if( surface )
pic_array[i]->p_sys = (picture_sys_t *)surface;
}
va->unlock();
/* Register pool with video output core */
picture_pool_configuration_t cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.picture_count = count;
cfg.picture = pic_array;
cfg.lock = PictureLock;
cfg.unlock = PictureUnlock;
pool = picture_pool_NewExtended(&cfg);
if (!pool)
goto error;
free(pic_array);
return pool;
error:
/* Cleanup on error */
va->lock();
for (unsigned int i = 0; i < count; i++)
{
picture_t *pic = pic_array[i];
if (!pic)
continue;
if (pic->p_sys)
{
vlc_va_surface_t *surface = (vlc_va_surface_t *) pic->p_sys;
vlc_va_conn_ReleaseSurface(va, surface);
}
picture_Release(pic);
}
free(pic_array);
va->unlock();
return NULL;
}
#endif /* HAVE_AVCODEC_VAAPI */
/*****************************************************************************
* Copyright © 2011 - 2013, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifndef VLC_VAAPI_COMMON_H
#define VLC_VAAPI_COMMON_H 1
vout_window_t *MakeWindow(vout_display_t *vd);
const xcb_screen_t *FindWindow(vout_display_t *vd,
vout_window_t *embed, xcb_connection_t *conn,
unsigned *restrict pnum, uint8_t *restrict pdepth,
uint16_t *restrict pwidth, uint16_t *restrict pheight);
int CreateWindow(vout_display_t *vd,
vout_window_t *embed, xcb_connection_t *conn,
uint_fast8_t depth, xcb_visualid_t vid,
uint_fast16_t width, uint_fast16_t height,
xcb_window_t *restrict window);
picture_pool_t *vout_vaapi_GetPool(vout_display_t *, vlc_va_conn_t *, unsigned);
#endif
/*****************************************************************************
* Copyright © 2013, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_vout_display.h>
#include <vlc_vout_window.h>
#include <vlc_picture_pool.h>
#include <vlc_arrays.h>
#if defined(HAVE_AVCODEC_VAAPI) && defined(HAVE_XCB) && defined(HAVE_GLX)
#ifdef HAVE_LIBAVCODEC_AVCODEC_H
# include <libavcodec/avcodec.h>
#else
# include <avcodec.h>
#endif
#include "../../codec/avcodec/avcodec.h"
#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xutil.h>
#include <vlc_xlib.h>
#include "../../video_output/xcb/xcb_events_vlc.h"
#include <GL/glx.h>
#include <GL/glxext.h>
#include <vlc_opengl.h>
#include "../../video_output/opengl.h"
#include <libavcodec/vaapi.h>
#include <va/va.h>
#include <va/va_x11.h>
#include <va/va_glx.h>
#include "../../codec/avcodec/va.h"
#include "../../codec/avcodec/vaapi.h"
#include "spu.h"
#include "common.h"
#include "xcb.h"
#include "glx.h"
/* define for extra debugging */
#undef VAAPI_DEBUG
static picture_pool_t *Pool(vout_display_t *, unsigned);
static void Render(vout_display_t *, picture_t *, subpicture_t *);
static void DisplayPicture(vout_display_t *, picture_t *, subpicture_t *);
/* */
struct vout_display_sys_t
{
vlc_va_conn_t *va;
xcb_connection_t *conn; /**< XCB connection */
vout_window_t *embed;
picture_pool_t *pool; /* picture pool */
VAImageFormat img_fmt;
VAImageFormat sub_fmt; /* Subpicture format VA_FOURCC_RGBA */
int sflags; /* Subtitle format flags */
struct {
unsigned int i_cache;
mtime_t i_start;
mtime_t i_stop;
subpicture_t *prev_subpicture;
} render;
vlc_mutex_t cache_lock;
vlc_array_t cache; /* array of subpicture_subpicture_cache_t */
xcb_cursor_t cursor; /* blank cursor */
xcb_window_t window; /* drawable X window */
xcb_window_t glwin; /* GLX window */
/* OpenGL */
vlc_gl_t gl;
GLXContext ctx;
GLuint gl_texture;
void *gl_surface;
const vlc_chroma_description_t *chroma;
bool visible; /* whether to draw */
};
static bool CheckGLXversion(vout_display_t *vd, Display *dpy, int major, int minor)
{
int i_major, i_minor;
if (!glXQueryVersion(dpy, &i_major, &i_minor))
{
msg_Dbg (vd, "GLX extension not available");
return false;
}
if (i_major != major)
msg_Dbg (vd, "GLX extension version %d.%d unknown", i_major, i_minor);
else if (i_minor < minor)
msg_Dbg (vd, "GLX extension version %d.%d too old", i_major, i_minor);
else
{
msg_Dbg (vd, "using GLX extension version %d.%d", i_major, i_minor);
return (i_minor >= minor);
}
return false;
}
static void SwapBuffers(vlc_gl_t *gl)
{
vout_display_sys_t *sys = gl->sys;
glXSwapBuffers(sys->va->x11, sys->glwin);
}
static void *GetProcAddress(vlc_gl_t *gl, const char *name)
{
(void)gl;
#ifdef GLX_ARB_get_proc_address
return glXGetProcAddressARB ((const GLubyte *)name);
#else
return NULL;
#endif
}
static int InitializeGLXv13(vout_display_t *vd)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
Display *dpy = va->x11;
/* Find window parameters */
unsigned snum;
uint8_t depth;
uint16_t width, height;
const xcb_screen_t *scr = FindWindow(vd, sys->embed,
sys->conn, &snum, &depth,
&width, &height);
if (scr == NULL)
return VLC_EGENERIC;
sys->window = xcb_generate_id(sys->conn);
/* GLX 1.3 */
static const int attr[] = {
GLX_RED_SIZE, 5,
GLX_GREEN_SIZE, 5,
GLX_BLUE_SIZE, 5,
GLX_DOUBLEBUFFER, True,
GLX_X_RENDERABLE, True,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
None };
xcb_get_window_attributes_reply_t *wa =
xcb_get_window_attributes_reply(sys->conn,
xcb_get_window_attributes(sys->conn, sys->embed->handle.xid),
NULL);
if (wa == NULL)
return VLC_EGENERIC;
xcb_visualid_t visual = wa->visual;
free(wa);
int nelem;
GLXFBConfig *confs = glXChooseFBConfig(dpy, snum, attr, &nelem);
if (confs == NULL)
{
msg_Err (vd, "no GLX frame buffer configurations");
return VLC_EGENERIC;
}
GLXFBConfig conf;
bool found = false;
for (int i = 0; i < nelem && !found; i++)
{
conf = confs[i];
XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, conf);
if (vi == NULL)
continue;
if (vi->visualid == visual)
found = true;
XFree(vi);
}
XFree(confs);
if (!found)
{
msg_Err (vd, "no matching GLX frame buffer configuration");
return VLC_EGENERIC;
}
sys->glwin = None;
if (!CreateWindow(vd, sys->embed, sys->conn, depth, 0 /* ??? */,
width, height, &sys->window))
sys->glwin = glXCreateWindow(dpy, conf, sys->window, NULL );
if (sys->glwin == None)
{
msg_Err (vd, "cannot create GLX window");
return VLC_EGENERIC;
}
sys->cursor = CreateBlankCursor(sys->conn, scr);
/* Create an OpenGL context */
sys->ctx = glXCreateNewContext(dpy, conf, GLX_RGBA_TYPE, NULL, True);
if (sys->ctx == NULL)
{
msg_Err (vd, "cannot create GLX context");
return VLC_EGENERIC;
}
if (!glXMakeContextCurrent(dpy, sys->glwin, sys->glwin, sys->ctx))
return VLC_EGENERIC;
const char *glx_extensions = glXQueryExtensionsString(dpy, snum);
bool is_swap_interval_set = false;
#ifdef GLX_SGI_swap_control
if (HasExtension(glx_extensions, "GLX_SGI_swap_control")) {
PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)GetProcAddress(NULL, "glXSwapIntervalSGI");
if (!is_swap_interval_set && SwapIntervalSGI)
is_swap_interval_set = !SwapIntervalSGI (1);
}
#endif
#ifdef GLX_EXT_swap_control
if (HasExtension(glx_extensions, "GLX_EXT_swap_control")) {
PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)GetProcAddress(NULL, "glXSwapIntervalEXT");
if (!is_swap_interval_set && SwapIntervalEXT)
{
SwapIntervalEXT(dpy, sys->glwin, 1);
is_swap_interval_set = true;
}
}
#endif
return VLC_SUCCESS;
}
static int CreateGLXSurface(vout_display_t *vd)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
/* Common GLX config */
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_CULL_FACE);
glDrawBuffer(GL_BACK); /* double buffered */
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/* Create OpenGL texture */
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &sys->gl_texture);
glBindTexture(GL_TEXTURE_2D, sys->gl_texture);
if (glIsTexture(sys->gl_texture) == GL_FALSE)
{
msg_Err(vd, "unable to allocate a GL texture");
sys->gl_texture = GL_NONE;
return VLC_EGENERIC;
}
#if !USE_OPENGL_ES
/* Set the texture parameters */
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 1.0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
#endif
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
vd->fmt.i_width, vd->fmt.i_height, 0,
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
/* Create GLX surfaces */
va->lock();
VAStatus status = vaCreateSurfaceGLX(va->p_display,
GL_TEXTURE_2D,
sys->gl_texture,
&sys->gl_surface);
va->unlock();
if (status != VA_STATUS_SUCCESS)
{
msg_Err(vd, "unable to allocate GLX Surface");
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static int Control(vout_display_t *vd, int query, va_list ap)
{
vout_display_sys_t *sys = vd->sys;
switch (query)
{
case VOUT_DISPLAY_CHANGE_FULLSCREEN:
{
const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
}
case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
{
unsigned state = va_arg(ap, unsigned);
return vout_window_SetState(sys->embed, state);
}
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_ZOOM:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
{
const vout_display_cfg_t *cfg;
const video_format_t *source;
bool is_forced = false;
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 (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
is_forced = (bool)va_arg(ap, int);
}
/* */
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
&& is_forced
&& (cfg->display.width != vd->cfg->display.width
||cfg->display.height != vd->cfg->display.height)
&& vout_window_SetSize(sys->embed,
cfg->display.width, cfg->display.height))
return VLC_EGENERIC;
vout_display_place_t place;
vout_display_PlacePicture(&place, source, cfg, false);
/* Move the picture within the window */
const uint32_t values[] = { place.x, place.y,
place.width, place.height, };
xcb_void_cookie_t ck =
xcb_configure_window_checked(sys->conn, sys->window,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
values);
if (CheckError(vd, sys->conn, "cannot resize XCB window", ck))
return VLC_EGENERIC;
glViewport(0, 0, place.width, place.height);
return VLC_SUCCESS;
}
/* Hide the mouse. It will be send when
* vout_display_t::info.b_hide_mouse is false */
case VOUT_DISPLAY_HIDE_MOUSE:
{
xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
xcb_flush(sys->conn);
return VLC_SUCCESS;
}
case VOUT_DISPLAY_GET_OPENGL:
{
vlc_gl_t **gl = va_arg (ap, vlc_gl_t **);
*gl = &sys->gl;
return VLC_SUCCESS;
}
case VOUT_DISPLAY_RESET_PICTURES:
assert(0);
default:
msg_Err (vd, "Unknown request in VAAPI vout display");
return VLC_EGENERIC;
}
}
static void Manage(vout_display_t *vd)
{
int canc = vlc_savecancel();
vout_display_sys_t *sys = vd->sys;
ManageEvent(vd, sys->conn, &sys->visible);
vlc_restorecancel(canc);
}
/**
* Probe the X server.
*/
int OpenVaapiGLX(vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *) obj;
const bool b_use_hw = var_GetBool(vd->p_libvlc, "ffmpeg-hw");
if (!b_use_hw)
{
msg_Warn(vd, "requires GPU hardware decoding to be enabled (option --ffmpeg-hw)");
return VLC_EGENERIC;
}
if (!vlc_xlib_init(VLC_OBJECT(vd)))
return VLC_EGENERIC;
vout_display_sys_t *sys = calloc(1, sizeof(*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
vd->sys = sys;
vlc_array_init(&sys->cache);
vlc_mutex_init(&sys->cache_lock);
sys->render.prev_subpicture = NULL;
sys->render.i_cache = 0;
sys->visible = false;
sys->embed = MakeWindow(vd);
if (unlikely(sys->embed == NULL))
{
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
/* Create a VA display */
vlc_va_conn_t *va = vlc_va_Initialize(sys->embed->display.x11);
if (unlikely(va == NULL))
{
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
/* Connect to X server */
xcb_connection_t *conn = xcb_connect(sys->embed->display.x11, NULL);
if (unlikely(xcb_connection_has_error (conn)))
{
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
sys->va = va;
sys->conn = conn;
va->lock();
XSetEventQueueOwner(va->x11, XCBOwnsEventQueue);
RegisterMouseEvents(obj, conn, sys->embed->handle.xid);
sys->gl_texture = GL_NONE;
sys->gl_surface = NULL;
sys->gl.sys = NULL;
sys->ctx = NULL;
if (!CheckGLXversion(vd, va->x11, 1, 3))
{
va->unlock();
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
if (InitializeGLXv13(vd) != VLC_SUCCESS)
{
va->unlock();
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
/* Initialize common OpenGL video display */
sys->gl.lock = NULL;
sys->gl.unlock = NULL;
sys->gl.swap = SwapBuffers;
sys->gl.getProcAddress = GetProcAddress;
sys->gl.sys = sys;
video_format_t fmt;
int result = vlc_va_get_image_format(va, VA_FOURCC( 'Y', 'V', '1', '2' ),
&sys->img_fmt, &fmt);
va->unlock();
if (result != VLC_SUCCESS)
{
msg_Err(vd, "GPU does not support requested fourcc");
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
vd->fmt.i_chroma = fmt.i_chroma;
vd->fmt.i_bits_per_pixel = fmt.i_bits_per_pixel;
sys->chroma = vlc_fourcc_GetChromaDescription(vd->fmt.i_chroma);
/* Initialize common OpenGL video display */
if (CreateGLXSurface(vd) != VLC_SUCCESS)
{
CloseVaapiGLX(obj);
return VLC_EGENERIC;
}
#if 1 /* DEBUG CODE */
msg_Dbg(vd, "video output format size %d x %d (%d x %d)",
vd->fmt.i_width, vd->fmt.i_height,
vd->fmt.i_visible_width, vd->fmt.i_visible_height);
#endif
/* */
vout_display_info_t info = vd->info;
info.is_slow = false;
info.has_pictures_invalid = false;
info.has_event_thread = true;
info.has_hide_mouse = false;
/* NOTE: Needs to be false for AMD/ATI Graphics cards */
info.can_recycle = var_CreateGetBool(vd, VOUT_CFG_PREFIX "recycle");
/* Setup vout_display_t once everything is fine */
if (VASubtitleFourCC(va, VA_FOURCC_RGBA, &sys->sub_fmt, &sys->sflags) == VLC_SUCCESS)
info.subpicture_chromas = va_subpicture_chromas;
else
msg_Warn(vd, "libva does not support fourcc RGBA subtitles");
/* Initialize common video display */
vd->info = info;
vd->pool = Pool;
vd->manage = Manage;
vd->control = Control;
vd->display = DisplayPicture;
vd->prepare = vd->info.subpicture_chromas ? Render : NULL;
/* */
if (!(sys->sflags & VA_SUBPICTURE_GLOBAL_ALPHA) &&
!(sys->sflags & VA_SUBPICTURE_CHROMA_KEYING))
msg_Warn(vd, "Display server does not support alpha or chroma for subtitles.");
/* */
bool is_fullscreen = vd->cfg->is_fullscreen;
if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
is_fullscreen = false;
vout_display_SendEventFullscreen(vd, is_fullscreen);
vout_display_SendEventDisplaySize(vd, vd->fmt.i_width, vd->fmt.i_height, is_fullscreen);
msg_Info(obj, "using VAAPI GLX video output (libva version %d.%d)",
va->i_version_major, va->i_version_minor);
return VLC_SUCCESS;
}
void CloseVaapiGLX(vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *) obj;
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = (vlc_va_conn_t *)sys->va;
sys->visible = false;
/* Cleanup GLX resources */
if (sys->gl_surface)
{
VAStatus status = vaDestroySurfaceGLX(va->p_display, sys->gl_surface);
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "GLX surface not destroyed");
}
if (sys->ctx != NULL)
{
glXMakeContextCurrent(va->x11, None, None, NULL);
glXDestroyContext(va->x11, sys->ctx);
if (sys->glwin)
glXDestroyWindow(va->x11, sys->glwin);
}
if (sys->gl_texture != GL_NONE)
{
glDeleteTextures(1, &sys->gl_texture);
sys->gl_texture = GL_NONE;
}
/* Cleanup cache */
vlc_mutex_destroy(&sys->cache_lock);
cache_SubpictureEmpty(obj, va, &sys->cache);
/* Cleanup VAAPI resources and connection */
if (va)
{
va->lock();
vlc_va_conn_DestroySurfaces(va);
va->unlock();
vlc_va_Terminate(va);
}
/* Cleanup XCB windows */
if (sys->conn)
{
/* show the default cursor */
xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
XCB_CW_CURSOR, &(uint32_t) { XCB_CURSOR_NONE });
xcb_flush(sys->conn);
xcb_disconnect(sys->conn);
}
if (sys->embed)
vout_display_DeleteWindow (vd, sys->embed);
if (sys->pool)
{
picture_pool_Delete(sys->pool);
sys->pool = NULL;
}
free(vd->sys);
msg_Info(vd, "VAAPI GLX closed");
}
static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
vout_display_sys_t *sys = vd->sys;
if (!subpicture)
return;
assert(!subpicture->b_ephemer);
assert(picture->format.i_chroma == VLC_CODEC_VAAPI_SURFACE);
if (!picture->p_sys)
return;
/* count RGBA subpicture regions */
int i_region = 0;
subpicture_region_t *region = NULL;
for (region = subpicture->p_region; region != NULL; region = region->p_next)
{
/* Skip non-RGBA regions for now */
if (region->fmt.i_chroma == VLC_CODEC_RGBA)
i_region++;
}
if (i_region == 0)
return;
/* same as previous subpicture? */
if ((sys->render.prev_subpicture == subpicture) &&
(sys->render.i_cache > 0))
{
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "reuse subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture,
sys->render.i_cache, picture->p_sys->i_id);
#endif
vlc_mutex_lock(&sys->cache_lock);
int ret = RenderCachedSubpictures(VLC_OBJECT(vd), sys->va, &sys->cache,
sys->render.i_cache, picture, subpicture);
vlc_mutex_unlock(&sys->cache_lock);
if (ret == VLC_SUCCESS)
return;
}
/* new subpicture */
vlc_mutex_lock(&sys->cache_lock);
/* Release exra reference */
if (sys->render.i_cache > 0)
cache_SubpictureRelease(VLC_OBJECT(vd), sys->va, &sys->cache, sys->render.i_cache);
/* render new subpicture */
subpicture_cache_t *cache = RenderDirectSubpicture(VLC_OBJECT(vd), sys->va, &sys->sub_fmt,
picture, subpicture, sys->sflags);
if (cache == NULL)
{
vlc_mutex_unlock(&sys->cache_lock);
return;
}
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "new subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture, cache_GetCacheID(cache), surface->i_id);
#endif
/* remember what was created, so it can be released in DisplayPicture() */
vlc_array_append(&sys->cache, (void *)cache);
sys->render.i_cache = cache_GetCacheID(cache);
sys->render.prev_subpicture = subpicture;
sys->render.i_start = subpicture->i_start;
sys->render.i_stop = subpicture->i_stop;
/* Hold extra reference */
cache_SubpictureHold(VLC_OBJECT(vd), &sys->cache, cache_GetCacheID(cache));
vlc_mutex_unlock(&sys->cache_lock);
}
static void DisplayVASurfaceGLX(vout_display_t *vd, picture_t *picture)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
picture_sys_t *surface = picture->p_sys;
assert(surface);
va->lock();
/* NOTE: libva defines various scaling filters,
* we use the default here. */
unsigned int flags = VA_FILTER_SCALING_DEFAULT;
/* NOTE: vaCreateContext() specifies VA_PROGRESSIVE */
flags |= picture->b_progressive ? VA_FRAME_PICTURE :
(picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, sys->gl_texture);
VAStatus status;
status = vaCopySurfaceGLX(va->p_display,
sys->gl_surface,
surface->i_id,
flags);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed displaying picture: %d (surface id=%d): %p",
status, surface->i_id, picture);
sys->va->unlock();
/* Render frame */
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, sys->gl_texture);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
{
float scale_w, scale_h;
scale_w = (float)sys->chroma->p[0].w.num / sys->chroma->p[0].w.den / vd->fmt.i_width;
scale_h = (float)sys->chroma->p[0].h.num / sys->chroma->p[0].h.den / vd->fmt.i_height;
float left, top, right, bottom;
left = (vd->source.i_x_offset + 0) * scale_w;
top = (vd->source.i_y_offset + 0) * scale_h;
right = (vd->source.i_x_offset + vd->source.i_visible_width) * scale_w;
bottom = (vd->source.i_y_offset + vd->source.i_visible_height) * scale_h;
glTexCoord2f(left, top); glVertex2f(-1.0f, 1.0f);
glTexCoord2f(right, top); glVertex2f( 1.0f, 1.0f);
glTexCoord2f(right, bottom); glVertex2f( 1.0f, -1.0f);
glTexCoord2f(left, bottom); glVertex2f(-1.0f, -1.0f);
}
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
static void DisplayPicture(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
int ret = VLC_EGENERIC;
int canc = vlc_savecancel();
if (!sys->visible)
goto out;
assert(picture->format.i_chroma == VLC_CODEC_VAAPI_SURFACE);
if ((picture->p_sys == NULL) ||
(picture->p_sys->i_id == VA_INVALID_SURFACE))
{
msg_Err(vd, "discarding picture without picture_sys_t information");
goto out;
}
if (subpicture)
{
vlc_mutex_lock(&sys->cache_lock);
ret = SubpictureRegionsLink(VLC_OBJECT(vd), sys->va, &sys->cache, picture, sys->sflags);
if (ret == VLC_EGENERIC)
msg_Err(vd, "Failed linking subpicture to surface");
vlc_mutex_unlock(&sys->cache_lock);
}
if (!vlc_gl_Lock(&sys->gl))
{
DisplayVASurfaceGLX(vd, picture);
vlc_gl_Swap(&sys->gl);
vlc_gl_Unlock(&sys->gl);
}
if (vd->cfg->is_fullscreen)
glClear(GL_COLOR_BUFFER_BIT);
if (subpicture)
{
vlc_mutex_lock(&sys->cache_lock);
if (ret == VLC_SUCCESS)
{
if (SubpictureRegionsUnlink(VLC_OBJECT(vd), sys->va, &sys->cache, picture) == VLC_EGENERIC)
msg_Err(vd, "Could not find subpicture in cache");
}
if (picture->p_sys->i_cache > 0)
cache_SubpictureRelease(VLC_OBJECT(vd), sys->va, &sys->cache, picture->p_sys->i_cache);
picture->p_sys->i_cache = 0;
vlc_mutex_unlock(&sys->cache_lock);
}
out:
if (subpicture) subpicture_Delete(subpicture);
picture_Release(picture);
vlc_restorecancel(canc);
}
static picture_pool_t *Pool(vout_display_t *vd, unsigned requested)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
if (sys->pool)
return sys->pool;
sys->pool = vout_vaapi_GetPool(vd, va, requested);
xcb_flush(sys->conn);
return sys->pool;
}
#endif /* HAVE_AVCODEC_VAAPI */
/*****************************************************************************
* Copyright © 2013, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifndef VLC_VAAPI_GLX_H
#define VLC_VAAPI_GLX_H 1
int OpenVaapiGLX(vlc_object_t *);
void CloseVaapiGLX(vlc_object_t *);
#endif
......@@ -40,8 +40,8 @@
#include <va/va.h>
#include "vaapi.h"
#include "vasub.h"
#include "../../codec/avcodec/vaapi.h"
#include "spu.h"
/* subpicture region cache management */
static unsigned int cache_counter = 0;
......@@ -221,11 +221,11 @@ unsigned int cache_GetCacheID(const subpicture_cache_t *cache)
/* ------------------------ */
/* VA API support functions */
/* ------------------------ */
int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags)
int VASubtitleFourCC(vlc_va_conn_t *va, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags)
{
vaconn->lock();
va->lock();
int num = vaMaxNumSubpictureFormats(vaconn->p_display);
int num = vaMaxNumSubpictureFormats(va->p_display);
if (num <= 0)
goto out_warning;
......@@ -235,7 +235,7 @@ int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImage
goto out;
unsigned int num_fmt = 0;
VAStatus status = vaQuerySubpictureFormats(vaconn->p_display,
VAStatus status = vaQuerySubpictureFormats(va->p_display,
p_fmt, vaflags, &num_fmt);
if (status != VA_STATUS_SUCCESS)
goto out;
......@@ -249,7 +249,7 @@ int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImage
memcpy((void*)sub_fmt, (void*)&format, sizeof(VAImageFormat));
free(p_fmt);
free(vaflags);
vaconn->unlock();
va->unlock();
return VLC_SUCCESS;
}
}
......@@ -259,7 +259,7 @@ out:
free(vaflags);
out_warning:
vaconn->unlock();
va->unlock();
return VLC_EGENERIC;
}
......@@ -391,7 +391,7 @@ static int CopyRGBAToVAImage(vlc_object_t *obj, VADisplay p_display,
/* Create subpicture cache for this subpicture and
* allocate needed VA API resources. Call this
* function when holding cache->lock */
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *va, VAImageFormat *fmt,
const subpicture_t *subpicture, const subpicture_region_t *region, const int flags)
{
assert(subpicture);
......@@ -400,21 +400,21 @@ vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *v
if (unlikely(!vasub_cache))
return NULL;
vaconn->lock();
va->lock();
/* Copy pixels from region into VAImage */
VAImage *image = vasub_cache->image;
int err = CopyRGBAToVAImage(obj, vaconn->p_display, region->p_picture,
int err = CopyRGBAToVAImage(obj, va->p_display, region->p_picture,
image, fmt, (flags == 0x0) ? true : false);
if (err != VLC_SUCCESS)
{
subpicture_cache_destroy(vasub_cache);
vaconn->unlock();
va->unlock();
return NULL;
}
VAStatus status;
status = vaCreateSubpicture(vaconn->p_display, image->image_id, &vasub_cache->i_id);
status = vaCreateSubpicture(va->p_display, image->image_id, &vasub_cache->i_id);
if (status != VA_STATUS_SUCCESS)
goto cleanup;
......@@ -428,7 +428,7 @@ vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *v
assert(!b_chroma);
float global_alpha = (float)(subpicture->i_alpha * region->i_alpha / 255) / 255;
status = vaSetSubpictureGlobalAlpha(vaconn->p_display, vasub_cache->i_id, global_alpha);
status = vaSetSubpictureGlobalAlpha(va->p_display, vasub_cache->i_id, global_alpha);
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed applying alpha value to subpicture");
}
......@@ -444,7 +444,7 @@ vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *v
(region->p_picture->format.i_gmask << 16) |
(region->p_picture->format.i_bmask << 8) | 0;
status = vaSetSubpictureChromakey(vaconn->p_display, vasub_cache->i_id,
status = vaSetSubpictureChromakey(va->p_display, vasub_cache->i_id,
min, max, mask);
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed applying chroma valuesto subpicture");
......@@ -455,22 +455,22 @@ vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *v
vasub_cache->rect.i_width = region->fmt.i_visible_width;
vasub_cache->rect.i_height = region->fmt.i_visible_height;
vaconn->unlock();
va->unlock();
return vasub_cache;
cleanup:
vaDestroyImage(vaconn->p_display, image->image_id);
vaDestroyImage(va->p_display, image->image_id);
vasub_cache->i_id = VA_INVALID_ID;
subpicture_cache_destroy(vasub_cache);
vaconn->unlock();
va->unlock();
return NULL;
}
/* Destroy subpicture cache for this subpicture and
* release allocated VA API resources. Call this
* function when holding cache->lock */
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpicture_cache_t *cache, const bool b_force)
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *va, subpicture_cache_t *cache, const bool b_force)
{
VLC_UNUSED(obj);
......@@ -478,7 +478,7 @@ void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpictur
msg_Dbg(obj, "cache_SubpictureDestroy cache %d", cache->i_id);
#endif
vaconn->lock();
va->lock();
const int num = vlc_array_count(&cache->subpictures);
for (int i = num - 1; i >= 0; i--)
......@@ -496,8 +496,8 @@ void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpictur
assert(subpic->i_id != VA_INVALID_ID);
assert(subpic->image->image_id != VA_INVALID_ID);
vaDestroySubpicture(vaconn->p_display, subpic->i_id);
vaDestroyImage(vaconn->p_display, subpic->image->image_id);
vaDestroySubpicture(va->p_display, subpic->i_id);
vaDestroyImage(va->p_display, subpic->image->image_id);
subpic->i_id = VA_INVALID_ID;
subpicture_cache_destroy(subpic);
......@@ -507,10 +507,10 @@ void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpictur
cache_destroy(cache);
cache = NULL;
vaconn->unlock();
va->unlock();
}
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array,
const picture_t *picture, const int flags)
{
assert(cache_array);
......@@ -554,11 +554,11 @@ int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t
unsigned int src_y = 0;
unsigned int src_h = src_y + vasubpic->rect.i_height;
vaconn->lock();
va->lock();
/* Associate subpicture with surface */
VAStatus status;
status = vaAssociateSubpicture(vaconn->p_display, vasubpic->i_id,
status = vaAssociateSubpicture(va->p_display, vasubpic->i_id,
&surface->i_id, 1, /* array of surfaces */
/* src rectangle */
src_x, src_y, src_w, src_h,
......@@ -568,7 +568,7 @@ int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t
vasubpic->rect.i_height,
flags);
vaconn->unlock();
va->unlock();
if (status != VA_STATUS_SUCCESS)
goto cleanup;
......@@ -592,10 +592,10 @@ cleanup:
{
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
vaconn->lock();
status = vaDeassociateSubpicture(vaconn->p_display,
va->lock();
status = vaDeassociateSubpicture(va->p_display,
sub->i_id, &surface->i_id, 1);
vaconn->unlock();
va->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed unlinking subid: %d (error %d)",
......@@ -609,7 +609,7 @@ error:
return VLC_EGENERIC;
}
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn,
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *va,
vlc_array_t *cache_array, const picture_t *picture)
{
assert(cache_array);
......@@ -641,10 +641,10 @@ int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn,
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
if (sub->i_id != VA_INVALID_ID)
{
vaconn->lock();
status = vaDeassociateSubpicture(vaconn->p_display,
va->lock();
status = vaDeassociateSubpicture(va->p_display,
sub->i_id, &surface->i_id, 1);
vaconn->unlock();
va->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed unlinking subid: %d (error %d)",
......@@ -656,8 +656,7 @@ int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn,
return VLC_SUCCESS;
}
/* NOTE: call with sys->cache_lock held */
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id)
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array, const int cache_id)
{
assert(cache_array);
......@@ -679,7 +678,7 @@ void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array
#endif
int index = vlc_array_index_of_item(cache_array, (void*)cache);
vlc_array_remove(cache_array, index);
cache_SubpictureDestroy(obj, vaconn, cache, false);
cache_SubpictureDestroy(obj, va, cache, false);
}
#ifdef VAAPI_DEBUG
msg_Info(obj, "cache id %d refcount %d", cache_id, cache->i_refcount);
......@@ -687,7 +686,6 @@ void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array
}
}
/* NOTE: call with sys->cache_lock held */
void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int cache_id)
{
VLC_UNUSED(obj);
......@@ -703,7 +701,7 @@ void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int
}
}
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *va, VAImageFormat *fmt,
picture_t *picture, subpicture_t *subpicture, int flags)
{
picture_sys_t *surface = picture->p_sys;
......@@ -728,7 +726,7 @@ subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vac
/* Create Subpicture */
vasubpicture_cache_t *vasub_cache;
vasub_cache = cache_SubpictureCreate(obj, vaconn, fmt, subpicture, region, flags);
vasub_cache = cache_SubpictureCreate(obj, va, fmt, subpicture, region, flags);
if (vasub_cache == NULL)
goto cleanup;
......@@ -750,14 +748,14 @@ subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vac
cleanup:
msg_Err(obj, "failed creating subpicture cache %d for surface %d",
cache->i_id, surface->i_id);
cache_SubpictureDestroy(obj, vaconn, cache, false);
cache_SubpictureDestroy(obj, va, cache, false);
return NULL;
}
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array,
const unsigned int cache_id, picture_t *picture, subpicture_t *subpicture)
{
VLC_UNUSED(vaconn);
VLC_UNUSED(va);
assert(picture);
assert(subpicture);
......@@ -824,4 +822,21 @@ cleanup:
return VLC_EGENERIC;
}
void cache_SubpictureEmpty(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *restrict cache)
{
const int count = vlc_array_count(cache);
for (int i = count - 1; i >= 0; i--)
{
subpicture_cache_t *spu_cache;
spu_cache = (subpicture_cache_t*) vlc_array_item_at_index(cache, i);
if (!spu_cache)
continue;
vlc_array_remove(cache, i);
cache_SubpictureRelease(obj, va, cache, cache_GetCacheID(spu_cache));
cache_SubpictureDestroy(obj, va, spu_cache, true);
}
vlc_array_clear(cache);
}
#endif
......@@ -17,12 +17,21 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifndef VLC_VAAPI_VASUB_H
#define VLC_VAAPI_VASUB_H 1
#ifndef VLC_VAAPI_SPU_H
#define VLC_VAAPI_SPU_H 1
#ifdef HAVE_AVCODEC_VAAPI
/* Available subtitle formats */
static const vlc_fourcc_t va_subpicture_chromas[] = {
VLC_CODEC_RGBA,
0
};
/* NOTE: call all functions with sys->cache_lock held */
/* Check if subtitle format is supported by VA API */
int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags);
int VASubtitleFourCC(vlc_va_conn_t *va, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags);
/* */
typedef struct vasubpicture_cache_t vasubpicture_cache_t;
......@@ -31,8 +40,11 @@ typedef struct subpicture_cache_t subpicture_cache_t;
/* Get Cache id */
unsigned int cache_GetCacheID(const subpicture_cache_t *cache);
/* Empty the array of cached subpictures */
void cache_SubpictureEmpty(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *restrict cache);
/* Create new VASubpicture */
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *sub_fmt,
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *va, VAImageFormat *sub_fmt,
const subpicture_t *subpicture, const subpicture_region_t *region, const int flags);
/* Destory VASubpicture and cached subpicture */
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *conn, subpicture_cache_t *cache, const bool b_force);
......@@ -40,20 +52,20 @@ void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *conn, subpicture_
/* Hold reference to subpicture cache object */
void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int cache_id);
/* Release reference to subpicture cache object */
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id);
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array, const int cache_id);
/* Link VASubpicture to VASurface */
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array,
const picture_t *picture, const int flags);
/* Unlink VASubpicture from VASurface */
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array,
const picture_t *picture);
/* Render VLC subpictures as VASubpictures and put them into a subpicture cache */
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *va, VAImageFormat *fmt,
picture_t *picture, subpicture_t *subpicture, int flags);
/* Check if cached subpicture can be reused for the next Subpicture Render cycle */
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *va, vlc_array_t *cache_array,
const unsigned int cache_id, picture_t *picture, subpicture_t *subpicture);
#endif
......
/**
* @file vaapi_x11.c
* @brief VA API using X11 video output module for VLC media player
* @file vaapi/xcb.c
* @brief VA API using XCB video output module for VLC media player
*/
/*****************************************************************************
* Copyright © 2011 - 2013, M2X BV
......@@ -46,34 +46,36 @@
# include <avcodec.h>
#endif
#include "avcodec.h"
#include "copy.h"
#include "../../codec/avcodec/avcodec.h"
#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xutil.h>
#include <vlc_xlib.h>
#include "../../video_output/xcb/xcb_events_vlc.h"
#include <libavcodec/vaapi.h>
#include <va/va.h>
#include <va/va_x11.h>
#include "va.h"
#include "vaapi.h"
#include "vasub.h"
#include "../../codec/avcodec/va.h"
#include "../../codec/avcodec/vaapi.h"
#include <X11/Xutil.h>
#include "spu.h"
#include "common.h"
#include "xcb.h"
#include "../../video_output/xcb/xcb_events_vlc.h"
/* define for extra debugging */
#undef VAAPI_DEBUG
static const vlc_fourcc_t va_subpicture_chromas[] = {
VLC_CODEC_RGBA,
0
};
static picture_pool_t *Pool(vout_display_t *, unsigned);
static void Render(vout_display_t *, picture_t *, subpicture_t *);
static void DisplayPicture(vout_display_t *, picture_t *, subpicture_t *);
/* */
struct vout_display_sys_t
{
vlc_va_conn_t *vaconn;
vlc_va_conn_t *va;
xcb_connection_t *conn; /**< XCB connection */
vout_window_t *embed;
......@@ -100,120 +102,118 @@ struct vout_display_sys_t
bool visible;
};
static picture_pool_t *Pool(vout_display_t *, unsigned);
static void Render(vout_display_t *, picture_t *, subpicture_t *);
static void DisplayPicture(vout_display_t *, picture_t *, subpicture_t *);
static int Control(vout_display_t *, int, va_list);
static void Manage(vout_display_t *);
/* */
static vout_window_t *MakeWindow(vout_display_t *vd)
static void Manage(vout_display_t *vd)
{
vout_window_cfg_t wnd_cfg;
memset (&wnd_cfg, 0, sizeof (wnd_cfg));
wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
wnd_cfg.x = var_InheritInteger(vd, "video-x");
wnd_cfg.y = var_InheritInteger(vd, "video-y");
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(vd, "parent window not available");
return wnd;
int canc = vlc_savecancel();
vout_display_sys_t *sys = vd->sys;
ManageEvent(vd, sys->conn, &sys->visible);
vlc_restorecancel(canc);
}
static const xcb_screen_t *FindWindow(vout_display_t *vd, xcb_connection_t *conn,
unsigned *restrict pnum, uint8_t *restrict pdepth,
uint16_t *restrict pwidth, uint16_t *restrict pheight)
static int Control(vout_display_t *vd, int query, va_list ap)
{
vout_display_sys_t *sys = vd->sys;
xcb_get_geometry_reply_t *geo =
xcb_get_geometry_reply(conn,
xcb_get_geometry(conn, sys->embed->handle.xid), NULL);
if (geo == NULL)
switch (query)
{
msg_Err (vd, "parent window not valid");
return NULL;
case VOUT_DISPLAY_CHANGE_FULLSCREEN:
{
const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
}
xcb_window_t root = geo->root;
*pdepth = geo->depth;
*pwidth = geo->width;
*pheight = geo->height;
free (geo);
case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
{
unsigned state = va_arg(ap, unsigned);
return vout_window_SetState(sys->embed, state);
}
/* Find the selected screen */
const xcb_setup_t *setup = xcb_get_setup(conn);
const xcb_screen_t *screen = NULL;
unsigned num = 0;
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_ZOOM:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
{
const vout_display_cfg_t *cfg;
const video_format_t *source;
bool is_forced = false;
for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
i.rem > 0;
xcb_screen_next (&i))
if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
|| query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
{
if (i.data->root == root)
source = (const video_format_t *)va_arg(ap, const video_format_t *);
cfg = vd->cfg;
}
else
{
screen = i.data;
break;
source = &vd->source;
cfg = (const vout_display_cfg_t*)va_arg(ap, const vout_display_cfg_t *);
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
is_forced = (bool)va_arg(ap, int);
}
/* */
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
&& is_forced
&& (cfg->display.width != vd->cfg->display.width
||cfg->display.height != vd->cfg->display.height)
&& vout_window_SetSize(sys->embed,
cfg->display.width, cfg->display.height))
return VLC_EGENERIC;
vout_display_place_t place;
vout_display_PlacePicture(&place, source, cfg, false);
/* Move the picture within the window */
const uint32_t values[] = { place.x, place.y,
place.width, place.height, };
xcb_void_cookie_t ck =
xcb_configure_window_checked(sys->conn, sys->window,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
values);
if (CheckError(vd, sys->conn, "cannot resize XCB window", ck))
return VLC_EGENERIC;
return VLC_SUCCESS;
}
num++;
case VOUT_DISPLAY_RESET_PICTURES:
{
assert(0);
return VLC_SUCCESS;
}
if (screen == NULL)
/* Hide the mouse. It will be send when
* vout_display_t::info.b_hide_mouse is false */
case VOUT_DISPLAY_HIDE_MOUSE:
{
msg_Err(vd, "parent window screen not found");
return NULL;
xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
xcb_flush(sys->conn);
return VLC_SUCCESS;
}
msg_Dbg(vd, "using screen 0x%"PRIx32 " (number: %u)", root, num);
*pnum = num;
return screen;
}
static int CreateWindow(vout_display_t *vd, xcb_connection_t *conn,
uint_fast8_t depth, xcb_visualid_t vid,
uint_fast16_t width, uint_fast16_t height)
{
vout_display_sys_t *sys = vd->sys;
const uint32_t mask = XCB_CW_EVENT_MASK;
const uint32_t values[] = {
/* XCB_CW_EVENT_MASK */
XCB_EVENT_MASK_VISIBILITY_CHANGE,
};
xcb_void_cookie_t cc, cm;
cc = xcb_create_window_checked(conn, depth, sys->window,
sys->embed->handle.xid, 0, 0,
width, height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
vid, mask, values);
cm = xcb_map_window_checked(conn, sys->window);
if (CheckError(vd, conn, "cannot create X11 window", cc) ||
CheckError(vd, conn, "cannot map X11 window", cm))
default:
msg_Err (vd, "Unknown request in VAAPI vout display");
return VLC_EGENERIC;
msg_Dbg (vd, "using VAAPI X11 window %08"PRIx32, sys->window);
xcb_flush(conn);
return VLC_SUCCESS;
}
}
/**
* Probe the X server.
*/
int OpenVaapiX11(vlc_object_t *obj)
int OpenVaapiXCB(vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *) obj;
const bool b_use_hw = var_GetBool( obj->p_libvlc, "ffmpeg-hw" );
const bool b_use_hw = var_GetBool(vd->p_libvlc, "ffmpeg-hw");
if (!b_use_hw)
{
msg_Warn( obj, "VAAPI-XCB requires GPU hardware decoding to be enabled (option --ffmpeg-hw)" );
msg_Warn(vd, "requires GPU hardware decoding to be enabled (option --ffmpeg-hw)");
return VLC_EGENERIC;
}
if (!vlc_xlib_init(obj))
if (!vlc_xlib_init(VLC_OBJECT(vd)))
return VLC_EGENERIC;
vout_display_sys_t *sys = calloc(1, sizeof(*sys));
......@@ -222,10 +222,6 @@ int OpenVaapiX11(vlc_object_t *obj)
vd->sys = sys;
sys->conn = NULL;
sys->vaconn = NULL;
sys->pool = NULL;
vlc_array_init(&sys->cache);
vlc_mutex_init(&sys->cache_lock);
......@@ -234,64 +230,81 @@ int OpenVaapiX11(vlc_object_t *obj)
sys->embed = MakeWindow(vd);
if (unlikely(sys->embed == NULL))
goto error;
{
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
/* Create a VA display */
sys->vaconn = vlc_va_Initialize(sys->embed->display.x11);
if (!sys->vaconn)
goto error;
vlc_va_conn_t *va = vlc_va_Initialize(sys->embed->display.x11);
if (unlikely(va == NULL))
{
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
/* Connect to X server */
xcb_connection_t *conn = xcb_connect(sys->embed->display.x11, NULL);
if (unlikely(xcb_connection_has_error (conn)))
goto error;
{
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
sys->va = va;
sys->conn = conn;
XSetEventQueueOwner(sys->vaconn->x11, XCBOwnsEventQueue);
XSetEventQueueOwner(va->x11, XCBOwnsEventQueue);
RegisterMouseEvents(obj, conn, sys->embed->handle.xid);
RegisterMouseEvents(VLC_OBJECT(vd), conn, sys->embed->handle.xid);
/* Find window parameters */
unsigned snum;
uint8_t depth;
uint16_t width, height;
const xcb_screen_t *scr = FindWindow(vd, conn, &snum, &depth,
const xcb_screen_t *scr = FindWindow(vd, sys->embed,
conn, &snum, &depth,
&width, &height);
if (scr == NULL)
goto error;
{
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
sys->window = xcb_generate_id(conn);
if (CreateWindow(vd, conn, depth, 0 /* ??? */, width, height) != VLC_SUCCESS)
if (CreateWindow(vd, sys->embed, conn, depth,
0 /* ??? */, width, height,
&sys->window) != VLC_SUCCESS)
{
msg_Err (vd, "cannot create VAAPI-X11 window");
goto error;
msg_Err (vd, "cannot create XCB window");
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
sys->vaconn->lock();
va->lock();
video_format_t fmt;
if (vlc_va_get_image_format(sys->vaconn,
VA_FOURCC( 'Y', 'V', '1', '2' ),
&sys->img_fmt, &fmt) != VLC_SUCCESS)
int result = vlc_va_get_image_format(va, VA_FOURCC( 'Y', 'V', '1', '2' ),
&sys->img_fmt, &fmt);
va->unlock();
if (result != VLC_SUCCESS)
{
msg_Err(vd, "GPU does not support requested FOURCC");
sys->vaconn->unlock();
goto error;
msg_Err(vd, "GPU does not support requested fourcc");
CloseVaapiXCB(obj);
return VLC_EGENERIC;
}
sys->vaconn->unlock();
sys->visible = false;
sys->cursor = CreateBlankCursor(conn, scr);
msg_Info(vd, "Video output format size %d x %d / %d x %d",
vd->fmt.i_width, vd->fmt.i_height,
vd->fmt.i_visible_width, vd->fmt.i_visible_height);
vd->fmt.i_chroma = fmt.i_chroma;
vd->fmt.i_bits_per_pixel = fmt.i_bits_per_pixel;
msg_Info(vd, "using VAAPI XCB video output (libva version %d.%d)",
sys->vaconn->i_version_major, sys->vaconn->i_version_minor);
/* */
vout_display_info_t info = vd->info;
info.is_slow = false;
......@@ -302,24 +315,17 @@ int OpenVaapiX11(vlc_object_t *obj)
info.can_recycle = var_CreateGetBool(vd, VOUT_CFG_PREFIX "recycle");
/* Setup vout_display_t once everything is fine */
if (VASubtitleFourCC(sys->vaconn, VA_FOURCC_RGBA, &sys->sub_fmt, &sys->sflags) == VLC_SUCCESS)
{
if (VASubtitleFourCC(va, VA_FOURCC_RGBA, &sys->sub_fmt, &sys->sflags) == VLC_SUCCESS)
info.subpicture_chromas = va_subpicture_chromas;
vd->prepare = Render;
}
else
{
msg_Warn(vd, "VAAP-XCB does not support fourcc RGBA subtitles");
vd->prepare = NULL;
info.subpicture_chromas = NULL;
}
msg_Warn(vd, "libva does not support fourcc RGBA subtitles");
vd->info = info;
vd->pool = Pool;
vd->display = DisplayPicture;
vd->control = Control;
vd->manage = Manage;
vd->info = info;
vd->control = Control;
vd->display = DisplayPicture;
vd->prepare = vd->info.subpicture_chromas ? Render : NULL;
/* */
if (!(sys->sflags & VA_SUBPICTURE_GLOBAL_ALPHA) &&
......@@ -332,44 +338,32 @@ int OpenVaapiX11(vlc_object_t *obj)
is_fullscreen = false;
vout_display_SendEventFullscreen(vd, is_fullscreen);
vout_display_SendEventDisplaySize(vd, width, height, is_fullscreen);
return VLC_SUCCESS;
error:
CloseVaapiX11(obj);
return VLC_EGENERIC;
msg_Info(obj, "using VAAPI XCB video output (libva version %d.%d)",
vd->sys->va->i_version_major, vd->sys->va->i_version_minor);
return VLC_SUCCESS;
}
void CloseVaapiX11(vlc_object_t *obj)
void CloseVaapiXCB(vlc_object_t *obj)
{
vout_display_t *vd = (vout_display_t *) obj;
vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;
vlc_va_conn_t *va = (vlc_va_conn_t *)sys->va;
sys->visible = false;
/* Cleanup cache */
vlc_mutex_destroy(&sys->cache_lock);
const int count = vlc_array_count(&sys->cache);
for (int i = count - 1; i >= 0; i--)
{
subpicture_cache_t *cache;
cache = (subpicture_cache_t*) vlc_array_item_at_index(&sys->cache, i);
if (!cache)
continue;
vlc_array_remove(&sys->cache, i);
cache_SubpictureRelease(obj, sys->vaconn, &sys->cache, cache_GetCacheID(cache));
cache_SubpictureDestroy(obj, sys->vaconn, cache, true);
}
vlc_array_clear(&sys->cache);
cache_SubpictureEmpty(obj, va, &sys->cache);
/* */
if (sys->vaconn)
if (va)
{
sys->vaconn->lock();
vlc_va_conn_DestroySurfaces(sys->vaconn);
sys->vaconn->unlock();
vlc_va_Terminate(sys->vaconn);
va->lock();
vlc_va_conn_DestroySurfaces(va);
va->unlock();
vlc_va_Terminate(va);
}
if (sys->conn)
......@@ -391,104 +385,7 @@ void CloseVaapiX11(vlc_object_t *obj)
}
free(vd->sys);
msg_Info(vd, "VAAPI XCB closed");
}
static void Manage(vout_display_t *vd)
{
int canc = vlc_savecancel();
vout_display_sys_t *sys = vd->sys;
ManageEvent(vd, sys->conn, &sys->visible);
vlc_restorecancel(canc);
}
static int Control (vout_display_t *vd, int query, va_list ap)
{
vout_display_sys_t *sys = vd->sys;
switch (query)
{
case VOUT_DISPLAY_CHANGE_FULLSCREEN:
{
const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
}
case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
{
unsigned state = va_arg(ap, unsigned);
return vout_window_SetState(sys->embed, state);
}
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_ZOOM:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
{
const vout_display_cfg_t *cfg;
const video_format_t *source;
bool is_forced = false;
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 (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
is_forced = (bool)va_arg(ap, int);
}
/* */
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
&& is_forced
&& (cfg->display.width != vd->cfg->display.width
||cfg->display.height != vd->cfg->display.height)
&& vout_window_SetSize(sys->embed,
cfg->display.width, cfg->display.height))
return VLC_EGENERIC;
vout_display_place_t place;
vout_display_PlacePicture(&place, source, cfg, false);
/* Move the picture within the window */
const uint32_t values[] = { place.x, place.y,
place.width, place.height, };
xcb_void_cookie_t ck =
xcb_configure_window_checked(sys->conn, sys->window,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
values);
if (CheckError(vd, sys->conn, "cannot resize X11 window", ck))
return VLC_EGENERIC;
return VLC_SUCCESS;
}
case VOUT_DISPLAY_RESET_PICTURES:
{
assert(0);
return VLC_SUCCESS;
}
/* Hide the mouse. It will be send when
* vout_display_t::info.b_hide_mouse is false */
case VOUT_DISPLAY_HIDE_MOUSE:
{
xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
xcb_flush(sys->conn);
return VLC_SUCCESS;
}
default:
msg_Err (vd, "Unknown request in VAAPI-X11 vout display");
return VLC_EGENERIC;
}
msg_Info(obj, "VAAPI XCB closed");
}
static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
......@@ -528,7 +425,7 @@ static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpict
sys->render.i_cache, picture->p_sys->i_id);
#endif
vlc_mutex_lock(&sys->cache_lock);
int ret = RenderCachedSubpictures(VLC_OBJECT(vd), sys->vaconn, &sys->cache,
int ret = RenderCachedSubpictures(VLC_OBJECT(vd), sys->va, &sys->cache,
sys->render.i_cache, picture, subpicture);
vlc_mutex_unlock(&sys->cache_lock);
......@@ -541,10 +438,10 @@ static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpict
/* Release exra reference */
if (sys->render.i_cache > 0)
cache_SubpictureRelease(VLC_OBJECT(vd), sys->vaconn, &sys->cache, sys->render.i_cache);
cache_SubpictureRelease(VLC_OBJECT(vd), sys->va, &sys->cache, sys->render.i_cache);
/* render new subpicture */
subpicture_cache_t *cache = RenderDirectSubpicture(VLC_OBJECT(vd), sys->vaconn, &sys->sub_fmt,
subpicture_cache_t *cache = RenderDirectSubpicture(VLC_OBJECT(vd), sys->va, &sys->sub_fmt,
picture, subpicture, sys->sflags);
if (cache == NULL)
{
......@@ -573,10 +470,11 @@ static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpict
static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
picture_sys_t *surface = picture->p_sys;
assert(surface);
sys->vaconn->lock();
sys->va->lock();
int canc = vlc_savecancel();
......@@ -596,7 +494,7 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
(picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD);
VAStatus status;
status = vaPutSurface(sys->vaconn->p_display,
status = vaPutSurface(va->p_display,
surface->i_id,
sys->window,
vd->source.i_x_offset, vd->source.i_y_offset,
......@@ -610,7 +508,7 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
vlc_restorecancel(canc);
sys->vaconn->unlock();
sys->va->unlock();
}
static void DisplayPicture(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
......@@ -634,170 +532,46 @@ static void DisplayPicture(vout_display_t *vd, picture_t *picture, subpicture_t
if (subpicture)
{
vlc_mutex_lock(&sys->cache_lock);
ret = SubpictureRegionsLink(VLC_OBJECT(vd), sys->vaconn, &sys->cache, picture, sys->sflags);
ret = SubpictureRegionsLink(VLC_OBJECT(vd), sys->va, &sys->cache, picture, sys->sflags);
if (ret == VLC_EGENERIC)
msg_Err(vd, "Failed linking subpicture to surface");
vlc_mutex_unlock(&sys->cache_lock);
}
DisplayVASurface(vd, picture);
out:
if (subpicture)
{
vlc_mutex_lock(&sys->cache_lock);
if (ret == VLC_SUCCESS)
{
if (SubpictureRegionsUnlink(VLC_OBJECT(vd), sys->vaconn, &sys->cache, picture) == VLC_EGENERIC)
if (SubpictureRegionsUnlink(VLC_OBJECT(vd), sys->va, &sys->cache, picture) == VLC_EGENERIC)
msg_Err(vd, "Could not find subpicture in cache");
}
if (picture->p_sys->i_cache > 0)
cache_SubpictureRelease(VLC_OBJECT(vd), sys->vaconn, &sys->cache, picture->p_sys->i_cache);
cache_SubpictureRelease(VLC_OBJECT(vd), sys->va, &sys->cache, picture->p_sys->i_cache);
picture->p_sys->i_cache = 0;
vlc_mutex_unlock(&sys->cache_lock);
subpicture_Delete(subpicture);
}
out:
if (subpicture) subpicture_Delete(subpicture);
picture_Release(picture);
vlc_restorecancel(canc);
}
/**
* PictureLock and PictureUnlock will be called by picture_pool_*
* when the picture is held and released. Both are called only
* once. PictureLock when the picture is retrieved from the pool
* and PictureUnlock when the picture is returned to the pool.
*/
static int PictureLock(picture_t *picture)
{
vlc_va_surface_t *surface = picture->p_sys;
if (surface == NULL)
abort();
vlc_va_SurfaceHold(surface);
return VLC_SUCCESS;
}
static void PictureUnlock(picture_t *picture)
{
assert(picture->p_sys);
vlc_va_surface_t *surface = picture->p_sys;
if (surface == NULL)
abort();
vlc_va_SurfaceRelease(surface);
}
/* PictureRelease is called when the picture pool is destroyed. */
static void PictureRelease(picture_t *picture)
{
if (picture->i_refcount > 0)
picture->i_refcount--;
picture->p_sys = NULL;
}
/**
* Return a direct buffer
*/
static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
static picture_pool_t *Pool(vout_display_t *vd, unsigned requested)
{
vout_display_sys_t *sys = vd->sys;
vlc_va_conn_t *va = sys->va;
assert(requested_count > 0);
if (sys->pool)
return sys->pool;
/* Create surfaces */
sys->vaconn->lock();
unsigned int alloc_count = __MAX(vlc_va_conn_SurfacesCount(sys->vaconn), requested_count);
picture_t **pic_array = (picture_t **) calloc(alloc_count, sizeof(picture_t *));
if (!pic_array)
goto fail;
unsigned int count;
for (count = 0; count < alloc_count; count++)
{
picture_t *pic = (picture_t*) picture_NewFromFormat(&vd->fmt);
if (!pic)
break;
assert(pic->p_sys == NULL);
pic->format.i_chroma = VLC_CODEC_VAAPI_SURFACE;
pic_array[count] = pic;
pic_array[count]->b_progressive = true;
pic_array[count]->pf_release = PictureRelease;
}
assert( count > 0 );
if (count == 0)
goto error;
unsigned int i_surface_count = __MIN(count, alloc_count);
assert( i_surface_count == count );
msg_Info( vd, "Requested %d/%d surfaces for picture pool.",
i_surface_count, vlc_va_conn_SurfacesCount(sys->vaconn) );
/* Create surfaces */
if( !vlc_va_conn_CreateSurface( sys->vaconn,
vd->fmt.i_visible_width,
vd->fmt.i_visible_height,
VA_RT_FORMAT_YUV420, i_surface_count ) )
{
/* FAIL */
goto error;
}
for (unsigned int i = 0; i < i_surface_count; i++)
{
vlc_va_surface_t *surface = vlc_va_conn_GetSurface( sys->vaconn );
if( surface )
pic_array[i]->p_sys = (picture_sys_t *)surface;
}
/* Register pool with video output core */
picture_pool_configuration_t cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.picture_count = count;
cfg.picture = pic_array;
cfg.lock = PictureLock;
cfg.unlock = PictureUnlock;
sys->pool = picture_pool_NewExtended(&cfg);
if (!sys->pool)
goto error;
sys->pool = vout_vaapi_GetPool(vd, va, requested);
xcb_flush(sys->conn);
free(pic_array);
sys->vaconn->unlock();
return sys->pool;
error:
/* Cleanup on error */
for (unsigned int i = 0; i < count; i++)
{
picture_t *pic = pic_array[i];
if (pic)
{
if (pic->p_sys)
{
vlc_va_surface_t *surface = (vlc_va_surface_t *) pic->p_sys;
vlc_va_conn_ReleaseSurface(sys->vaconn, surface);
}
picture_Release(pic);
}
}
free(pic_array);
fail:
sys->vaconn->unlock();
return NULL;
}
#endif /* HAVE_AVCODEC_VAAPI */
/*****************************************************************************
* Copyright © 2011 - 2013, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifndef VLC_VAAPI_XCB_H
#define VLC_VAAPI_XCB_H 1
int OpenVaapiXCB(vlc_object_t *);
void CloseVaapiXCB(vlc_object_t *);
#endif
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