mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
plugins: re-indent common and video context creation utils.
This commit is contained in:
parent
c5581298fb
commit
b80257389d
4 changed files with 373 additions and 385 deletions
|
@ -41,364 +41,354 @@
|
||||||
|
|
||||||
/* Preferred first */
|
/* Preferred first */
|
||||||
static const char *display_types[] = {
|
static const char *display_types[] = {
|
||||||
"gst-vaapi-display",
|
"gst-vaapi-display",
|
||||||
"vaapi-display",
|
"vaapi-display",
|
||||||
#if USE_WAYLAND
|
#if USE_WAYLAND
|
||||||
"wl-display",
|
"wl-display",
|
||||||
"wl-display-name",
|
"wl-display-name",
|
||||||
#endif
|
#endif
|
||||||
#if USE_X11
|
#if USE_X11
|
||||||
"x11-display",
|
"x11-display",
|
||||||
"x11-display-name",
|
"x11-display-name",
|
||||||
#endif
|
#endif
|
||||||
#if USE_DRM
|
#if USE_DRM
|
||||||
"drm-device",
|
"drm-device",
|
||||||
"drm-device-path",
|
"drm-device-path",
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
const gchar *type_str;
|
{
|
||||||
GstVaapiDisplayType type;
|
const gchar *type_str;
|
||||||
GstVaapiDisplay * (*create_display)(const gchar *);
|
GstVaapiDisplayType type;
|
||||||
|
GstVaapiDisplay *(*create_display) (const gchar *);
|
||||||
} DisplayMap;
|
} DisplayMap;
|
||||||
|
|
||||||
static const DisplayMap g_display_map[] = {
|
static const DisplayMap g_display_map[] = {
|
||||||
#if USE_WAYLAND
|
#if USE_WAYLAND
|
||||||
{ "wayland",
|
{"wayland",
|
||||||
GST_VAAPI_DISPLAY_TYPE_WAYLAND,
|
GST_VAAPI_DISPLAY_TYPE_WAYLAND,
|
||||||
gst_vaapi_display_wayland_new },
|
gst_vaapi_display_wayland_new},
|
||||||
#endif
|
#endif
|
||||||
#if USE_GLX
|
#if USE_GLX
|
||||||
{ "glx",
|
{"glx",
|
||||||
GST_VAAPI_DISPLAY_TYPE_GLX,
|
GST_VAAPI_DISPLAY_TYPE_GLX,
|
||||||
gst_vaapi_display_glx_new },
|
gst_vaapi_display_glx_new},
|
||||||
#endif
|
#endif
|
||||||
#if USE_X11
|
#if USE_X11
|
||||||
{ "x11",
|
{"x11",
|
||||||
GST_VAAPI_DISPLAY_TYPE_X11,
|
GST_VAAPI_DISPLAY_TYPE_X11,
|
||||||
gst_vaapi_display_x11_new },
|
gst_vaapi_display_x11_new},
|
||||||
#endif
|
#endif
|
||||||
#if USE_DRM
|
#if USE_DRM
|
||||||
{ "drm",
|
{"drm",
|
||||||
GST_VAAPI_DISPLAY_TYPE_DRM,
|
GST_VAAPI_DISPLAY_TYPE_DRM,
|
||||||
gst_vaapi_display_drm_new },
|
gst_vaapi_display_drm_new},
|
||||||
#endif
|
#endif
|
||||||
{ NULL, }
|
{NULL,}
|
||||||
};
|
};
|
||||||
|
|
||||||
static GstVaapiDisplay *
|
static GstVaapiDisplay *
|
||||||
gst_vaapi_create_display(GstVaapiDisplayType display_type)
|
gst_vaapi_create_display (GstVaapiDisplayType display_type)
|
||||||
{
|
{
|
||||||
GstVaapiDisplay *display = NULL;
|
GstVaapiDisplay *display = NULL;
|
||||||
const DisplayMap *m;
|
const DisplayMap *m;
|
||||||
|
|
||||||
for (m = g_display_map; m->type_str != NULL; m++) {
|
for (m = g_display_map; m->type_str != NULL; m++) {
|
||||||
if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY &&
|
if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY && display_type != m->type)
|
||||||
display_type != m->type)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
display = m->create_display(NULL);
|
display = m->create_display (NULL);
|
||||||
if (display || display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
|
if (display || display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_ensure_display(gpointer element, GstVaapiDisplayType type)
|
gst_vaapi_ensure_display (gpointer element, GstVaapiDisplayType type)
|
||||||
{
|
{
|
||||||
GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(element);
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
|
||||||
GstVaapiDisplay *display;
|
GstVaapiDisplay *display;
|
||||||
GstVideoContext *context;
|
GstVideoContext *context;
|
||||||
|
|
||||||
g_return_val_if_fail(GST_IS_VIDEO_CONTEXT(element), FALSE);
|
g_return_val_if_fail (GST_IS_VIDEO_CONTEXT (element), FALSE);
|
||||||
|
|
||||||
context = GST_VIDEO_CONTEXT(element);
|
context = GST_VIDEO_CONTEXT (element);
|
||||||
g_return_val_if_fail(context != NULL, FALSE);
|
g_return_val_if_fail (context != NULL, FALSE);
|
||||||
|
|
||||||
gst_vaapi_video_context_prepare(context, display_types);
|
gst_vaapi_video_context_prepare (context, display_types);
|
||||||
|
|
||||||
/* Neighbour found and it updated the display */
|
/* Neighbour found and it updated the display */
|
||||||
if (plugin->display && gst_vaapi_display_type_is_compatible(
|
if (plugin->display
|
||||||
plugin->display_type, type))
|
&& gst_vaapi_display_type_is_compatible (plugin->display_type, type))
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* If no neighboor, or application not interested, use system default */
|
|
||||||
display = gst_vaapi_create_display(type);
|
|
||||||
if (!display)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gst_vaapi_video_context_propagate(context, display);
|
|
||||||
GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE(plugin, display);
|
|
||||||
gst_vaapi_display_unref(display);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
/* If no neighboor, or application not interested, use system default */
|
||||||
|
display = gst_vaapi_create_display (type);
|
||||||
|
if (!display)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gst_vaapi_video_context_propagate (context, display);
|
||||||
|
GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE (plugin, display);
|
||||||
|
gst_vaapi_display_unref (display);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_vaapi_set_display(
|
gst_vaapi_set_display (const gchar * type,
|
||||||
const gchar *type,
|
const GValue * value, GstVaapiDisplay ** display_ptr)
|
||||||
const GValue *value,
|
|
||||||
GstVaapiDisplay **display
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
GstVaapiDisplay *dpy = NULL;
|
GstVaapiDisplay *display = NULL;
|
||||||
|
|
||||||
if (!strcmp(type, "vaapi-display")) {
|
if (!strcmp (type, "vaapi-display")) {
|
||||||
g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
|
g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
|
||||||
dpy = gst_vaapi_display_new_with_display(g_value_get_pointer(value));
|
display = gst_vaapi_display_new_with_display (g_value_get_pointer (value));
|
||||||
}
|
} else if (!strcmp (type, "gst-vaapi-display")) {
|
||||||
else if (!strcmp(type, "gst-vaapi-display")) {
|
g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
|
||||||
g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
|
display = gst_vaapi_display_ref (g_value_get_pointer (value));
|
||||||
dpy = gst_vaapi_display_ref(g_value_get_pointer(value));
|
}
|
||||||
}
|
|
||||||
#if USE_DRM
|
#if USE_DRM
|
||||||
else if (!strcmp(type, "drm-device")) {
|
else if (!strcmp (type, "drm-device")) {
|
||||||
gint device;
|
gint device;
|
||||||
g_return_if_fail(G_VALUE_HOLDS_INT(value));
|
g_return_if_fail (G_VALUE_HOLDS_INT (value));
|
||||||
device = g_value_get_int(value);
|
device = g_value_get_int (value);
|
||||||
dpy = gst_vaapi_display_drm_new_with_device(device);
|
display = gst_vaapi_display_drm_new_with_device (device);
|
||||||
}
|
} else if (!strcmp (type, "drm-device-path")) {
|
||||||
else if (!strcmp(type, "drm-device-path")) {
|
const gchar *device_path;
|
||||||
const gchar *device_path;
|
g_return_if_fail (G_VALUE_HOLDS_STRING (value));
|
||||||
g_return_if_fail(G_VALUE_HOLDS_STRING(value));
|
device_path = g_value_get_string (value);
|
||||||
device_path = g_value_get_string(value);
|
display = gst_vaapi_display_drm_new (device_path);
|
||||||
dpy = gst_vaapi_display_drm_new(device_path);
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#if USE_X11
|
#if USE_X11
|
||||||
else if (!strcmp(type, "x11-display-name")) {
|
else if (!strcmp (type, "x11-display-name")) {
|
||||||
g_return_if_fail(G_VALUE_HOLDS_STRING(value));
|
g_return_if_fail (G_VALUE_HOLDS_STRING (value));
|
||||||
#if USE_GLX
|
#if USE_GLX
|
||||||
dpy = gst_vaapi_display_glx_new(g_value_get_string(value));
|
display = gst_vaapi_display_glx_new (g_value_get_string (value));
|
||||||
#endif
|
#endif
|
||||||
if (!dpy)
|
if (!display)
|
||||||
dpy = gst_vaapi_display_x11_new(g_value_get_string(value));
|
display = gst_vaapi_display_x11_new (g_value_get_string (value));
|
||||||
}
|
} else if (!strcmp (type, "x11-display")) {
|
||||||
else if (!strcmp(type, "x11-display")) {
|
g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
|
||||||
g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
|
|
||||||
#if USE_GLX
|
#if USE_GLX
|
||||||
dpy = gst_vaapi_display_glx_new_with_display(g_value_get_pointer(value));
|
display =
|
||||||
|
gst_vaapi_display_glx_new_with_display (g_value_get_pointer (value));
|
||||||
#endif
|
#endif
|
||||||
if (!dpy)
|
if (!display)
|
||||||
dpy = gst_vaapi_display_x11_new_with_display(g_value_get_pointer(value));
|
display =
|
||||||
}
|
gst_vaapi_display_x11_new_with_display (g_value_get_pointer (value));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if USE_WAYLAND
|
#if USE_WAYLAND
|
||||||
else if (!strcmp(type, "wl-display")) {
|
else if (!strcmp (type, "wl-display")) {
|
||||||
struct wl_display *wl_display;
|
struct wl_display *wl_display;
|
||||||
g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
|
g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
|
||||||
wl_display = g_value_get_pointer(value);
|
wl_display = g_value_get_pointer (value);
|
||||||
dpy = gst_vaapi_display_wayland_new_with_display(wl_display);
|
display = gst_vaapi_display_wayland_new_with_display (wl_display);
|
||||||
}
|
} else if (!strcmp (type, "wl-display-name")) {
|
||||||
else if (!strcmp(type, "wl-display-name")) {
|
const gchar *display_name;
|
||||||
const gchar *display_name;
|
g_return_if_fail (G_VALUE_HOLDS_STRING (value));
|
||||||
g_return_if_fail(G_VALUE_HOLDS_STRING(value));
|
display_name = g_value_get_string (value);
|
||||||
display_name = g_value_get_string(value);
|
display = gst_vaapi_display_wayland_new (display_name);
|
||||||
dpy = gst_vaapi_display_wayland_new(display_name);
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (dpy) {
|
if (display) {
|
||||||
gst_vaapi_display_replace(display, dpy);
|
gst_vaapi_display_replace (display_ptr, display);
|
||||||
gst_vaapi_display_unref(dpy);
|
gst_vaapi_display_unref (display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_reply_to_query(GstQuery *query, GstVaapiDisplay *display)
|
gst_vaapi_reply_to_query (GstQuery * query, GstVaapiDisplay * display)
|
||||||
{
|
{
|
||||||
#if GST_CHECK_VERSION(1,1,0)
|
#if GST_CHECK_VERSION(1,1,0)
|
||||||
const gchar *type = NULL;
|
const gchar *type = NULL;
|
||||||
GstContext *context;
|
GstContext *context;
|
||||||
|
|
||||||
if (GST_QUERY_TYPE(query) != GST_QUERY_CONTEXT)
|
if (GST_QUERY_TYPE (query) != GST_QUERY_CONTEXT)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!display)
|
if (!display)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!gst_query_parse_context_type(query, &type))
|
if (!gst_query_parse_context_type (query, &type))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (g_strcmp0(type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
|
if (g_strcmp0 (type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
context = gst_vaapi_video_context_new_with_display(display, FALSE);
|
context = gst_vaapi_video_context_new_with_display (display, FALSE);
|
||||||
gst_query_set_context(query, context);
|
gst_query_set_context (query, context);
|
||||||
gst_context_unref(context);
|
gst_context_unref (context);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
#else
|
#else
|
||||||
GstVaapiDisplayType display_type;
|
GstVaapiDisplayType display_type;
|
||||||
const gchar **types;
|
const gchar **types;
|
||||||
const gchar *type;
|
const gchar *type;
|
||||||
gint i;
|
gint i;
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
|
|
||||||
if (GST_QUERY_TYPE(query) != GST_QUERY_CUSTOM)
|
if (GST_QUERY_TYPE (query) != GST_QUERY_CUSTOM)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!display)
|
if (!display)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
types = gst_video_context_query_get_supported_types(query);
|
types = gst_video_context_query_get_supported_types (query);
|
||||||
|
|
||||||
if (!types)
|
if (!types)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
display_type = gst_vaapi_display_get_display_type(display);
|
display_type = gst_vaapi_display_get_display_type (display);
|
||||||
for (i = 0; types[i] && !res; i++) {
|
for (i = 0; types[i] && !res; i++) {
|
||||||
type = types[i];
|
type = types[i];
|
||||||
|
|
||||||
res = TRUE;
|
res = TRUE;
|
||||||
if (!strcmp(type, "gst-vaapi-display")) {
|
if (!strcmp (type, "gst-vaapi-display")) {
|
||||||
gst_video_context_query_set_pointer(query, type, display);
|
gst_video_context_query_set_pointer (query, type, display);
|
||||||
}
|
} else if (!strcmp (type, "vaapi-display")) {
|
||||||
else if (!strcmp(type, "vaapi-display")) {
|
VADisplay vadpy = gst_vaapi_display_get_display (display);
|
||||||
VADisplay vadpy = gst_vaapi_display_get_display(display);
|
gst_video_context_query_set_pointer (query, type, vadpy);
|
||||||
gst_video_context_query_set_pointer(query, type, vadpy);
|
} else {
|
||||||
}
|
switch (display_type) {
|
||||||
else {
|
|
||||||
switch (display_type) {
|
|
||||||
#if USE_DRM
|
#if USE_DRM
|
||||||
case GST_VAAPI_DISPLAY_TYPE_DRM: {
|
case GST_VAAPI_DISPLAY_TYPE_DRM:{
|
||||||
GstVaapiDisplayDRM * const drm_dpy =
|
GstVaapiDisplayDRM *const drm_dpy = GST_VAAPI_DISPLAY_DRM (display);
|
||||||
GST_VAAPI_DISPLAY_DRM(display);
|
if (!strcmp (type, "drm-device-path"))
|
||||||
if (!strcmp(type, "drm-device-path"))
|
gst_video_context_query_set_string (query, type,
|
||||||
gst_video_context_query_set_string(query, type,
|
gst_vaapi_display_drm_get_device_path (drm_dpy));
|
||||||
gst_vaapi_display_drm_get_device_path(drm_dpy));
|
|
||||||
#if 0
|
#if 0
|
||||||
/* XXX: gst_video_context_query_set_int() does not exist yet */
|
/* XXX: gst_video_context_query_set_int() does not exist yet */
|
||||||
else if (!strcmp(type, "drm-device"))
|
else if (!strcmp (type, "drm-device"))
|
||||||
gst_video_context_query_set_int(query, type,
|
gst_video_context_query_set_int (query, type,
|
||||||
gst_vaapi_display_drm_get_device(drm_dpy));
|
gst_vaapi_display_drm_get_device (drm_dpy));
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if USE_X11
|
#if USE_X11
|
||||||
#if USE_GLX
|
#if USE_GLX
|
||||||
case GST_VAAPI_DISPLAY_TYPE_GLX:
|
case GST_VAAPI_DISPLAY_TYPE_GLX:
|
||||||
#endif
|
#endif
|
||||||
case GST_VAAPI_DISPLAY_TYPE_X11: {
|
case GST_VAAPI_DISPLAY_TYPE_X11:{
|
||||||
GstVaapiDisplayX11 * const xvadpy =
|
GstVaapiDisplayX11 *const xvadpy = GST_VAAPI_DISPLAY_X11 (display);
|
||||||
GST_VAAPI_DISPLAY_X11(display);
|
Display *const x11dpy = gst_vaapi_display_x11_get_display (xvadpy);
|
||||||
Display * const x11dpy =
|
if (!strcmp (type, "x11-display"))
|
||||||
gst_vaapi_display_x11_get_display(xvadpy);
|
gst_video_context_query_set_pointer (query, type, x11dpy);
|
||||||
if (!strcmp(type, "x11-display"))
|
else if (!strcmp (type, "x11-display-name"))
|
||||||
gst_video_context_query_set_pointer(query, type, x11dpy);
|
gst_video_context_query_set_string (query, type,
|
||||||
else if (!strcmp(type, "x11-display-name"))
|
DisplayString (x11dpy));
|
||||||
gst_video_context_query_set_string(query, type,
|
else
|
||||||
DisplayString(x11dpy));
|
res = FALSE;
|
||||||
else
|
break;
|
||||||
res = FALSE;
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#if USE_WAYLAND
|
#if USE_WAYLAND
|
||||||
case GST_VAAPI_DISPLAY_TYPE_WAYLAND: {
|
case GST_VAAPI_DISPLAY_TYPE_WAYLAND:{
|
||||||
GstVaapiDisplayWayland * const wlvadpy =
|
GstVaapiDisplayWayland *const wlvadpy =
|
||||||
GST_VAAPI_DISPLAY_WAYLAND(display);
|
GST_VAAPI_DISPLAY_WAYLAND (display);
|
||||||
struct wl_display * const wldpy =
|
struct wl_display *const wldpy =
|
||||||
gst_vaapi_display_wayland_get_display(wlvadpy);
|
gst_vaapi_display_wayland_get_display (wlvadpy);
|
||||||
if (!strcmp(type, "wl-display"))
|
if (!strcmp (type, "wl-display"))
|
||||||
gst_video_context_query_set_pointer(query, type, wldpy);
|
gst_video_context_query_set_pointer (query, type, wldpy);
|
||||||
else
|
else
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
}
|
||||||
|
return res;
|
||||||
#endif /* !GST_CHECK_VERSION(1,1,0) */
|
#endif /* !GST_CHECK_VERSION(1,1,0) */
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_append_surface_caps(GstCaps *out_caps, GstCaps *in_caps)
|
gst_vaapi_append_surface_caps (GstCaps * out_caps, GstCaps * in_caps)
|
||||||
{
|
{
|
||||||
GstStructure *structure;
|
GstStructure *structure;
|
||||||
const GValue *v_width, *v_height, *v_framerate, *v_par;
|
const GValue *v_width, *v_height, *v_framerate, *v_par;
|
||||||
guint i, n_structures;
|
guint i, n_structures;
|
||||||
|
|
||||||
structure = gst_caps_get_structure(in_caps, 0);
|
structure = gst_caps_get_structure (in_caps, 0);
|
||||||
v_width = gst_structure_get_value(structure, "width");
|
v_width = gst_structure_get_value (structure, "width");
|
||||||
v_height = gst_structure_get_value(structure, "height");
|
v_height = gst_structure_get_value (structure, "height");
|
||||||
v_framerate = gst_structure_get_value(structure, "framerate");
|
v_framerate = gst_structure_get_value (structure, "framerate");
|
||||||
v_par = gst_structure_get_value(structure, "pixel-aspect-ratio");
|
v_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
|
||||||
if (!v_width || !v_height)
|
if (!v_width || !v_height)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
n_structures = gst_caps_get_size(out_caps);
|
n_structures = gst_caps_get_size (out_caps);
|
||||||
for (i = 0; i < n_structures; i++) {
|
for (i = 0; i < n_structures; i++) {
|
||||||
structure = gst_caps_get_structure(out_caps, i);
|
structure = gst_caps_get_structure (out_caps, i);
|
||||||
gst_structure_set_value(structure, "width", v_width);
|
gst_structure_set_value (structure, "width", v_width);
|
||||||
gst_structure_set_value(structure, "height", v_height);
|
gst_structure_set_value (structure, "height", v_height);
|
||||||
if (v_framerate)
|
if (v_framerate)
|
||||||
gst_structure_set_value(structure, "framerate", v_framerate);
|
gst_structure_set_value (structure, "framerate", v_framerate);
|
||||||
if (v_par)
|
if (v_par)
|
||||||
gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
|
gst_structure_set_value (structure, "pixel-aspect-ratio", v_par);
|
||||||
}
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_vaapi_apply_composition (GstVaapiSurface * surface, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
#if GST_CHECK_VERSION(1,0,0)
|
||||||
|
GstVideoOverlayCompositionMeta *const cmeta =
|
||||||
|
gst_buffer_get_video_overlay_composition_meta (buffer);
|
||||||
|
GstVideoOverlayComposition *composition;
|
||||||
|
|
||||||
|
if (!cmeta)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
composition = cmeta->overlay;
|
||||||
|
|
||||||
gboolean
|
|
||||||
gst_vaapi_apply_composition(GstVaapiSurface *surface, GstBuffer *buffer)
|
|
||||||
{
|
|
||||||
#if GST_CHECK_VERSION(1,0,0)
|
|
||||||
GstVideoOverlayCompositionMeta * const cmeta =
|
|
||||||
gst_buffer_get_video_overlay_composition_meta(buffer);
|
|
||||||
GstVideoOverlayComposition *composition;
|
|
||||||
|
|
||||||
if (!cmeta)
|
|
||||||
return TRUE;
|
|
||||||
composition = cmeta->overlay;
|
|
||||||
#else
|
#else
|
||||||
GstVideoOverlayComposition * const composition =
|
GstVideoOverlayComposition *const composition =
|
||||||
gst_video_buffer_get_overlay_composition(buffer);
|
gst_video_buffer_get_overlay_composition (buffer);
|
||||||
#endif
|
#endif
|
||||||
if (!composition)
|
if (!composition)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
return gst_vaapi_surface_set_subpictures_from_composition(surface,
|
return gst_vaapi_surface_set_subpictures_from_composition (surface,
|
||||||
composition, TRUE);
|
composition, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_caps_set_interlaced(GstCaps *caps, GstVideoInfo *vip)
|
gst_caps_set_interlaced (GstCaps * caps, GstVideoInfo * vip)
|
||||||
{
|
{
|
||||||
#if GST_CHECK_VERSION(1,0,0)
|
#if GST_CHECK_VERSION(1,0,0)
|
||||||
GstVideoInterlaceMode mode;
|
GstVideoInterlaceMode mode;
|
||||||
const gchar *mode_str;
|
const gchar *mode_str;
|
||||||
|
|
||||||
mode = vip ? GST_VIDEO_INFO_INTERLACE_MODE(vip) :
|
mode = vip ? GST_VIDEO_INFO_INTERLACE_MODE (vip) :
|
||||||
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
|
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
|
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
|
||||||
mode_str = "progressive";
|
mode_str = "progressive";
|
||||||
break;
|
break;
|
||||||
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
|
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
|
||||||
mode_str = "interleaved";
|
mode_str = "interleaved";
|
||||||
break;
|
break;
|
||||||
case GST_VIDEO_INTERLACE_MODE_MIXED:
|
case GST_VIDEO_INTERLACE_MODE_MIXED:
|
||||||
mode_str = "mixed";
|
mode_str = "mixed";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GST_ERROR("unsupported `interlace-mode' %d", mode);
|
GST_ERROR ("unsupported `interlace-mode' %d", mode);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_caps_set_simple(caps, "interlace-mode", G_TYPE_STRING, mode_str, NULL);
|
gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, mode_str, NULL);
|
||||||
#else
|
#else
|
||||||
gst_caps_set_simple(caps, "interlaced", G_TYPE_BOOLEAN,
|
gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN,
|
||||||
vip ? GST_VIDEO_INFO_IS_INTERLACED(vip) : FALSE, NULL);
|
vip ? GST_VIDEO_INFO_IS_INTERLACED (vip) : FALSE, NULL);
|
||||||
#endif
|
#endif
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,27 +30,24 @@
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_ensure_display(gpointer element, GstVaapiDisplayType type);
|
gst_vaapi_ensure_display (gpointer element, GstVaapiDisplayType type);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
void
|
void
|
||||||
gst_vaapi_set_display(
|
gst_vaapi_set_display (const gchar * type,
|
||||||
const gchar *type,
|
const GValue * value, GstVaapiDisplay ** display_ptr);
|
||||||
const GValue *value,
|
|
||||||
GstVaapiDisplay **display
|
|
||||||
);
|
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_reply_to_query(GstQuery *query, GstVaapiDisplay *display);
|
gst_vaapi_reply_to_query (GstQuery * query, GstVaapiDisplay * display);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_append_surface_caps (GstCaps *out_caps, GstCaps *in_caps);
|
gst_vaapi_append_surface_caps (GstCaps * out_caps, GstCaps * in_caps);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_apply_composition(GstVaapiSurface *surface, GstBuffer *buffer);
|
gst_vaapi_apply_composition (GstVaapiSurface * surface, GstBuffer * buffer);
|
||||||
|
|
||||||
#ifndef G_PRIMITIVE_SWAP
|
#ifndef G_PRIMITIVE_SWAP
|
||||||
#define G_PRIMITIVE_SWAP(type, a, b) do { \
|
#define G_PRIMITIVE_SWAP(type, a, b) do { \
|
||||||
|
@ -73,6 +70,6 @@ gst_vaapi_apply_composition(GstVaapiSurface *surface, GstBuffer *buffer);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_caps_set_interlaced(GstCaps *caps, GstVideoInfo *vip);
|
gst_caps_set_interlaced (GstCaps * caps, GstVideoInfo * vip);
|
||||||
|
|
||||||
#endif /* GST_VAAPI_PLUGIN_UTIL_H */
|
#endif /* GST_VAAPI_PLUGIN_UTIL_H */
|
||||||
|
|
|
@ -28,154 +28,154 @@
|
||||||
|
|
||||||
#if GST_CHECK_VERSION(1,1,0)
|
#if GST_CHECK_VERSION(1,1,0)
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC(GST_CAT_CONTEXT);
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
|
||||||
|
|
||||||
#define GST_VAAPI_TYPE_DISPLAY \
|
#define GST_VAAPI_TYPE_DISPLAY \
|
||||||
gst_vaapi_display_get_type()
|
gst_vaapi_display_get_type()
|
||||||
|
|
||||||
GType
|
GType
|
||||||
gst_vaapi_display_get_type(void) G_GNUC_CONST;
|
gst_vaapi_display_get_type (void)
|
||||||
|
G_GNUC_CONST;
|
||||||
|
|
||||||
G_DEFINE_BOXED_TYPE(GstVaapiDisplay, gst_vaapi_display,
|
G_DEFINE_BOXED_TYPE (GstVaapiDisplay, gst_vaapi_display,
|
||||||
(GBoxedCopyFunc)gst_vaapi_display_ref,
|
(GBoxedCopyFunc) gst_vaapi_display_ref,
|
||||||
(GBoxedFreeFunc)gst_vaapi_display_unref)
|
(GBoxedFreeFunc) gst_vaapi_display_unref)
|
||||||
|
|
||||||
GstContext *
|
GstContext *gst_vaapi_video_context_new_with_display (GstVaapiDisplay *
|
||||||
gst_vaapi_video_context_new_with_display(GstVaapiDisplay *display,
|
display, gboolean persistent)
|
||||||
gboolean persistent)
|
|
||||||
{
|
{
|
||||||
GstContext *context;
|
GstContext *context;
|
||||||
GstStructure *structure;
|
GstStructure *structure;
|
||||||
|
|
||||||
context = gst_context_new(GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME, persistent);
|
context = gst_context_new (GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME, persistent);
|
||||||
structure = gst_context_writable_structure(context);
|
structure = gst_context_writable_structure (context);
|
||||||
gst_structure_set(structure, "display", GST_VAAPI_TYPE_DISPLAY,
|
gst_structure_set (structure, "display", GST_VAAPI_TYPE_DISPLAY,
|
||||||
display, NULL);
|
display, NULL);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_video_context_get_display(GstContext *context,
|
gst_vaapi_video_context_get_display (GstContext * context,
|
||||||
GstVaapiDisplay **display_ptr)
|
GstVaapiDisplay ** display_ptr)
|
||||||
{
|
{
|
||||||
const GstStructure *structure;
|
const GstStructure *structure;
|
||||||
|
|
||||||
g_return_val_if_fail(GST_IS_CONTEXT(context), FALSE);
|
g_return_val_if_fail (GST_IS_CONTEXT (context), FALSE);
|
||||||
g_return_val_if_fail(g_strcmp0(gst_context_get_context_type(context),
|
g_return_val_if_fail (g_strcmp0 (gst_context_get_context_type (context),
|
||||||
GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME) == 0, FALSE);
|
GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME) == 0, FALSE);
|
||||||
|
|
||||||
structure = gst_context_get_structure(context);
|
structure = gst_context_get_structure (context);
|
||||||
return gst_structure_get(structure, "display", GST_VAAPI_TYPE_DISPLAY,
|
return gst_structure_get (structure, "display", GST_VAAPI_TYPE_DISPLAY,
|
||||||
display_ptr, NULL);
|
display_ptr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
context_pad_query(const GValue *item, GValue *value, gpointer user_data)
|
context_pad_query (const GValue * item, GValue * value, gpointer user_data)
|
||||||
{
|
{
|
||||||
GstPad * const pad = g_value_get_object(item);
|
GstPad *const pad = g_value_get_object (item);
|
||||||
GstQuery * const query = user_data;
|
GstQuery *const query = user_data;
|
||||||
|
|
||||||
if (gst_pad_peer_query(pad, query)) {
|
if (gst_pad_peer_query (pad, query)) {
|
||||||
g_value_set_boolean(value, TRUE);
|
g_value_set_boolean (value, TRUE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_CAT_INFO_OBJECT(GST_CAT_CONTEXT, pad, "context pad peer query failed");
|
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "context pad peer query failed");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
run_context_query (GstElement * element, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstIteratorFoldFunction const func = context_pad_query;
|
||||||
|
GstIterator *it;
|
||||||
|
GValue res = { 0 };
|
||||||
|
|
||||||
|
g_value_init (&res, G_TYPE_BOOLEAN);
|
||||||
|
g_value_set_boolean (&res, FALSE);
|
||||||
|
|
||||||
|
/* Ask downstream neighbour */
|
||||||
|
it = gst_element_iterate_src_pads (element);
|
||||||
|
while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
|
||||||
|
gst_iterator_resync (it);
|
||||||
|
gst_iterator_free (it);
|
||||||
|
|
||||||
|
if (g_value_get_boolean (&res))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
/* If none, ask upstream neighbour (auto-plugged case) */
|
||||||
run_context_query(GstElement *element, GstQuery *query)
|
it = gst_element_iterate_sink_pads (element);
|
||||||
{
|
while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
|
||||||
GstIteratorFoldFunction const func = context_pad_query;
|
gst_iterator_resync (it);
|
||||||
GstIterator *it;
|
gst_iterator_free (it);
|
||||||
GValue res = { 0 };
|
|
||||||
|
|
||||||
g_value_init(&res, G_TYPE_BOOLEAN);
|
return g_value_get_boolean (&res);
|
||||||
g_value_set_boolean(&res, FALSE);
|
|
||||||
|
|
||||||
/* Ask downstream neighbour */
|
|
||||||
it = gst_element_iterate_src_pads(element);
|
|
||||||
while (gst_iterator_fold(it, func, &res, query) == GST_ITERATOR_RESYNC)
|
|
||||||
gst_iterator_resync(it);
|
|
||||||
gst_iterator_free(it);
|
|
||||||
|
|
||||||
if (g_value_get_boolean(&res))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* If none, ask upstream neighbour (auto-plugged case) */
|
|
||||||
it = gst_element_iterate_sink_pads(element);
|
|
||||||
while (gst_iterator_fold(it, func, &res, query) == GST_ITERATOR_RESYNC)
|
|
||||||
gst_iterator_resync(it);
|
|
||||||
gst_iterator_free(it);
|
|
||||||
|
|
||||||
return g_value_get_boolean(&res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_vaapi_video_context_prepare(GstElement *element, const gchar **types)
|
gst_vaapi_video_context_prepare (GstElement * element, const gchar ** types)
|
||||||
{
|
{
|
||||||
GstContext *context;
|
GstContext *context;
|
||||||
GstQuery *query;
|
GstQuery *query;
|
||||||
GstMessage *msg;
|
GstMessage *msg;
|
||||||
|
|
||||||
if (!GST_CAT_CONTEXT)
|
if (!GST_CAT_CONTEXT)
|
||||||
GST_DEBUG_CATEGORY_GET(GST_CAT_CONTEXT, "GST_CONTEXT");
|
GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
|
||||||
|
|
||||||
/* 1) Check if the element already has a context of the specific
|
/* 1) Check if the element already has a context of the specific
|
||||||
* type, i.e. it was previously set via
|
* type, i.e. it was previously set via
|
||||||
* gst_element_set_context(). */
|
* gst_element_set_context(). */
|
||||||
/* This was already done by the caller of this function:
|
/* This was already done by the caller of this function:
|
||||||
* gst_vaapi_ensure_display() */
|
* gst_vaapi_ensure_display() */
|
||||||
|
|
||||||
/* 2) Query downstream with GST_QUERY_CONTEXT for the context and
|
/* 2) Query downstream with GST_QUERY_CONTEXT for the context and
|
||||||
check if downstream already has a context of the specific
|
check if downstream already has a context of the specific
|
||||||
type */
|
type */
|
||||||
/* 3) Query upstream with GST_QUERY_CONTEXT for the context and
|
/* 3) Query upstream with GST_QUERY_CONTEXT for the context and
|
||||||
check if upstream already has a context of the specific
|
check if upstream already has a context of the specific
|
||||||
type */
|
type */
|
||||||
context = NULL;
|
context = NULL;
|
||||||
query = gst_query_new_context(GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME);
|
query = gst_query_new_context (GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME);
|
||||||
if (run_context_query(element, query)) {
|
if (run_context_query (element, query)) {
|
||||||
gst_query_parse_context(query, &context);
|
gst_query_parse_context (query, &context);
|
||||||
GST_CAT_INFO_OBJECT(GST_CAT_CONTEXT, element,
|
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
|
||||||
"found context (%p) in query", context);
|
"found context (%p) in query", context);
|
||||||
gst_element_set_context(element, context);
|
gst_element_set_context (element, context);
|
||||||
}
|
} else {
|
||||||
else {
|
/* 4) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
|
||||||
/* 4) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
|
the required context types and afterwards check if an
|
||||||
the required context types and afterwards check if an
|
usable context was set now as in 1). The message could
|
||||||
usable context was set now as in 1). The message could
|
be handled by the parent bins of the element and the
|
||||||
be handled by the parent bins of the element and the
|
application. */
|
||||||
application. */
|
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
|
||||||
GST_CAT_INFO_OBJECT(GST_CAT_CONTEXT, element,
|
"posting `need-context' message");
|
||||||
"posting `need-context' message");
|
msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
|
||||||
msg = gst_message_new_need_context(GST_OBJECT_CAST(element),
|
GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME);
|
||||||
GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME);
|
gst_element_post_message (element, msg);
|
||||||
gst_element_post_message(element, msg);
|
|
||||||
|
|
||||||
/* The check of an usable context is done by the caller:
|
/* The check of an usable context is done by the caller:
|
||||||
gst_vaapi_ensure_display() */
|
gst_vaapi_ensure_display() */
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_query_unref(query);
|
gst_query_unref (query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 5) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT message
|
/* 5) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT message
|
||||||
on the bus. */
|
on the bus. */
|
||||||
void
|
void
|
||||||
gst_vaapi_video_context_propagate(GstElement *element, GstVaapiDisplay *display)
|
gst_vaapi_video_context_propagate (GstElement * element,
|
||||||
|
GstVaapiDisplay * display)
|
||||||
{
|
{
|
||||||
GstContext *context;
|
GstContext *context;
|
||||||
GstMessage *msg;
|
GstMessage *msg;
|
||||||
|
|
||||||
context = gst_vaapi_video_context_new_with_display(display, FALSE);
|
context = gst_vaapi_video_context_new_with_display (display, FALSE);
|
||||||
|
|
||||||
GST_CAT_INFO_OBJECT(GST_CAT_CONTEXT, element,
|
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
|
||||||
"posting `have-context' (%p) message with display (%p)",
|
"posting `have-context' (%p) message with display (%p)",
|
||||||
context, display);
|
context, display);
|
||||||
msg = gst_message_new_have_context(GST_OBJECT_CAST(element), context);
|
msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
|
||||||
gst_element_post_message(GST_ELEMENT_CAST(element), msg);
|
gst_element_post_message (GST_ELEMENT_CAST (element), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,35 +41,36 @@
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
GstContext *
|
GstContext *
|
||||||
gst_vaapi_video_context_new_with_display(GstVaapiDisplay *display,
|
gst_vaapi_video_context_new_with_display (GstVaapiDisplay * display,
|
||||||
gboolean persistent);
|
gboolean persistent);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean
|
gboolean
|
||||||
gst_vaapi_video_context_get_display(GstContext *context,
|
gst_vaapi_video_context_get_display (GstContext * context,
|
||||||
GstVaapiDisplay **display_ptr);
|
GstVaapiDisplay ** display_ptr);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
void
|
void
|
||||||
gst_vaapi_video_context_prepare(GstElement *element, const gchar **types);
|
gst_vaapi_video_context_prepare (GstElement * element, const gchar ** types);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
void
|
void
|
||||||
gst_vaapi_video_context_propagate(GstElement *element,
|
gst_vaapi_video_context_propagate (GstElement * element,
|
||||||
GstVaapiDisplay *display);
|
GstVaapiDisplay * display);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include <gst/video/videocontext.h>
|
#include <gst/video/videocontext.h>
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gst_vaapi_video_context_prepare(GstVideoContext *context, const gchar **types)
|
gst_vaapi_video_context_prepare (GstVideoContext * context,
|
||||||
|
const gchar ** types)
|
||||||
{
|
{
|
||||||
gst_video_context_prepare(context, types);
|
gst_video_context_prepare (context, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gst_vaapi_video_context_propagate(GstVideoContext *context,
|
gst_vaapi_video_context_propagate (GstVideoContext * context,
|
||||||
GstVaapiDisplay *display)
|
GstVaapiDisplay * display)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue