/* * gstvaapidisplay.c - VA display abstraction * * Copyright (C) 2010-2011 Splitted-Desktop Systems * Author: Gwenole Beauchesne * Copyright (C) 2011-2013 Intel Corporation * Author: Gwenole Beauchesne * * 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 */ /** * SECTION:gstvaapidisplay * @short_description: VA display abstraction */ #include "sysdeps.h" #include #include "gstvaapiutils.h" #include "gstvaapivalue.h" #include "gstvaapidisplay.h" #include "gstvaapidisplay_priv.h" #include "gstvaapiworkarounds.h" #include "gstvaapiversion.h" #define DEBUG 1 #include "gstvaapidebug.h" GST_DEBUG_CATEGORY (gst_debug_vaapi); /* Ensure those symbols are actually defined in the resulting libraries */ #undef gst_vaapi_display_ref #undef gst_vaapi_display_unref #undef gst_vaapi_display_replace typedef struct _GstVaapiConfig GstVaapiConfig; struct _GstVaapiConfig { GstVaapiProfile profile; GstVaapiEntrypoint entrypoint; }; typedef struct _GstVaapiProperty GstVaapiProperty; struct _GstVaapiProperty { const gchar *name; VADisplayAttribute attribute; gint old_value; }; typedef struct _GstVaapiFormatInfo GstVaapiFormatInfo; struct _GstVaapiFormatInfo { GstVideoFormat format; guint flags; }; #define DEFAULT_RENDER_MODE GST_VAAPI_RENDER_MODE_TEXTURE #define DEFAULT_ROTATION GST_VAAPI_ROTATION_0 enum { PROP_0, PROP_RENDER_MODE, PROP_ROTATION, PROP_HUE, PROP_SATURATION, PROP_BRIGHTNESS, PROP_CONTRAST, N_PROPERTIES }; static GstVaapiDisplayCache *g_display_cache; static GMutex g_display_cache_lock; static GParamSpec *g_properties[N_PROPERTIES] = { NULL, }; static void gst_vaapi_display_properties_init (void); static gboolean get_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint * value); static gboolean set_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint value); static gboolean get_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat * v); static gboolean set_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat v); static void libgstvaapi_init_once (void) { static gsize g_once = FALSE; if (!g_once_init_enter (&g_once)) return; GST_DEBUG_CATEGORY_INIT (gst_debug_vaapi, "vaapi", 0, "VA-API helper"); /* Dump gstreamer-vaapi version for debugging purposes */ GST_INFO ("gstreamer-vaapi version %s", GST_VAAPI_VERSION_ID); gst_vaapi_display_properties_init (); g_once_init_leave (&g_once, TRUE); } static GstVaapiDisplayCache * get_display_cache (void) { GstVaapiDisplayCache *cache = NULL; g_mutex_lock (&g_display_cache_lock); if (!g_display_cache) g_display_cache = gst_vaapi_display_cache_new (); if (g_display_cache) cache = gst_vaapi_display_cache_ref (g_display_cache); g_mutex_unlock (&g_display_cache_lock); return cache; } static void free_display_cache (void) { g_mutex_lock (&g_display_cache_lock); if (g_display_cache && gst_vaapi_display_cache_is_empty (g_display_cache)) gst_vaapi_display_cache_replace (&g_display_cache, NULL); g_mutex_unlock (&g_display_cache_lock); } /* GstVaapiDisplayType enumerations */ GType gst_vaapi_display_type_get_type (void) { static GType g_type = 0; static const GEnumValue display_types[] = { {GST_VAAPI_DISPLAY_TYPE_ANY, "Auto detection", "any"}, #if USE_X11 {GST_VAAPI_DISPLAY_TYPE_X11, "VA/X11 display", "x11"}, #endif #if USE_GLX {GST_VAAPI_DISPLAY_TYPE_GLX, "VA/GLX display", "glx"}, #endif #if USE_WAYLAND {GST_VAAPI_DISPLAY_TYPE_WAYLAND, "VA/Wayland display", "wayland"}, #endif #if USE_DRM {GST_VAAPI_DISPLAY_TYPE_DRM, "VA/DRM display", "drm"}, #endif {0, NULL, NULL}, }; if (!g_type) g_type = g_enum_register_static ("GstVaapiDisplayType", display_types); return g_type; } /** * gst_vaapi_display_type_is_compatible: * @type1: the #GstVaapiDisplayType to test * @type2: the reference #GstVaapiDisplayType * * Compares whether #GstVaapiDisplay @type1 is compatible with @type2. * That is, if @type2 is in "any" category, or derived from @type1. * * Returns: %TRUE if @type1 is compatible with @type2, %FALSE otherwise. */ gboolean gst_vaapi_display_type_is_compatible (GstVaapiDisplayType type1, GstVaapiDisplayType type2) { if (type1 == type2) return TRUE; switch (type1) { case GST_VAAPI_DISPLAY_TYPE_GLX: if (type2 == GST_VAAPI_DISPLAY_TYPE_X11) return TRUE; break; default: break; } return type2 == GST_VAAPI_DISPLAY_TYPE_ANY; } /* Append GstVideoFormat to formats array */ static inline void append_format (GArray * formats, GstVideoFormat format, guint flags) { GstVaapiFormatInfo fi; fi.format = format; fi.flags = flags; g_array_append_val (formats, fi); } /* Append VAImageFormats to formats array */ static void append_formats (GArray * formats, const VAImageFormat * va_formats, guint * flags, guint n) { GstVideoFormat format; const GstVaapiFormatInfo *YV12_fip = NULL; const GstVaapiFormatInfo *I420_fip = NULL; guint i; for (i = 0; i < n; i++) { const VAImageFormat *const va_format = &va_formats[i]; const GstVaapiFormatInfo **fipp; format = gst_vaapi_video_format_from_va_format (va_format); if (format == GST_VIDEO_FORMAT_UNKNOWN) { GST_DEBUG ("unsupported format %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (va_format->fourcc)); continue; } append_format (formats, format, flags ? flags[i] : 0); switch (format) { case GST_VIDEO_FORMAT_YV12: fipp = &YV12_fip; break; case GST_VIDEO_FORMAT_I420: fipp = &I420_fip; break; default: fipp = NULL; break; } if (fipp) *fipp = &g_array_index (formats, GstVaapiFormatInfo, formats->len - 1); } /* Append I420 (resp. YV12) format if YV12 (resp. I420) is not supported by the underlying driver */ if (YV12_fip && !I420_fip) append_format (formats, GST_VIDEO_FORMAT_I420, YV12_fip->flags); else if (I420_fip && !YV12_fip) append_format (formats, GST_VIDEO_FORMAT_YV12, I420_fip->flags); } /* Sort image formats. Prefer YUV formats first */ static gint compare_yuv_formats (gconstpointer a, gconstpointer b) { const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *) a)->format; const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *) b)->format; const gboolean is_fmt1_yuv = gst_vaapi_video_format_is_yuv (fmt1); const gboolean is_fmt2_yuv = gst_vaapi_video_format_is_yuv (fmt2); if (is_fmt1_yuv != is_fmt2_yuv) return is_fmt1_yuv ? -1 : 1; return ((gint) gst_vaapi_video_format_get_score (fmt1) - (gint) gst_vaapi_video_format_get_score (fmt2)); } /* Sort subpicture formats. Prefer RGB formats first */ static gint compare_rgb_formats (gconstpointer a, gconstpointer b) { const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *) a)->format; const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *) b)->format; const gboolean is_fmt1_rgb = gst_vaapi_video_format_is_rgb (fmt1); const gboolean is_fmt2_rgb = gst_vaapi_video_format_is_rgb (fmt2); if (is_fmt1_rgb != is_fmt2_rgb) return is_fmt1_rgb ? -1 : 1; return ((gint) gst_vaapi_video_format_get_score (fmt1) - (gint) gst_vaapi_video_format_get_score (fmt2)); } /* Check if configs array contains profile at entrypoint */ static inline gboolean find_config (GArray * configs, GstVaapiProfile profile, GstVaapiEntrypoint entrypoint) { GstVaapiConfig *config; guint i; if (!configs) return FALSE; for (i = 0; i < configs->len; i++) { config = &g_array_index (configs, GstVaapiConfig, i); if (config->profile == profile && config->entrypoint == entrypoint) return TRUE; } return FALSE; } /* HACK: append H.263 Baseline profile if MPEG-4:2 Simple profile is supported */ static void append_h263_config (GArray * configs) { GstVaapiConfig *config, tmp_config; GstVaapiConfig *mpeg4_simple_config = NULL; GstVaapiConfig *h263_baseline_config = NULL; guint i; if (!WORKAROUND_H263_BASELINE_DECODE_PROFILE) return; if (!configs) return; for (i = 0; i < configs->len; i++) { config = &g_array_index (configs, GstVaapiConfig, i); if (config->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE) mpeg4_simple_config = config; else if (config->profile == GST_VAAPI_PROFILE_H263_BASELINE) h263_baseline_config = config; } if (mpeg4_simple_config && !h263_baseline_config) { tmp_config = *mpeg4_simple_config; tmp_config.profile = GST_VAAPI_PROFILE_H263_BASELINE; g_array_append_val (configs, tmp_config); } } /* Sort profiles. Group per codec */ static gint compare_profiles (gconstpointer a, gconstpointer b) { const GstVaapiConfig *const config1 = (GstVaapiConfig *) a; const GstVaapiConfig *const config2 = (GstVaapiConfig *) b; if (config1->profile == config2->profile) return config1->entrypoint - config2->entrypoint; return config1->profile - config2->profile; } /* Convert configs array to profiles as GstCaps */ static GArray * get_profiles (GArray * configs) { GstVaapiConfig *config; GArray *out_profiles; guint i; if (!configs) return NULL; out_profiles = g_array_new (FALSE, FALSE, sizeof (GstVaapiProfile)); if (!out_profiles) return NULL; for (i = 0; i < configs->len; i++) { config = &g_array_index (configs, GstVaapiConfig, i); g_array_append_val (out_profiles, config->profile); } return out_profiles; } /* Find format info */ static const GstVaapiFormatInfo * find_format_info (GArray * formats, GstVideoFormat format) { const GstVaapiFormatInfo *fip; guint i; for (i = 0; i < formats->len; i++) { fip = &g_array_index (formats, GstVaapiFormatInfo, i); if (fip->format == format) return fip; } return NULL; } /* Check if formats array contains format */ static inline gboolean find_format (GArray * formats, GstVideoFormat format) { return find_format_info (formats, format) != NULL; } /* Convert formats array to GstCaps */ static GArray * get_formats (GArray * formats) { const GstVaapiFormatInfo *fip; GArray *out_formats; guint i; out_formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat)); if (!out_formats) return NULL; for (i = 0; i < formats->len; i++) { fip = &g_array_index (formats, GstVaapiFormatInfo, i); g_array_append_val (out_formats, fip->format); } return out_formats; } /* Find display attribute */ static const GstVaapiProperty * find_property (GArray * properties, const gchar * name) { GstVaapiProperty *prop; guint i; if (!name) return NULL; for (i = 0; i < properties->len; i++) { prop = &g_array_index (properties, GstVaapiProperty, i); if (strcmp (prop->name, name) == 0) return prop; } return NULL; } #if 0 static const GstVaapiProperty * find_property_by_type (GArray * properties, VADisplayAttribType type) { GstVaapiProperty *prop; guint i; for (i = 0; i < properties->len; i++) { prop = &g_array_index (properties, GstVaapiProperty, i); if (prop->attribute.type == type) return prop; } return NULL; } #endif static inline const GstVaapiProperty * find_property_by_pspec (GstVaapiDisplay * display, GParamSpec * pspec) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); return find_property (priv->properties, pspec->name); } static guint find_property_id (const gchar * name) { typedef struct { const gchar *name; guint id; } property_map; static const property_map g_property_map[] = { {GST_VAAPI_DISPLAY_PROP_RENDER_MODE, PROP_RENDER_MODE}, {GST_VAAPI_DISPLAY_PROP_ROTATION, PROP_ROTATION}, {GST_VAAPI_DISPLAY_PROP_HUE, PROP_HUE}, {GST_VAAPI_DISPLAY_PROP_SATURATION, PROP_SATURATION}, {GST_VAAPI_DISPLAY_PROP_BRIGHTNESS, PROP_BRIGHTNESS}, {GST_VAAPI_DISPLAY_PROP_CONTRAST, PROP_CONTRAST}, {NULL,} }; const property_map *m; for (m = g_property_map; m->name != NULL; m++) { if (strcmp (m->name, name) == 0) return m->id; } return 0; } /* Initialize VA profiles (decoders, encoders) */ static gboolean ensure_profiles (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VAProfile *profiles = NULL; VAEntrypoint *entrypoints = NULL; gint i, j, n, num_entrypoints; VAStatus status; gboolean success = FALSE; if (priv->has_profiles) return TRUE; priv->decoders = g_array_new (FALSE, FALSE, sizeof (GstVaapiConfig)); if (!priv->decoders) goto cleanup; priv->encoders = g_array_new (FALSE, FALSE, sizeof (GstVaapiConfig)); if (!priv->encoders) goto cleanup; priv->has_profiles = TRUE; /* VA profiles */ profiles = g_new (VAProfile, vaMaxNumProfiles (priv->display)); if (!profiles) goto cleanup; entrypoints = g_new (VAEntrypoint, vaMaxNumEntrypoints (priv->display)); if (!entrypoints) goto cleanup; n = 0; status = vaQueryConfigProfiles (priv->display, profiles, &n); if (!vaapi_check_status (status, "vaQueryConfigProfiles()")) goto cleanup; GST_DEBUG ("%d profiles", n); for (i = 0; i < n; i++) { #if VA_CHECK_VERSION(0,34,0) /* Introduced in VA/VPP API */ if (profiles[i] == VAProfileNone) continue; #endif GST_DEBUG (" %s", string_of_VAProfile (profiles[i])); } for (i = 0; i < n; i++) { GstVaapiConfig config; config.profile = gst_vaapi_profile (profiles[i]); if (!config.profile) continue; status = vaQueryConfigEntrypoints (priv->display, profiles[i], entrypoints, &num_entrypoints); if (!vaapi_check_status (status, "vaQueryConfigEntrypoints()")) continue; for (j = 0; j < num_entrypoints; j++) { config.entrypoint = gst_vaapi_entrypoint (entrypoints[j]); switch (config.entrypoint) { case GST_VAAPI_ENTRYPOINT_VLD: case GST_VAAPI_ENTRYPOINT_IDCT: case GST_VAAPI_ENTRYPOINT_MOCO: g_array_append_val (priv->decoders, config); break; case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE: g_array_append_val (priv->encoders, config); break; } } } append_h263_config (priv->decoders); g_array_sort (priv->decoders, compare_profiles); g_array_sort (priv->encoders, compare_profiles); /* Video processing API */ #if USE_VA_VPP status = vaQueryConfigEntrypoints (priv->display, VAProfileNone, entrypoints, &num_entrypoints); if (vaapi_check_status (status, "vaQueryEntrypoints() [VAProfileNone]")) { for (j = 0; j < num_entrypoints; j++) { if (entrypoints[j] == VAEntrypointVideoProc) priv->has_vpp = TRUE; } } #endif success = TRUE; cleanup: g_free (profiles); g_free (entrypoints); return success; } /* Initialize VA display attributes */ static gboolean ensure_properties (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VADisplayAttribute *display_attrs = NULL; VAStatus status; gint i, n; gboolean success = FALSE; if (priv->properties) return TRUE; priv->properties = g_array_new (FALSE, FALSE, sizeof (GstVaapiProperty)); if (!priv->properties) goto cleanup; /* VA display attributes */ display_attrs = g_new (VADisplayAttribute, vaMaxNumDisplayAttributes (priv->display)); if (!display_attrs) goto cleanup; n = 0; status = vaQueryDisplayAttributes (priv->display, display_attrs, &n); if (!vaapi_check_status (status, "vaQueryDisplayAttributes()")) goto cleanup; GST_DEBUG ("%d display attributes", n); for (i = 0; i < n; i++) { VADisplayAttribute *const attr = &display_attrs[i]; GstVaapiProperty prop; gint value; GST_DEBUG (" %s", string_of_VADisplayAttributeType (attr->type)); switch (attr->type) { #if !VA_CHECK_VERSION(0,34,0) case VADisplayAttribDirectSurface: prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE; break; #endif case VADisplayAttribRenderMode: prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE; break; case VADisplayAttribRotation: prop.name = GST_VAAPI_DISPLAY_PROP_ROTATION; break; case VADisplayAttribHue: prop.name = GST_VAAPI_DISPLAY_PROP_HUE; break; case VADisplayAttribSaturation: prop.name = GST_VAAPI_DISPLAY_PROP_SATURATION; break; case VADisplayAttribBrightness: prop.name = GST_VAAPI_DISPLAY_PROP_BRIGHTNESS; break; case VADisplayAttribContrast: prop.name = GST_VAAPI_DISPLAY_PROP_CONTRAST; break; default: prop.name = NULL; break; } if (!prop.name) continue; /* Assume the attribute is really supported if we can get the * actual and current value */ if (!get_attribute (display, attr->type, &value)) continue; /* Some drivers (e.g. EMGD) have completely random initial * values */ if (value < attr->min_value || value > attr->max_value) continue; prop.attribute = *attr; prop.old_value = value; g_array_append_val (priv->properties, prop); } success = TRUE; cleanup: g_free (display_attrs); return success; } /* Initialize VA image formats */ static gboolean ensure_image_formats (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VAImageFormat *formats = NULL; VAStatus status; gint i, n; gboolean success = FALSE; if (priv->image_formats) return TRUE; priv->image_formats = g_array_new (FALSE, FALSE, sizeof (GstVaapiFormatInfo)); if (!priv->image_formats) goto cleanup; /* VA image formats */ formats = g_new (VAImageFormat, vaMaxNumImageFormats (priv->display)); if (!formats) goto cleanup; n = 0; status = vaQueryImageFormats (priv->display, formats, &n); if (!vaapi_check_status (status, "vaQueryImageFormats()")) goto cleanup; GST_DEBUG ("%d image formats", n); for (i = 0; i < n; i++) GST_DEBUG (" %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (formats[i].fourcc)); append_formats (priv->image_formats, formats, NULL, n); g_array_sort (priv->image_formats, compare_yuv_formats); success = TRUE; cleanup: g_free (formats); return success; } /* Initialize VA subpicture formats */ static gboolean ensure_subpicture_formats (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VAImageFormat *formats = NULL; unsigned int *flags = NULL; VAStatus status; guint i, n; gboolean success = FALSE; if (priv->subpicture_formats) return TRUE; priv->subpicture_formats = g_array_new (FALSE, FALSE, sizeof (GstVaapiFormatInfo)); if (!priv->subpicture_formats) goto cleanup; /* VA subpicture formats */ n = vaMaxNumSubpictureFormats (priv->display); formats = g_new (VAImageFormat, n); if (!formats) goto cleanup; flags = g_new (guint, n); if (!flags) goto cleanup; n = 0; status = vaQuerySubpictureFormats (priv->display, formats, flags, &n); if (!vaapi_check_status (status, "vaQuerySubpictureFormats()")) goto cleanup; GST_DEBUG ("%d subpicture formats", n); for (i = 0; i < n; i++) { GST_DEBUG (" %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (formats[i].fourcc)); flags[i] = to_GstVaapiSubpictureFlags (flags[i]); } append_formats (priv->subpicture_formats, formats, flags, n); g_array_sort (priv->subpicture_formats, compare_rgb_formats); success = TRUE; cleanup: g_free (formats); g_free (flags); return success; } static void gst_vaapi_display_calculate_pixel_aspect_ratio (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); gdouble ratio, delta; gint i, j, index, windex; static const gint par[][2] = { {1, 1}, /* regular screen */ {16, 15}, /* PAL TV */ {11, 10}, /* 525 line Rec.601 video */ {54, 59}, /* 625 line Rec.601 video */ {64, 45}, /* 1280x1024 on 16:9 display */ {5, 3}, /* 1280x1024 on 4:3 display */ {4, 3} /* 800x600 on 16:9 display */ }; /* First, calculate the "real" ratio based on the X values; * which is the "physical" w/h divided by the w/h in pixels of the * display */ if (!priv->width || !priv->height || !priv->width_mm || !priv->height_mm) ratio = 1.0; else ratio = (gdouble) (priv->width_mm * priv->height) / (priv->height_mm * priv->width); GST_DEBUG ("calculated pixel aspect ratio: %f", ratio); /* Now, find the one from par[][2] with the lowest delta to the real one */ #define DELTA(idx, w) (ABS(ratio - ((gdouble)par[idx][w] / par[idx][!(w)]))) delta = DELTA (0, 0); index = 0; windex = 0; for (i = 1; i < G_N_ELEMENTS (par); i++) { for (j = 0; j < 2; j++) { const gdouble this_delta = DELTA (i, j); if (this_delta < delta) { index = i; windex = j; delta = this_delta; } } } #undef DELTA priv->par_n = par[index][windex]; priv->par_d = par[index][windex ^ 1]; } static void gst_vaapi_display_destroy (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); if (priv->decoders) { g_array_free (priv->decoders, TRUE); priv->decoders = NULL; } if (priv->encoders) { g_array_free (priv->encoders, TRUE); priv->encoders = NULL; } if (priv->image_formats) { g_array_free (priv->image_formats, TRUE); priv->image_formats = NULL; } if (priv->subpicture_formats) { g_array_free (priv->subpicture_formats, TRUE); priv->subpicture_formats = NULL; } if (priv->properties) { g_array_free (priv->properties, TRUE); priv->properties = NULL; } if (priv->display) { if (!priv->parent) vaTerminate (priv->display); priv->display = NULL; } if (!priv->use_foreign_display) { GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS (display); if (klass->close_display) klass->close_display (display); } g_free (priv->vendor_string); priv->vendor_string = NULL; gst_vaapi_display_replace_internal (&priv->parent, NULL); g_mutex_lock (&g_display_cache_lock); if (priv->cache) gst_vaapi_display_cache_remove (priv->cache, display); g_mutex_unlock (&g_display_cache_lock); gst_vaapi_display_cache_replace (&priv->cache, NULL); free_display_cache (); } static gboolean gst_vaapi_display_create_unlocked (GstVaapiDisplay * display, GstVaapiDisplayInitType init_type, gpointer init_value) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); const GstVaapiDisplayClass *const klass = GST_VAAPI_DISPLAY_GET_CLASS (display); gint major_version, minor_version; VAStatus status; GstVaapiDisplayInfo info; const GstVaapiDisplayInfo *cached_info = NULL; memset (&info, 0, sizeof (info)); info.display = display; info.display_type = priv->display_type; switch (init_type) { case GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY: info.va_display = init_value; priv->display = init_value; priv->use_foreign_display = TRUE; break; case GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME: if (klass->open_display && !klass->open_display (display, init_value)) return FALSE; goto create_display; case GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY: if (klass->bind_display && !klass->bind_display (display, init_value)) return FALSE; // fall-through create_display: if (!klass->get_display || !klass->get_display (display, &info)) return FALSE; priv->display = info.va_display; priv->display_type = info.display_type; if (klass->get_size) klass->get_size (display, &priv->width, &priv->height); if (klass->get_size_mm) klass->get_size_mm (display, &priv->width_mm, &priv->height_mm); gst_vaapi_display_calculate_pixel_aspect_ratio (display); break; } if (!priv->display) return FALSE; cached_info = gst_vaapi_display_cache_lookup_by_va_display (priv->cache, info.va_display); if (cached_info) { gst_vaapi_display_replace_internal (&priv->parent, cached_info->display); priv->display_type = cached_info->display_type; } if (!priv->parent) { status = vaInitialize (priv->display, &major_version, &minor_version); if (!vaapi_check_status (status, "vaInitialize()")) return FALSE; GST_DEBUG ("VA-API version %d.%d", major_version, minor_version); } if (!cached_info) { if (!gst_vaapi_display_cache_add (priv->cache, &info)) return FALSE; } return TRUE; } static gboolean gst_vaapi_display_create (GstVaapiDisplay * display, GstVaapiDisplayInitType init_type, gpointer init_value) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); GstVaapiDisplayCache *cache; gboolean success; cache = get_display_cache (); if (!cache) return FALSE; gst_vaapi_display_cache_replace (&priv->cache, cache); gst_vaapi_display_cache_unref (cache); g_mutex_lock (&g_display_cache_lock); success = gst_vaapi_display_create_unlocked (display, init_type, init_value); g_mutex_unlock (&g_display_cache_lock); return success; } static void gst_vaapi_display_lock_default (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); if (priv->parent) priv = GST_VAAPI_DISPLAY_GET_PRIVATE (priv->parent); g_rec_mutex_lock (&priv->mutex); } static void gst_vaapi_display_unlock_default (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); if (priv->parent) priv = GST_VAAPI_DISPLAY_GET_PRIVATE (priv->parent); g_rec_mutex_unlock (&priv->mutex); } static void gst_vaapi_display_init (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); const GstVaapiDisplayClass *const dpy_class = GST_VAAPI_DISPLAY_GET_CLASS (display); priv->display_type = GST_VAAPI_DISPLAY_TYPE_ANY; priv->par_n = 1; priv->par_d = 1; g_rec_mutex_init (&priv->mutex); if (dpy_class->init) dpy_class->init (display); } static void gst_vaapi_display_finalize (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); gst_vaapi_display_destroy (display); g_rec_mutex_clear (&priv->mutex); } void gst_vaapi_display_class_init (GstVaapiDisplayClass * klass) { GstVaapiMiniObjectClass *const object_class = GST_VAAPI_MINI_OBJECT_CLASS (klass); GstVaapiDisplayClass *const dpy_class = GST_VAAPI_DISPLAY_CLASS (klass); libgstvaapi_init_once (); object_class->size = sizeof (GstVaapiDisplay); object_class->finalize = (GDestroyNotify) gst_vaapi_display_finalize; dpy_class->lock = gst_vaapi_display_lock_default; dpy_class->unlock = gst_vaapi_display_unlock_default; } static void gst_vaapi_display_properties_init (void) { /** * GstVaapiDisplay:render-mode: * * The VA display rendering mode, expressed as a #GstVaapiRenderMode. */ g_properties[PROP_RENDER_MODE] = g_param_spec_enum (GST_VAAPI_DISPLAY_PROP_RENDER_MODE, "render mode", "The display rendering mode", GST_VAAPI_TYPE_RENDER_MODE, DEFAULT_RENDER_MODE, G_PARAM_READWRITE); /** * GstVaapiDisplay:rotation: * * The VA display rotation mode, expressed as a #GstVaapiRotation. */ g_properties[PROP_ROTATION] = g_param_spec_enum (GST_VAAPI_DISPLAY_PROP_ROTATION, "rotation", "The display rotation mode", GST_VAAPI_TYPE_ROTATION, DEFAULT_ROTATION, G_PARAM_READWRITE); /** * GstVaapiDisplay:hue: * * The VA display hue, expressed as a float value. Range is -180.0 * to 180.0. Default value is 0.0 and represents no modification. */ g_properties[PROP_HUE] = g_param_spec_float (GST_VAAPI_DISPLAY_PROP_HUE, "hue", "The display hue value", -180.0, 180.0, 0.0, G_PARAM_READWRITE); /** * GstVaapiDisplay:saturation: * * The VA display saturation, expressed as a float value. Range is * 0.0 to 2.0. Default value is 1.0 and represents no modification. */ g_properties[PROP_SATURATION] = g_param_spec_float (GST_VAAPI_DISPLAY_PROP_SATURATION, "saturation", "The display saturation value", 0.0, 2.0, 1.0, G_PARAM_READWRITE); /** * GstVaapiDisplay:brightness: * * The VA display brightness, expressed as a float value. Range is * -1.0 to 1.0. Default value is 0.0 and represents no modification. */ g_properties[PROP_BRIGHTNESS] = g_param_spec_float (GST_VAAPI_DISPLAY_PROP_BRIGHTNESS, "brightness", "The display brightness value", -1.0, 1.0, 0.0, G_PARAM_READWRITE); /** * GstVaapiDisplay:contrast: * * The VA display contrast, expressed as a float value. Range is 0.0 * to 2.0. Default value is 1.0 and represents no modification. */ g_properties[PROP_CONTRAST] = g_param_spec_float (GST_VAAPI_DISPLAY_PROP_CONTRAST, "contrast", "The display contrast value", 0.0, 2.0, 1.0, G_PARAM_READWRITE); } static inline const GstVaapiDisplayClass * gst_vaapi_display_class (void) { static GstVaapiDisplayClass g_class; static gsize g_class_init = FALSE; if (g_once_init_enter (&g_class_init)) { gst_vaapi_display_class_init (&g_class); g_once_init_leave (&g_class_init, TRUE); } return &g_class; } GstVaapiDisplay * gst_vaapi_display_new (const GstVaapiDisplayClass * klass, GstVaapiDisplayInitType init_type, gpointer init_value) { GstVaapiDisplay *display; display = (GstVaapiDisplay *) gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS (klass)); if (!display) return NULL; gst_vaapi_display_init (display); if (!gst_vaapi_display_create (display, init_type, init_value)) goto error; return display; error: gst_vaapi_display_unref_internal (display); return NULL; } /** * gst_vaapi_display_new_with_display: * @va_display: a #VADisplay * * Creates a new #GstVaapiDisplay, using @va_display as the VA * display. * * Return value: the newly created #GstVaapiDisplay object */ GstVaapiDisplay * gst_vaapi_display_new_with_display (VADisplay va_display) { GstVaapiDisplayCache *const cache = get_display_cache (); const GstVaapiDisplayInfo *info; g_return_val_if_fail (va_display != NULL, NULL); g_return_val_if_fail (cache != NULL, NULL); info = gst_vaapi_display_cache_lookup_by_va_display (cache, va_display); if (info) return gst_vaapi_display_ref_internal (info->display); return gst_vaapi_display_new (gst_vaapi_display_class (), GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY, va_display); } /** * gst_vaapi_display_ref: * @display: a #GstVaapiDisplay * * Atomically increases the reference count of the given @display by one. * * Returns: The same @display argument */ GstVaapiDisplay * gst_vaapi_display_ref (GstVaapiDisplay * display) { return gst_vaapi_display_ref_internal (display); } /** * gst_vaapi_display_unref: * @display: a #GstVaapiDisplay * * Atomically decreases the reference count of the @display by one. If * the reference count reaches zero, the display will be free'd. */ void gst_vaapi_display_unref (GstVaapiDisplay * display) { gst_vaapi_display_unref_internal (display); } /** * gst_vaapi_display_replace: * @old_display_ptr: a pointer to a #GstVaapiDisplay * @new_display: a #GstVaapiDisplay * * Atomically replaces the display display held in @old_display_ptr * with @new_display. This means that @old_display_ptr shall reference * a valid display. However, @new_display can be NULL. */ void gst_vaapi_display_replace (GstVaapiDisplay ** old_display_ptr, GstVaapiDisplay * new_display) { gst_vaapi_display_replace_internal (old_display_ptr, new_display); } /** * gst_vaapi_display_lock: * @display: a #GstVaapiDisplay * * Locks @display. If @display is already locked by another thread, * the current thread will block until @display is unlocked by the * other thread. */ void gst_vaapi_display_lock (GstVaapiDisplay * display) { GstVaapiDisplayClass *klass; g_return_if_fail (display != NULL); klass = GST_VAAPI_DISPLAY_GET_CLASS (display); if (klass->lock) klass->lock (display); } /** * gst_vaapi_display_unlock: * @display: a #GstVaapiDisplay * * Unlocks @display. If another thread is blocked in a * gst_vaapi_display_lock() call for @display, it will be woken and * can lock @display itself. */ void gst_vaapi_display_unlock (GstVaapiDisplay * display) { GstVaapiDisplayClass *klass; g_return_if_fail (display != NULL); klass = GST_VAAPI_DISPLAY_GET_CLASS (display); if (klass->unlock) klass->unlock (display); } /** * gst_vaapi_display_sync: * @display: a #GstVaapiDisplay * * Flushes any requests queued for the windowing system and waits until * all requests have been handled. This is often used for making sure * that the display is synchronized with the current state of the program. * * This is most useful for X11. On windowing systems where requests are * handled synchronously, this function will do nothing. */ void gst_vaapi_display_sync (GstVaapiDisplay * display) { GstVaapiDisplayClass *klass; g_return_if_fail (display != NULL); klass = GST_VAAPI_DISPLAY_GET_CLASS (display); if (klass->sync) klass->sync (display); else if (klass->flush) klass->flush (display); } /** * gst_vaapi_display_flush: * @display: a #GstVaapiDisplay * * Flushes any requests queued for the windowing system. * * This is most useful for X11. On windowing systems where requests * are handled synchronously, this function will do nothing. */ void gst_vaapi_display_flush (GstVaapiDisplay * display) { GstVaapiDisplayClass *klass; g_return_if_fail (display != NULL); klass = GST_VAAPI_DISPLAY_GET_CLASS (display); if (klass->flush) klass->flush (display); } /** * gst_vaapi_display_get_display: * @display: a #GstVaapiDisplay * * Returns the #GstVaapiDisplayType bound to @display. * * Return value: the #GstVaapiDisplayType */ GstVaapiDisplayType gst_vaapi_display_get_display_type (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, GST_VAAPI_DISPLAY_TYPE_ANY); return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->display_type; } /** * gst_vaapi_display_get_display: * @display: a #GstVaapiDisplay * * Returns the #VADisplay bound to @display. * * Return value: the #VADisplay */ VADisplay gst_vaapi_display_get_display (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->display; } /** * gst_vaapi_display_get_width: * @display: a #GstVaapiDisplay * * Retrieves the width of a #GstVaapiDisplay. * * Return value: the width of the @display, in pixels */ guint gst_vaapi_display_get_width (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, 0); return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->width; } /** * gst_vaapi_display_get_height: * @display: a #GstVaapiDisplay * * Retrieves the height of a #GstVaapiDisplay * * Return value: the height of the @display, in pixels */ guint gst_vaapi_display_get_height (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, 0); return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->height; } /** * gst_vaapi_display_get_size: * @display: a #GstVaapiDisplay * @pwidth: return location for the width, or %NULL * @pheight: return location for the height, or %NULL * * Retrieves the dimensions of a #GstVaapiDisplay. */ void gst_vaapi_display_get_size (GstVaapiDisplay * display, guint * pwidth, guint * pheight) { g_return_if_fail (GST_VAAPI_DISPLAY (display)); if (pwidth) *pwidth = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->width; if (pheight) *pheight = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->height; } /** * gst_vaapi_display_get_pixel_aspect_ratio: * @display: a #GstVaapiDisplay * @par_n: return location for the numerator of pixel aspect ratio, or %NULL * @par_d: return location for the denominator of pixel aspect ratio, or %NULL * * Retrieves the pixel aspect ratio of a #GstVaapiDisplay. */ void gst_vaapi_display_get_pixel_aspect_ratio (GstVaapiDisplay * display, guint * par_n, guint * par_d) { g_return_if_fail (display != NULL); if (par_n) *par_n = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->par_n; if (par_d) *par_d = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->par_d; } /** * gst_vaapi_display_has_video_processing: * @display: a #GstVaapiDisplay * * Checks whether the underlying VA driver implementation supports * video processing (VPP) acceleration. * * Returns: %TRUE if some VPP features are available */ gboolean gst_vaapi_display_has_video_processing (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, FALSE); if (!ensure_profiles (display)) return FALSE; return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->has_vpp; } /** * gst_vaapi_display_get_decode_profiles: * @display: a #GstVaapiDisplay * * Gets the supported profiles for decoding. The caller owns an extra * reference to the resulting array of #GstVaapiProfile elements, so * it shall be released with g_array_unref() after usage. * * Return value: a newly allocated #GArray, or %NULL or error or if * decoding is not supported at all */ GArray * gst_vaapi_display_get_decode_profiles (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); if (!ensure_profiles (display)) return NULL; return get_profiles (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->decoders); } /** * gst_vaapi_display_has_decoder: * @display: a #GstVaapiDisplay * @profile: a #VAProfile * @entrypoint: a #GstVaaiEntrypoint * * Returns whether VA @display supports @profile for decoding at the * specified @entrypoint. * * Return value: %TRUE if VA @display supports @profile for decoding. */ gboolean gst_vaapi_display_has_decoder (GstVaapiDisplay * display, GstVaapiProfile profile, GstVaapiEntrypoint entrypoint) { g_return_val_if_fail (display != NULL, FALSE); if (!ensure_profiles (display)) return FALSE; return find_config (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->decoders, profile, entrypoint); } /** * gst_vaapi_display_get_encode_profiles: * @display: a #GstVaapiDisplay * * Gets the supported profiles for encoding. The caller owns an extra * reference to the resulting array of #GstVaapiProfile elements, so * it shall be released with g_array_unref() after usage. * * Return value: a newly allocated #GArray, or %NULL or error or if * encoding is not supported at all */ GArray * gst_vaapi_display_get_encode_profiles (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); if (!ensure_profiles (display)) return NULL; return get_profiles (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->encoders); } /** * gst_vaapi_display_has_encoder: * @display: a #GstVaapiDisplay * @profile: a #VAProfile * @entrypoint: a #GstVaapiEntrypoint * * Returns whether VA @display supports @profile for encoding at the * specified @entrypoint. * * Return value: %TRUE if VA @display supports @profile for encoding. */ gboolean gst_vaapi_display_has_encoder (GstVaapiDisplay * display, GstVaapiProfile profile, GstVaapiEntrypoint entrypoint) { g_return_val_if_fail (display != NULL, FALSE); if (!ensure_profiles (display)) return FALSE; return find_config (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->encoders, profile, entrypoint); } /** * gst_vaapi_display_get_image_formats: * @display: a #GstVaapiDisplay * * Gets the supported image formats for gst_vaapi_surface_get_image() * or gst_vaapi_surface_put_image(). * * Note that this method does not necessarily map image formats * returned by vaQueryImageFormats(). The set of capabilities can be * stripped down, if gstreamer-vaapi does not support the format, or * expanded to cover compatible formats not exposed by the underlying * driver. e.g. I420 can be supported even if the driver only exposes * YV12. * * Note: the caller owns an extra reference to the resulting array of * #GstVideoFormat elements, so it shall be released with * g_array_unref() after usage. * * Return value: a newly allocated #GArray, or %NULL on error or if * the set is empty */ GArray * gst_vaapi_display_get_image_formats (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); if (!ensure_image_formats (display)) return NULL; return get_formats (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->image_formats); } /** * gst_vaapi_display_has_image_format: * @display: a #GstVaapiDisplay * @format: a #GstVideoFormat * * Returns whether VA @display supports @format image format. * * Return value: %TRUE if VA @display supports @format image format */ gboolean gst_vaapi_display_has_image_format (GstVaapiDisplay * display, GstVideoFormat format) { GstVaapiDisplayPrivate *priv; g_return_val_if_fail (display != NULL, FALSE); g_return_val_if_fail (format, FALSE); priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); if (!ensure_image_formats (display)) return FALSE; if (find_format (priv->image_formats, format)) return TRUE; /* XXX: try subpicture formats since some drivers could report a * set of VA image formats that is not a superset of the set of VA * subpicture formats */ if (!ensure_subpicture_formats (display)) return FALSE; return find_format (priv->subpicture_formats, format); } /** * gst_vaapi_display_get_subpicture_formats: * @display: a #GstVaapiDisplay * * Gets the supported subpicture formats. * * Note that this method does not necessarily map subpicture formats * returned by vaQuerySubpictureFormats(). The set of capabilities can * be stripped down if gstreamer-vaapi does not support the * format. e.g. this is the case for paletted formats like IA44. * * Note: the caller owns an extra reference to the resulting array of * #GstVideoFormat elements, so it shall be released with * g_array_unref() after usage. * * Return value: a newly allocated #GArray, or %NULL on error of if * the set is empty */ GArray * gst_vaapi_display_get_subpicture_formats (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); if (!ensure_subpicture_formats (display)) return NULL; return get_formats (GST_VAAPI_DISPLAY_GET_PRIVATE (display)-> subpicture_formats); } /** * gst_vaapi_display_has_subpicture_format: * @display: a #GstVaapiDisplay * @format: a #GstVideoFormat * @flags_ptr: pointer to #GstVaapiSubpictureFlags, or zero * * Returns whether VA @display supports @format subpicture format with * the supplied @flags. * * Return value: %TRUE if VA @display supports @format subpicture format */ gboolean gst_vaapi_display_has_subpicture_format (GstVaapiDisplay * display, GstVideoFormat format, guint * flags_ptr) { GstVaapiDisplayPrivate *priv; const GstVaapiFormatInfo *fip; g_return_val_if_fail (display != NULL, FALSE); g_return_val_if_fail (format, FALSE); priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); if (!ensure_subpicture_formats (display)) return FALSE; fip = find_format_info (priv->subpicture_formats, format); if (!fip) return FALSE; if (flags_ptr) *flags_ptr = fip->flags; return TRUE; } /** * gst_vaapi_display_has_property: * @display: a #GstVaapiDisplay * @name: the property name to check * * Returns whether VA @display supports the requested property. The * check is performed against the property @name. So, the client * application may perform this check only once and cache this * information. * * Return value: %TRUE if VA @display supports property @name */ gboolean gst_vaapi_display_has_property (GstVaapiDisplay * display, const gchar * name) { g_return_val_if_fail (display != NULL, FALSE); g_return_val_if_fail (name, FALSE); if (!ensure_properties (display)) return FALSE; return find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties, name) != NULL; } gboolean gst_vaapi_display_get_property (GstVaapiDisplay * display, const gchar * name, GValue * out_value) { const GstVaapiProperty *prop; g_return_val_if_fail (display != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (out_value != NULL, FALSE); if (!ensure_properties (display)) return FALSE; prop = find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties, name); if (!prop) return FALSE; switch (prop->attribute.type) { case VADisplayAttribRenderMode:{ GstVaapiRenderMode mode; if (!gst_vaapi_display_get_render_mode (display, &mode)) return FALSE; g_value_init (out_value, GST_VAAPI_TYPE_RENDER_MODE); g_value_set_enum (out_value, mode); break; } case VADisplayAttribRotation:{ GstVaapiRotation rotation; rotation = gst_vaapi_display_get_rotation (display); g_value_init (out_value, GST_VAAPI_TYPE_ROTATION); g_value_set_enum (out_value, rotation); break; } case VADisplayAttribHue: case VADisplayAttribSaturation: case VADisplayAttribBrightness: case VADisplayAttribContrast:{ gfloat value; if (!get_color_balance (display, find_property_id (name), &value)) return FALSE; g_value_init (out_value, G_TYPE_FLOAT); g_value_set_float (out_value, value); break; } default: GST_WARNING ("unsupported property '%s'", name); return FALSE; } return TRUE; } gboolean gst_vaapi_display_set_property (GstVaapiDisplay * display, const gchar * name, const GValue * value) { const GstVaapiProperty *prop; g_return_val_if_fail (display != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); if (!ensure_properties (display)) return FALSE; prop = find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties, name); if (!prop) return FALSE; switch (prop->attribute.type) { case VADisplayAttribRenderMode:{ GstVaapiRenderMode mode; if (!G_VALUE_HOLDS (value, GST_VAAPI_TYPE_RENDER_MODE)) return FALSE; mode = g_value_get_enum (value); return gst_vaapi_display_set_render_mode (display, mode); } case VADisplayAttribRotation:{ GstVaapiRotation rotation; if (!G_VALUE_HOLDS (value, GST_VAAPI_TYPE_ROTATION)) return FALSE; rotation = g_value_get_enum (value); return gst_vaapi_display_set_rotation (display, rotation); } case VADisplayAttribHue: case VADisplayAttribSaturation: case VADisplayAttribBrightness: case VADisplayAttribContrast:{ gfloat v; if (!G_VALUE_HOLDS (value, G_TYPE_FLOAT)) return FALSE; v = g_value_get_float (value); return set_color_balance (display, find_property_id (name), v); } default: break; } GST_WARNING ("unsupported property '%s'", name); return FALSE; } static gboolean get_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint * value) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VADisplayAttribute attr; VAStatus status; attr.type = type; attr.flags = VA_DISPLAY_ATTRIB_GETTABLE; status = vaGetDisplayAttributes (priv->display, &attr, 1); if (!vaapi_check_status (status, "vaGetDisplayAttributes()")) return FALSE; *value = attr.value; return TRUE; } static gboolean set_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint value) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); VADisplayAttribute attr; VAStatus status; attr.type = type; attr.value = value; attr.flags = VA_DISPLAY_ATTRIB_SETTABLE; status = vaSetDisplayAttributes (priv->display, &attr, 1); if (!vaapi_check_status (status, "vaSetDisplayAttributes()")) return FALSE; return TRUE; } static gboolean get_render_mode_VADisplayAttribRenderMode (GstVaapiDisplay * display, GstVaapiRenderMode * pmode) { gint modes, devices; if (!get_attribute (display, VADisplayAttribRenderDevice, &devices)) return FALSE; if (!devices) return FALSE; if (!get_attribute (display, VADisplayAttribRenderMode, &modes)) return FALSE; /* Favor "overlay" mode since it is the most restrictive one */ if (modes & (VA_RENDER_MODE_LOCAL_OVERLAY | VA_RENDER_MODE_EXTERNAL_OVERLAY)) *pmode = GST_VAAPI_RENDER_MODE_OVERLAY; else *pmode = GST_VAAPI_RENDER_MODE_TEXTURE; return TRUE; } static gboolean get_render_mode_VADisplayAttribDirectSurface (GstVaapiDisplay * display, GstVaapiRenderMode * pmode) { #if VA_CHECK_VERSION(0,34,0) /* VADisplayAttribDirectsurface was removed in VA-API >= 0.34.0 */ return FALSE; #else gint direct_surface; if (!get_attribute (display, VADisplayAttribDirectSurface, &direct_surface)) return FALSE; if (direct_surface) *pmode = GST_VAAPI_RENDER_MODE_OVERLAY; else *pmode = GST_VAAPI_RENDER_MODE_TEXTURE; return TRUE; #endif } static gboolean get_render_mode_default (GstVaapiDisplay * display, GstVaapiRenderMode * pmode) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); switch (priv->display_type) { #if USE_WAYLAND case GST_VAAPI_DISPLAY_TYPE_WAYLAND: /* wl_buffer mapped from VA surface through vaGetSurfaceBufferWl() */ *pmode = GST_VAAPI_RENDER_MODE_OVERLAY; break; #endif #if USE_DRM case GST_VAAPI_DISPLAY_TYPE_DRM: /* vaGetSurfaceBufferDRM() returns the underlying DRM buffer handle */ *pmode = GST_VAAPI_RENDER_MODE_OVERLAY; break; #endif default: /* This includes VA/X11 and VA/GLX modes */ *pmode = DEFAULT_RENDER_MODE; break; } return TRUE; } /** * gst_vaapi_display_get_render_mode: * @display: a #GstVaapiDisplay * @pmode: return location for the VA @display rendering mode * * Returns the current VA @display rendering mode. * * Return value: %TRUE if VA @display rendering mode could be determined */ gboolean gst_vaapi_display_get_render_mode (GstVaapiDisplay * display, GstVaapiRenderMode * pmode) { g_return_val_if_fail (display != NULL, FALSE); /* Try with render-mode attribute */ if (get_render_mode_VADisplayAttribRenderMode (display, pmode)) return TRUE; /* Try with direct-surface attribute */ if (get_render_mode_VADisplayAttribDirectSurface (display, pmode)) return TRUE; /* Default: determine from the display type */ return get_render_mode_default (display, pmode); } /** * gst_vaapi_display_set_render_mode: * @display: a #GstVaapiDisplay * @mode: the #GstVaapiRenderMode to set * * Sets the VA @display rendering mode to the supplied @mode. This * function returns %FALSE if the rendering mode could not be set, * e.g. run-time switching rendering mode is not supported. * * Return value: %TRUE if VA @display rendering @mode could be changed * to the requested value */ gboolean gst_vaapi_display_set_render_mode (GstVaapiDisplay * display, GstVaapiRenderMode mode) { gint modes, devices; g_return_val_if_fail (display != NULL, FALSE); if (!get_attribute (display, VADisplayAttribRenderDevice, &devices)) return FALSE; modes = 0; switch (mode) { case GST_VAAPI_RENDER_MODE_OVERLAY: if (devices & VA_RENDER_DEVICE_LOCAL) modes |= VA_RENDER_MODE_LOCAL_OVERLAY; if (devices & VA_RENDER_DEVICE_EXTERNAL) modes |= VA_RENDER_MODE_EXTERNAL_OVERLAY; break; case GST_VAAPI_RENDER_MODE_TEXTURE: if (devices & VA_RENDER_DEVICE_LOCAL) modes |= VA_RENDER_MODE_LOCAL_GPU; if (devices & VA_RENDER_DEVICE_EXTERNAL) modes |= VA_RENDER_MODE_EXTERNAL_GPU; break; } if (!modes) return FALSE; if (!set_attribute (display, VADisplayAttribRenderMode, modes)) return FALSE; return TRUE; } /** * gst_vaapi_display_get_rotation: * @display: a #GstVaapiDisplay * * Returns the current VA @display rotation angle. If the VA driver * does not support "rotation" display attribute, then the display is * assumed to be un-rotated. * * Return value: the current #GstVaapiRotation value */ GstVaapiRotation gst_vaapi_display_get_rotation (GstVaapiDisplay * display) { gint value; g_return_val_if_fail (display != NULL, DEFAULT_ROTATION); if (!get_attribute (display, VADisplayAttribRotation, &value)) value = VA_ROTATION_NONE; return to_GstVaapiRotation (value); } /** * gst_vaapi_display_set_rotation: * @display: a #GstVaapiDisplay * @rotation: the #GstVaapiRotation value to set * * Sets the VA @display rotation angle to the supplied @rotation * value. This function returns %FALSE if the rotation angle could not * be set, e.g. the VA driver does not allow to change the display * rotation angle. * * Return value: %TRUE if VA @display rotation angle could be changed * to the requested value */ gboolean gst_vaapi_display_set_rotation (GstVaapiDisplay * display, GstVaapiRotation rotation) { guint value; g_return_val_if_fail (display != NULL, FALSE); value = from_GstVaapiRotation (rotation); if (!set_attribute (display, VADisplayAttribRotation, value)) return FALSE; return TRUE; } /* Get color balance attributes */ static gboolean get_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat * v) { GParamSpecFloat *const pspec = G_PARAM_SPEC_FLOAT (g_properties[prop_id]); const GstVaapiProperty *prop; const VADisplayAttribute *attr; gfloat out_value; gint value; if (!ensure_properties (display)) return FALSE; if (!pspec) return FALSE; prop = find_property_by_pspec (display, &pspec->parent_instance); if (!prop) return FALSE; attr = &prop->attribute; if (!get_attribute (display, attr->type, &value)) return FALSE; /* Scale wrt. the medium ("default") value */ out_value = pspec->default_value; if (value > attr->value) out_value += ((gfloat) (value - attr->value) / (attr->max_value - attr->value) * (pspec->maximum - pspec->default_value)); else if (value < attr->value) out_value -= ((gfloat) (attr->value - value) / (attr->value - attr->min_value) * (pspec->default_value - pspec->minimum)); *v = out_value; return TRUE; } /* Set color balance attribute */ static gboolean set_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat v) { GParamSpecFloat *const pspec = G_PARAM_SPEC_FLOAT (g_properties[prop_id]); const GstVaapiProperty *prop; const VADisplayAttribute *attr; gint value; if (!ensure_properties (display)) return FALSE; if (!pspec) return FALSE; prop = find_property_by_pspec (display, &pspec->parent_instance); if (!prop) return FALSE; attr = &prop->attribute; /* Scale wrt. the medium ("default") value */ value = attr->value; if (v > pspec->default_value) value += ((v - pspec->default_value) / (pspec->maximum - pspec->default_value) * (attr->max_value - attr->value)); else if (v < pspec->default_value) value -= ((pspec->default_value - v) / (pspec->default_value - pspec->minimum) * (attr->value - attr->min_value)); if (!set_attribute (display, attr->type, value)) return FALSE; return TRUE; } /* Ensures the VA driver vendor string was copied */ static gboolean ensure_vendor_string (GstVaapiDisplay * display) { GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display); const gchar *vendor_string; GST_VAAPI_DISPLAY_LOCK (display); if (!priv->vendor_string) { vendor_string = vaQueryVendorString (priv->display); if (vendor_string) priv->vendor_string = g_strdup (vendor_string); } GST_VAAPI_DISPLAY_UNLOCK (display); return priv->vendor_string != NULL; } /** * gst_vaapi_display_get_vendor_string: * @display: a #GstVaapiDisplay * * Returns the VA driver vendor string attached to the supplied VA @display. * The @display owns the vendor string, do *not* de-allocate it. * * This function is thread safe. * * Return value: the current #GstVaapiRotation value */ const gchar * gst_vaapi_display_get_vendor_string (GstVaapiDisplay * display) { g_return_val_if_fail (display != NULL, NULL); if (!ensure_vendor_string (display)) return NULL; return display->priv.vendor_string; }