mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-12 04:11:31 +00:00
e88f349c6f
When the downstream has any caps, then raw video feature will be used. At this situation, the preferred format should be chose from caps which contains "vide/x-raw" feature instead of from the fist allowed caps. Fixes #142
1010 lines
28 KiB
C
1010 lines
28 KiB
C
/*
|
|
* gstvaapipluginutil.h - VA-API plugin helpers
|
|
*
|
|
* Copyright (C) 2011-2014 Intel Corporation
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
|
* Copyright (C) 2011 Collabora
|
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "gstcompat.h"
|
|
#include "gstvaapivideocontext.h"
|
|
#if USE_DRM
|
|
# include <gst/vaapi/gstvaapidisplay_drm.h>
|
|
#endif
|
|
#if USE_X11
|
|
# include <gst/vaapi/gstvaapidisplay_x11.h>
|
|
#endif
|
|
#if USE_GLX
|
|
# include <gst/vaapi/gstvaapidisplay_glx.h>
|
|
#endif
|
|
#if USE_EGL
|
|
# include <gst/vaapi/gstvaapidisplay_egl.h>
|
|
#endif
|
|
#if USE_WAYLAND
|
|
# include <gst/vaapi/gstvaapidisplay_wayland.h>
|
|
#endif
|
|
#if USE_GST_GL_HELPERS
|
|
# include <gst/gl/gl.h>
|
|
#if USE_EGL && GST_GL_HAVE_PLATFORM_EGL
|
|
# include <gst/gl/egl/gstgldisplay_egl.h>
|
|
#endif
|
|
#endif
|
|
#include "gstvaapipluginutil.h"
|
|
#include "gstvaapipluginbase.h"
|
|
|
|
/* Environment variable for disable driver white-list */
|
|
#define GST_VAAPI_ALL_DRIVERS_ENV "GST_VAAPI_ALL_DRIVERS"
|
|
|
|
typedef GstVaapiDisplay *(*GstVaapiDisplayCreateFunc) (const gchar *);
|
|
typedef GstVaapiDisplay *(*GstVaapiDisplayCreateFromHandleFunc) (gpointer);
|
|
|
|
typedef struct
|
|
{
|
|
const gchar *type_str;
|
|
GstVaapiDisplayType type;
|
|
GstVaapiDisplayCreateFunc create_display;
|
|
GstVaapiDisplayCreateFromHandleFunc create_display_from_handle;
|
|
} DisplayMap;
|
|
|
|
/* *INDENT-OFF* */
|
|
static const DisplayMap g_display_map[] = {
|
|
#if USE_WAYLAND
|
|
{"wayland",
|
|
GST_VAAPI_DISPLAY_TYPE_WAYLAND,
|
|
gst_vaapi_display_wayland_new,
|
|
(GstVaapiDisplayCreateFromHandleFunc)
|
|
gst_vaapi_display_wayland_new_with_display},
|
|
#endif
|
|
#if USE_GLX
|
|
{"glx",
|
|
GST_VAAPI_DISPLAY_TYPE_GLX,
|
|
gst_vaapi_display_glx_new,
|
|
(GstVaapiDisplayCreateFromHandleFunc)
|
|
gst_vaapi_display_glx_new_with_display},
|
|
#endif
|
|
#if USE_X11
|
|
{"x11",
|
|
GST_VAAPI_DISPLAY_TYPE_X11,
|
|
gst_vaapi_display_x11_new,
|
|
(GstVaapiDisplayCreateFromHandleFunc)
|
|
gst_vaapi_display_x11_new_with_display},
|
|
#endif
|
|
#if USE_DRM
|
|
{"drm",
|
|
GST_VAAPI_DISPLAY_TYPE_DRM,
|
|
gst_vaapi_display_drm_new},
|
|
#endif
|
|
{NULL,}
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
static GstVaapiDisplay *
|
|
gst_vaapi_create_display (GstVaapiDisplayType display_type,
|
|
const gchar * display_name)
|
|
{
|
|
GstVaapiDisplay *display = NULL;
|
|
const DisplayMap *m;
|
|
|
|
for (m = g_display_map; m->type_str != NULL; m++) {
|
|
if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY && display_type != m->type)
|
|
continue;
|
|
|
|
display = m->create_display (display_name);
|
|
if (display || display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
|
|
break;
|
|
}
|
|
return display;
|
|
}
|
|
|
|
#if USE_GST_GL_HELPERS
|
|
static GstVaapiDisplay *
|
|
gst_vaapi_create_display_from_handle (GstVaapiDisplayType display_type,
|
|
gpointer handle)
|
|
{
|
|
GstVaapiDisplay *display;
|
|
const DisplayMap *m;
|
|
|
|
if (display_type == GST_VAAPI_DISPLAY_TYPE_ANY)
|
|
return NULL;
|
|
|
|
for (m = g_display_map; m->type_str != NULL; m++) {
|
|
if (m->type == display_type) {
|
|
display = m->create_display_from_handle ?
|
|
m->create_display_from_handle (handle) : NULL;
|
|
return display;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static GstVaapiDisplayType
|
|
gst_vaapi_get_display_type_from_gl (GstGLDisplayType gl_display_type,
|
|
GstGLPlatform gl_platform)
|
|
{
|
|
switch (gl_display_type) {
|
|
#if USE_X11
|
|
case GST_GL_DISPLAY_TYPE_X11:{
|
|
#if USE_GLX
|
|
if (gl_platform == GST_GL_PLATFORM_GLX)
|
|
return GST_VAAPI_DISPLAY_TYPE_GLX;
|
|
#endif
|
|
return GST_VAAPI_DISPLAY_TYPE_X11;
|
|
}
|
|
#endif
|
|
#if USE_WAYLAND
|
|
case GST_GL_DISPLAY_TYPE_WAYLAND:{
|
|
return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
|
|
}
|
|
#endif
|
|
#if USE_EGL
|
|
case GST_GL_DISPLAY_TYPE_EGL:{
|
|
return GST_VAAPI_DISPLAY_TYPE_EGL;
|
|
}
|
|
#endif
|
|
default:
|
|
/* unsupported display. Still DRM may work. */
|
|
break;
|
|
}
|
|
|
|
return GST_VAAPI_DISPLAY_TYPE_ANY;
|
|
}
|
|
|
|
static GstVaapiDisplayType
|
|
gst_vaapi_get_display_type_from_gl_env (void)
|
|
{
|
|
const gchar *const gl_window_type = g_getenv ("GST_GL_WINDOW");
|
|
|
|
if (!gl_window_type) {
|
|
#if USE_X11 && GST_GL_HAVE_WINDOW_X11
|
|
return GST_VAAPI_DISPLAY_TYPE_X11;
|
|
#elif USE_WAYLAND && GST_GL_HAVE_WINDOW_WAYLAND
|
|
return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
|
|
#elif USE_EGL && GST_GL_HAVE_PLATFORM_EGL
|
|
return GST_VAAPI_DISPLAY_TYPE_EGL;
|
|
#endif
|
|
}
|
|
#if USE_X11
|
|
if (g_strcmp0 (gl_window_type, "x11") == 0)
|
|
return GST_VAAPI_DISPLAY_TYPE_X11;
|
|
#endif
|
|
#if USE_WAYLAND
|
|
if (g_strcmp0 (gl_window_type, "wayland") == 0)
|
|
return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
|
|
#endif
|
|
#if USE_EGL
|
|
{
|
|
const gchar *const gl_platform_type = g_getenv ("GST_GL_PLATFORM");
|
|
if (g_strcmp0 (gl_platform_type, "egl") == 0)
|
|
return GST_VAAPI_DISPLAY_TYPE_EGL;
|
|
}
|
|
#endif
|
|
|
|
return GST_VAAPI_DISPLAY_TYPE_ANY;
|
|
}
|
|
|
|
#if USE_EGL
|
|
static gint
|
|
gst_vaapi_get_gles_version_from_gl_api (GstGLAPI gl_api)
|
|
{
|
|
switch (gl_api) {
|
|
case GST_GL_API_GLES1:
|
|
return 1;
|
|
case GST_GL_API_GLES2:
|
|
return 2;
|
|
case GST_GL_API_OPENGL:
|
|
case GST_GL_API_OPENGL3:
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static guintptr
|
|
gst_vaapi_get_egl_handle_from_gl_display (GstGLDisplay * gl_display)
|
|
{
|
|
guintptr egl_handle = 0;
|
|
GstGLDisplayEGL *egl_display;
|
|
|
|
egl_display = gst_gl_display_egl_from_gl_display (gl_display);
|
|
if (egl_display) {
|
|
egl_handle = gst_gl_display_get_handle (GST_GL_DISPLAY (egl_display));
|
|
gst_object_unref (egl_display);
|
|
}
|
|
return egl_handle;
|
|
}
|
|
#endif /* USE_EGL */
|
|
|
|
static GstVaapiDisplay *
|
|
gst_vaapi_create_display_from_egl (GstGLDisplay * gl_display,
|
|
GstGLContext * gl_context, GstVaapiDisplayType display_type,
|
|
gpointer native_display)
|
|
{
|
|
GstVaapiDisplay *display = NULL;
|
|
#if USE_EGL
|
|
GstGLAPI gl_api;
|
|
gint gles_version;
|
|
guintptr egl_handler;
|
|
|
|
gl_api = gst_gl_context_get_gl_api (gl_context);
|
|
gles_version = gst_vaapi_get_gles_version_from_gl_api (gl_api);
|
|
if (gles_version == -1)
|
|
return NULL;
|
|
|
|
egl_handler = gst_vaapi_get_egl_handle_from_gl_display (gl_display);
|
|
if (egl_handler != 0) {
|
|
gpointer native_display_egl = GSIZE_TO_POINTER (egl_handler);
|
|
display = gst_vaapi_display_egl_new_with_native_display (native_display_egl,
|
|
display_type, gles_version);
|
|
}
|
|
|
|
if (!display) {
|
|
GstVaapiDisplay *wrapped_display;
|
|
|
|
wrapped_display =
|
|
gst_vaapi_create_display_from_handle (display_type, native_display);
|
|
if (wrapped_display) {
|
|
display = gst_vaapi_display_egl_new (wrapped_display, gles_version);
|
|
gst_object_unref (wrapped_display);
|
|
}
|
|
}
|
|
|
|
if (display) {
|
|
gst_vaapi_display_egl_set_gl_context (GST_VAAPI_DISPLAY_EGL (display),
|
|
GSIZE_TO_POINTER (gst_gl_context_get_gl_context (gl_context)));
|
|
}
|
|
#endif
|
|
return display;
|
|
}
|
|
#endif /* USE_GST_GL_HELPERS */
|
|
|
|
static GstVaapiDisplay *
|
|
gst_vaapi_create_display_from_gl_context (GstObject * gl_context_object)
|
|
{
|
|
#if USE_GST_GL_HELPERS
|
|
GstGLContext *const gl_context = GST_GL_CONTEXT (gl_context_object);
|
|
GstGLDisplay *const gl_display = gst_gl_context_get_display (gl_context);
|
|
GstGLDisplayType gl_display_type;
|
|
GstGLPlatform gl_platform;
|
|
gpointer native_display;
|
|
GstVaapiDisplay *display = NULL;
|
|
GstVaapiDisplayType display_type;
|
|
|
|
/* Get display type and the native hanler */
|
|
gl_display_type = gst_gl_display_get_handle_type (gl_display);
|
|
gl_platform = gst_gl_context_get_gl_platform (gl_context);
|
|
display_type =
|
|
gst_vaapi_get_display_type_from_gl (gl_display_type, gl_platform);
|
|
|
|
native_display = GSIZE_TO_POINTER (gst_gl_display_get_handle (gl_display));
|
|
|
|
if (display_type == GST_VAAPI_DISPLAY_TYPE_ANY) {
|
|
/* derive type and native_display from the active window */
|
|
GstGLWindow *const gl_window = gst_gl_context_get_window (gl_context);
|
|
if (gl_window)
|
|
native_display = GSIZE_TO_POINTER (gst_gl_window_get_display (gl_window));
|
|
display_type = gst_vaapi_get_display_type_from_gl_env ();
|
|
}
|
|
|
|
if (gl_platform == GST_GL_PLATFORM_EGL) {
|
|
display = gst_vaapi_create_display_from_egl (gl_display, gl_context,
|
|
display_type, native_display);
|
|
}
|
|
|
|
/* Non-EGL and fallback */
|
|
if (!display) {
|
|
display =
|
|
gst_vaapi_create_display_from_handle (display_type, native_display);
|
|
}
|
|
|
|
gst_object_unref (gl_display);
|
|
|
|
return display;
|
|
#endif
|
|
GST_ERROR ("No GstGL support");
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_find_gl_context (GstElement * element)
|
|
{
|
|
#if USE_GST_GL_HELPERS
|
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
|
|
|
|
/* if the element is vaapisink or any vaapi encoder it doesn't need
|
|
* to know a GstGLContext in order to create an appropriate
|
|
* GstVaapiDisplay. Let's them to choose their own
|
|
* GstVaapiDisplay */
|
|
if (GST_IS_VIDEO_SINK (element) || GST_IS_VIDEO_ENCODER (element))
|
|
return;
|
|
|
|
if (!gst_gl_ensure_element_data (plugin,
|
|
(GstGLDisplay **) & plugin->gl_display,
|
|
(GstGLContext **) & plugin->gl_other_context))
|
|
goto no_valid_gl_display;
|
|
|
|
gst_vaapi_find_gl_local_context (element, &plugin->gl_context);
|
|
|
|
if (plugin->gl_context) {
|
|
gst_vaapi_plugin_base_set_srcpad_can_dmabuf (plugin, plugin->gl_context);
|
|
} else {
|
|
GstObject *gl_context;
|
|
|
|
gl_context = gst_vaapi_plugin_base_create_gl_context (plugin);
|
|
if (gl_context) {
|
|
gst_vaapi_plugin_base_set_gl_context (plugin, gl_context);
|
|
gst_object_unref (gl_context);
|
|
}
|
|
}
|
|
|
|
/* ERRORS */
|
|
no_valid_gl_display:
|
|
{
|
|
GST_INFO_OBJECT (plugin, "No valid GL display found");
|
|
gst_object_replace (&plugin->gl_display, NULL);
|
|
gst_object_replace (&plugin->gl_other_context, NULL);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_ensure_display (GstElement * element, GstVaapiDisplayType type)
|
|
{
|
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
|
|
GstVaapiDisplay *display = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
|
|
|
|
if (gst_vaapi_video_context_prepare (element, &plugin->display)) {
|
|
/* Neighbour found and it updated the display */
|
|
if (gst_vaapi_plugin_base_has_display_type (plugin, type))
|
|
return TRUE;
|
|
}
|
|
|
|
/* Query for a local GstGL context. If it's found, it will be used
|
|
* to create the VA display */
|
|
if (!plugin->gl_context)
|
|
gst_vaapi_find_gl_context (element);
|
|
|
|
/* If no neighboor, or application not interested, use system default */
|
|
if (plugin->gl_context) {
|
|
display = gst_vaapi_create_display_from_gl_context (plugin->gl_context);
|
|
/* Cannot instantiate VA display based on GL context. Reset the
|
|
* requested display type to ANY to try again */
|
|
if (!display)
|
|
gst_vaapi_plugin_base_set_display_type (plugin,
|
|
GST_VAAPI_DISPLAY_TYPE_ANY);
|
|
}
|
|
if (!display)
|
|
display = gst_vaapi_create_display (type, plugin->display_name);
|
|
if (!display)
|
|
return FALSE;
|
|
|
|
gst_vaapi_video_context_propagate (element, display);
|
|
gst_object_unref (display);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_handle_context_query (GstElement * element, GstQuery * query)
|
|
{
|
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
|
|
const gchar *type = NULL;
|
|
GstContext *context, *old_context;
|
|
|
|
g_return_val_if_fail (query != NULL, FALSE);
|
|
|
|
#if USE_GST_GL_HELPERS
|
|
if (plugin->gl_display && plugin->gl_context && plugin->gl_other_context) {
|
|
if (gst_gl_handle_context_query (element, query,
|
|
(GstGLDisplay *) plugin->gl_display,
|
|
(GstGLContext *) plugin->gl_context,
|
|
(GstGLContext *) plugin->gl_other_context))
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
if (!plugin->display)
|
|
return FALSE;
|
|
|
|
if (!gst_query_parse_context_type (query, &type))
|
|
return FALSE;
|
|
|
|
if (g_strcmp0 (type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
|
|
return FALSE;
|
|
|
|
gst_query_parse_context (query, &old_context);
|
|
if (old_context) {
|
|
context = gst_context_copy (old_context);
|
|
gst_vaapi_video_context_set_display (context, plugin->display);
|
|
} else {
|
|
context = gst_vaapi_video_context_new_with_display (plugin->display, FALSE);
|
|
}
|
|
|
|
gst_query_set_context (query, context);
|
|
gst_context_unref (context);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_append_surface_caps (GstCaps * out_caps, GstCaps * in_caps)
|
|
{
|
|
GstStructure *structure;
|
|
const GValue *v_width, *v_height, *v_framerate, *v_par;
|
|
guint i, n_structures;
|
|
|
|
structure = gst_caps_get_structure (in_caps, 0);
|
|
v_width = gst_structure_get_value (structure, "width");
|
|
v_height = gst_structure_get_value (structure, "height");
|
|
v_framerate = gst_structure_get_value (structure, "framerate");
|
|
v_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
|
|
if (!v_width || !v_height)
|
|
return FALSE;
|
|
|
|
n_structures = gst_caps_get_size (out_caps);
|
|
for (i = 0; i < n_structures; i++) {
|
|
structure = gst_caps_get_structure (out_caps, i);
|
|
gst_structure_set_value (structure, "width", v_width);
|
|
gst_structure_set_value (structure, "height", v_height);
|
|
if (v_framerate)
|
|
gst_structure_set_value (structure, "framerate", v_framerate);
|
|
if (v_par)
|
|
gst_structure_set_value (structure, "pixel-aspect-ratio", v_par);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_apply_composition (GstVaapiSurface * surface, GstBuffer * buffer)
|
|
{
|
|
GstVideoOverlayCompositionMeta *const cmeta =
|
|
gst_buffer_get_video_overlay_composition_meta (buffer);
|
|
GstVideoOverlayComposition *composition = NULL;
|
|
|
|
if (cmeta)
|
|
composition = cmeta->overlay;
|
|
return gst_vaapi_surface_set_subpictures_from_composition (surface,
|
|
composition, TRUE);
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_value_set_format (GValue * value, GstVideoFormat format)
|
|
{
|
|
const gchar *str;
|
|
|
|
str = gst_video_format_to_string (format);
|
|
if (!str)
|
|
return FALSE;
|
|
|
|
g_value_init (value, G_TYPE_STRING);
|
|
g_value_set_string (value, str);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_value_set_format_list (GValue * value, GArray * formats)
|
|
{
|
|
GValue v_format = G_VALUE_INIT;
|
|
guint i;
|
|
|
|
g_value_init (value, GST_TYPE_LIST);
|
|
for (i = 0; i < formats->len; i++) {
|
|
GstVideoFormat const format = g_array_index (formats, GstVideoFormat, i);
|
|
|
|
if (!gst_vaapi_value_set_format (&v_format, format))
|
|
continue;
|
|
gst_value_list_append_value (value, &v_format);
|
|
g_value_unset (&v_format);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
set_video_template_caps (GstCaps * caps)
|
|
{
|
|
GstStructure *const structure = gst_caps_get_structure (caps, 0);
|
|
|
|
gst_structure_set (structure,
|
|
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
|
|
}
|
|
|
|
GstCaps *
|
|
gst_vaapi_video_format_new_template_caps (GstVideoFormat format)
|
|
{
|
|
GstCaps *caps;
|
|
|
|
g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, NULL);
|
|
|
|
caps = gst_caps_new_empty_simple ("video/x-raw");
|
|
if (!caps)
|
|
return NULL;
|
|
|
|
gst_caps_set_simple (caps,
|
|
"format", G_TYPE_STRING, gst_video_format_to_string (format), NULL);
|
|
set_video_template_caps (caps);
|
|
return caps;
|
|
}
|
|
|
|
GstCaps *
|
|
gst_vaapi_video_format_new_template_caps_from_list (GArray * formats)
|
|
{
|
|
GValue v_formats = G_VALUE_INIT;
|
|
GstCaps *caps;
|
|
|
|
caps = gst_caps_new_empty_simple ("video/x-raw");
|
|
if (!caps)
|
|
return NULL;
|
|
|
|
if (!gst_vaapi_value_set_format_list (&v_formats, formats)) {
|
|
gst_caps_unref (caps);
|
|
return NULL;
|
|
}
|
|
|
|
gst_caps_set_value (caps, "format", &v_formats);
|
|
set_video_template_caps (caps);
|
|
g_value_unset (&v_formats);
|
|
return caps;
|
|
}
|
|
|
|
GstCaps *
|
|
gst_vaapi_video_format_new_template_caps_with_features (GstVideoFormat format,
|
|
const gchar * features_string)
|
|
{
|
|
GstCapsFeatures *features;
|
|
GstCaps *caps;
|
|
|
|
caps = gst_vaapi_video_format_new_template_caps (format);
|
|
if (!caps)
|
|
return NULL;
|
|
|
|
features = gst_caps_features_new (features_string, NULL);
|
|
if (!features) {
|
|
gst_caps_unref (caps);
|
|
return NULL;
|
|
}
|
|
gst_caps_set_features (caps, 0, features);
|
|
return caps;
|
|
}
|
|
|
|
static GstVideoFormat
|
|
gst_vaapi_find_preferred_format (const GValue * format_list,
|
|
GstVideoFormat native_format)
|
|
{
|
|
const GValue *frmt;
|
|
GstVideoFormat out_format;
|
|
guint i;
|
|
|
|
/* if one format, that is the one */
|
|
if (G_VALUE_HOLDS_STRING (format_list))
|
|
return gst_video_format_from_string (g_value_get_string (format_list));
|
|
|
|
if (!GST_VALUE_HOLDS_LIST (format_list)) {
|
|
GST_ERROR ("negotiated caps do not have a valid format");
|
|
return GST_VIDEO_FORMAT_UNKNOWN;
|
|
}
|
|
|
|
if (native_format == GST_VIDEO_FORMAT_UNKNOWN
|
|
|| native_format == GST_VIDEO_FORMAT_ENCODED) {
|
|
native_format = GST_VIDEO_FORMAT_NV12; /* default VA format */
|
|
}
|
|
|
|
/* search our native format in the list */
|
|
for (i = 0; i < gst_value_list_get_size (format_list); i++) {
|
|
frmt = gst_value_list_get_value (format_list, i);
|
|
out_format = gst_video_format_from_string (g_value_get_string (frmt));
|
|
|
|
/* GStreamer do not handle encoded formats nicely. Try the next
|
|
* one. */
|
|
if (out_format == GST_VIDEO_FORMAT_ENCODED)
|
|
continue;
|
|
|
|
if (native_format == out_format)
|
|
return out_format;
|
|
}
|
|
|
|
/* just pick the first valid format in the list */
|
|
i = 0;
|
|
do {
|
|
frmt = gst_value_list_get_value (format_list, i++);
|
|
out_format = gst_video_format_from_string (g_value_get_string (frmt));
|
|
} while (out_format == GST_VIDEO_FORMAT_ENCODED);
|
|
|
|
return out_format;
|
|
}
|
|
|
|
GstVaapiCapsFeature
|
|
gst_vaapi_find_preferred_caps_feature (GstPad * pad, GstCaps * allowed_caps,
|
|
GstVideoFormat * out_format_ptr)
|
|
{
|
|
GstVaapiCapsFeature feature = GST_VAAPI_CAPS_FEATURE_NOT_NEGOTIATED;
|
|
guint i, j, num_structures;
|
|
GstCaps *peer_caps, *out_caps = NULL, *caps = NULL;
|
|
static const guint feature_list[] = { GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE,
|
|
GST_VAAPI_CAPS_FEATURE_DMABUF,
|
|
GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META,
|
|
GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY,
|
|
};
|
|
|
|
/* query with no filter */
|
|
peer_caps = gst_pad_peer_query_caps (pad, NULL);
|
|
if (!peer_caps)
|
|
goto cleanup;
|
|
if (gst_caps_is_empty (peer_caps))
|
|
goto cleanup;
|
|
|
|
/* filter against our allowed caps */
|
|
out_caps = gst_caps_intersect_full (allowed_caps, peer_caps,
|
|
GST_CAPS_INTERSECT_FIRST);
|
|
|
|
/* default feature */
|
|
feature = GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY;
|
|
|
|
/* if downstream requests caps ANY, system memory is preferred */
|
|
if (gst_caps_is_any (peer_caps))
|
|
goto find_format;
|
|
|
|
num_structures = gst_caps_get_size (out_caps);
|
|
for (i = 0; i < num_structures; i++) {
|
|
GstCapsFeatures *const features = gst_caps_get_features (out_caps, i);
|
|
GstStructure *const structure = gst_caps_get_structure (out_caps, i);
|
|
|
|
/* Skip ANY features, we need an exact match for correct evaluation */
|
|
if (gst_caps_features_is_any (features))
|
|
continue;
|
|
|
|
gst_caps_replace (&caps, NULL);
|
|
caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
|
|
if (!caps)
|
|
continue;
|
|
gst_caps_set_features (caps, 0, gst_caps_features_copy (features));
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (feature_list); j++) {
|
|
if (gst_vaapi_caps_feature_contains (caps, feature_list[j])
|
|
&& feature < feature_list[j]) {
|
|
feature = feature_list[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Stop at the first match, the caps should already be sorted out
|
|
by preference order from downstream elements */
|
|
if (feature != GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY)
|
|
break;
|
|
}
|
|
|
|
if (!caps)
|
|
goto cleanup;
|
|
|
|
find_format:
|
|
if (out_format_ptr) {
|
|
GstVideoFormat out_format;
|
|
GstStructure *structure = NULL;
|
|
const GValue *format_list;
|
|
GstCapsFeatures *features;
|
|
|
|
/* if the best feature is SystemMemory, we should choose the
|
|
* vidoe/x-raw caps in the filtered peer caps set. If not, use
|
|
* the first caps, which is the preferred by downstream. */
|
|
if (feature == GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY) {
|
|
gst_caps_replace (&caps, out_caps);
|
|
num_structures = gst_caps_get_size (caps);
|
|
for (i = 0; i < num_structures; i++) {
|
|
structure = gst_caps_get_structure (caps, i);
|
|
features = gst_caps_get_features (caps, i);
|
|
if (!gst_caps_features_is_any (features)
|
|
&& gst_caps_features_contains (features,
|
|
gst_vaapi_caps_feature_to_string
|
|
(GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY)))
|
|
break;
|
|
}
|
|
} else {
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
}
|
|
if (!structure)
|
|
goto cleanup;
|
|
format_list = gst_structure_get_value (structure, "format");
|
|
if (!format_list)
|
|
goto cleanup;
|
|
out_format = gst_vaapi_find_preferred_format (format_list, *out_format_ptr);
|
|
if (out_format == GST_VIDEO_FORMAT_UNKNOWN)
|
|
goto cleanup;
|
|
|
|
*out_format_ptr = out_format;
|
|
}
|
|
|
|
cleanup:
|
|
gst_caps_replace (&caps, NULL);
|
|
gst_caps_replace (&out_caps, NULL);
|
|
gst_caps_replace (&peer_caps, NULL);
|
|
return feature;
|
|
}
|
|
|
|
const gchar *
|
|
gst_vaapi_caps_feature_to_string (GstVaapiCapsFeature feature)
|
|
{
|
|
const gchar *str;
|
|
|
|
switch (feature) {
|
|
case GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY:
|
|
str = GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY;
|
|
break;
|
|
case GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META:
|
|
str = GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META;
|
|
break;
|
|
case GST_VAAPI_CAPS_FEATURE_DMABUF:
|
|
str = GST_CAPS_FEATURE_MEMORY_DMABUF;
|
|
break;
|
|
case GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE:
|
|
str = GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE;
|
|
break;
|
|
default:
|
|
str = NULL;
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
gboolean
|
|
gst_caps_set_interlaced (GstCaps * caps, GstVideoInfo * vip)
|
|
{
|
|
GstVideoInterlaceMode mode;
|
|
const gchar *mode_str;
|
|
|
|
mode = vip ? GST_VIDEO_INFO_INTERLACE_MODE (vip) :
|
|
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
|
|
switch (mode) {
|
|
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
|
|
mode_str = "progressive";
|
|
break;
|
|
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
|
|
mode_str = "interleaved";
|
|
break;
|
|
case GST_VIDEO_INTERLACE_MODE_MIXED:
|
|
mode_str = "mixed";
|
|
break;
|
|
default:
|
|
GST_ERROR ("unsupported `interlace-mode' %d", mode);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, mode_str, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < gst_caps_get_size (caps); i++) {
|
|
GstCapsFeatures *const features = gst_caps_get_features (caps, i);
|
|
/* Skip ANY features, we need an exact match for correct evaluation */
|
|
if (gst_caps_features_is_any (features))
|
|
continue;
|
|
if (gst_caps_features_contains (features, feature))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_vaapi_caps_feature_contains (const GstCaps * caps,
|
|
GstVaapiCapsFeature feature)
|
|
{
|
|
const gchar *feature_str;
|
|
|
|
g_return_val_if_fail (caps != NULL, FALSE);
|
|
|
|
feature_str = gst_vaapi_caps_feature_to_string (feature);
|
|
if (!feature_str)
|
|
return FALSE;
|
|
|
|
return _gst_caps_has_feature (caps, feature_str);
|
|
}
|
|
|
|
/* Checks whether the supplied caps contain VA surfaces */
|
|
gboolean
|
|
gst_caps_has_vaapi_surface (GstCaps * caps)
|
|
{
|
|
g_return_val_if_fail (caps != NULL, FALSE);
|
|
|
|
return _gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE);
|
|
}
|
|
|
|
gboolean
|
|
gst_caps_is_video_raw (GstCaps * caps)
|
|
{
|
|
GstStructure *structure;
|
|
|
|
g_return_val_if_fail (caps != NULL, FALSE);
|
|
|
|
if (!gst_caps_is_fixed (caps))
|
|
return FALSE;
|
|
if (!_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY))
|
|
return FALSE;
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
return gst_structure_has_name (structure, "video/x-raw");
|
|
}
|
|
|
|
void
|
|
gst_video_info_change_format (GstVideoInfo * vip, GstVideoFormat format,
|
|
guint width, guint height)
|
|
{
|
|
GstVideoInfo vi = *vip;
|
|
|
|
gst_video_info_set_format (vip, format, width, height);
|
|
|
|
GST_VIDEO_INFO_INTERLACE_MODE (vip) = GST_VIDEO_INFO_INTERLACE_MODE (&vi);
|
|
GST_VIDEO_FORMAT_INFO_FLAGS (vip) = GST_VIDEO_FORMAT_INFO_FLAGS (&vi);
|
|
GST_VIDEO_INFO_VIEWS (vip) = GST_VIDEO_INFO_VIEWS (&vi);
|
|
GST_VIDEO_INFO_PAR_N (vip) = GST_VIDEO_INFO_PAR_N (&vi);
|
|
GST_VIDEO_INFO_PAR_D (vip) = GST_VIDEO_INFO_PAR_D (&vi);
|
|
GST_VIDEO_INFO_FPS_N (vip) = GST_VIDEO_INFO_FPS_N (&vi);
|
|
GST_VIDEO_INFO_FPS_D (vip) = GST_VIDEO_INFO_FPS_D (&vi);
|
|
GST_VIDEO_INFO_MULTIVIEW_MODE (vip) = GST_VIDEO_INFO_MULTIVIEW_MODE (&vi);
|
|
GST_VIDEO_INFO_MULTIVIEW_FLAGS (vip) = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vi);
|
|
}
|
|
|
|
/**
|
|
* gst_video_info_changed:
|
|
* @old: old #GstVideoInfo
|
|
* @new: new #GstVideoInfo
|
|
*
|
|
* Compares @old and @new
|
|
*
|
|
* Returns: %TRUE if @old has different format/width/height than
|
|
* @new. Otherwise, %FALSE.
|
|
**/
|
|
gboolean
|
|
gst_video_info_changed (const GstVideoInfo * old, const GstVideoInfo * new)
|
|
{
|
|
if (GST_VIDEO_INFO_FORMAT (old) != GST_VIDEO_INFO_FORMAT (new))
|
|
return TRUE;
|
|
if (GST_VIDEO_INFO_WIDTH (old) != GST_VIDEO_INFO_WIDTH (new))
|
|
return TRUE;
|
|
if (GST_VIDEO_INFO_HEIGHT (old) != GST_VIDEO_INFO_HEIGHT (new))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_video_info_force_nv12_if_encoded:
|
|
* @vinfo: a #GstVideoInfo
|
|
*
|
|
* If the format of @vinfo is %GST_VIDEO_FORMAT_ENCODED it is changed
|
|
* to %GST_VIDEO_FORMAT_NV12.
|
|
**/
|
|
void
|
|
gst_video_info_force_nv12_if_encoded (GstVideoInfo * vinfo)
|
|
{
|
|
if (GST_VIDEO_INFO_FORMAT (vinfo) != GST_VIDEO_FORMAT_ENCODED)
|
|
return;
|
|
gst_video_info_set_format (vinfo, GST_VIDEO_FORMAT_NV12,
|
|
GST_VIDEO_INFO_WIDTH (vinfo), GST_VIDEO_INFO_HEIGHT (vinfo));
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_create_test_display:
|
|
*
|
|
* Creates a temporal #GstVaapiDisplay instance, just for testing the
|
|
* supported features.
|
|
*
|
|
* Returns: a new #GstVaapiDisplay instances. Free with
|
|
* gst_object_unref () after use. Or %NULL if no VA display is
|
|
* available.
|
|
**/
|
|
GstVaapiDisplay *
|
|
gst_vaapi_create_test_display (void)
|
|
{
|
|
guint i;
|
|
GstVaapiDisplay *display = NULL;
|
|
const GstVaapiDisplayType test_display_map[] = {
|
|
#if USE_DRM
|
|
GST_VAAPI_DISPLAY_TYPE_DRM,
|
|
#endif
|
|
#if USE_WAYLAND
|
|
GST_VAAPI_DISPLAY_TYPE_WAYLAND,
|
|
#endif
|
|
#if USE_X11
|
|
GST_VAAPI_DISPLAY_TYPE_X11,
|
|
#endif
|
|
};
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (test_display_map); i++) {
|
|
display = gst_vaapi_create_display (test_display_map[i], NULL);
|
|
if (display)
|
|
break;
|
|
}
|
|
return display;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_driver_is_whitelisted:
|
|
* @display: a #GstVaapiDisplay
|
|
*
|
|
* Looks the VA-API driver vendors in an internal white-list.
|
|
*
|
|
* Returns: %TRUE if driver is in the white-list, otherwise %FALSE
|
|
**/
|
|
gboolean
|
|
gst_vaapi_driver_is_whitelisted (GstVaapiDisplay * display)
|
|
{
|
|
const gchar *vendor;
|
|
guint i;
|
|
static const gchar *whitelist[] = {
|
|
"Intel i965 driver",
|
|
"mesa gallium",
|
|
NULL
|
|
};
|
|
|
|
g_return_val_if_fail (display, FALSE);
|
|
|
|
if (g_getenv (GST_VAAPI_ALL_DRIVERS_ENV))
|
|
return TRUE;
|
|
|
|
vendor = gst_vaapi_display_get_vendor_string (display);
|
|
if (!vendor)
|
|
goto no_vendor;
|
|
|
|
for (i = 0; whitelist[i]; i++) {
|
|
if (g_ascii_strncasecmp (vendor, whitelist[i], strlen (whitelist[i])) == 0)
|
|
return TRUE;
|
|
}
|
|
|
|
GST_WARNING ("Unsupported VA driver: %s. Export environment variable "
|
|
GST_VAAPI_ALL_DRIVERS_ENV " to bypass", vendor);
|
|
return FALSE;
|
|
|
|
/* ERRORS */
|
|
no_vendor:
|
|
{
|
|
GST_WARNING ("no VA-API driver vendor description");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_codecs_has_codec:
|
|
* @codecs: a #GArray of #GstVaapiCodec
|
|
* @codec: a #GstVaapiCodec to find in @codec
|
|
*
|
|
* Search in the available @codecs for the specific @codec.
|
|
*
|
|
* Returns: %TRUE if @codec is in @codecs
|
|
**/
|
|
gboolean
|
|
gst_vaapi_codecs_has_codec (GArray * codecs, GstVaapiCodec codec)
|
|
{
|
|
guint i;
|
|
GstVaapiCodec c;
|
|
|
|
g_return_val_if_fail (codec, FALSE);
|
|
|
|
for (i = 0; i < codecs->len; i++) {
|
|
c = g_array_index (codecs, GstVaapiCodec, i);
|
|
if (c == codec)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|