[Spice-devel] [spice-gtk v1 1/2] Use libva to check video hardware accel capabilities

Victor Toso victortoso at redhat.com
Thu Feb 15 09:05:04 UTC 2018


From: Victor Toso <me at victortoso.com>

Libva is an implementation for VA-API.

This can be used to automatically send to the server the
preferred video codecs as the client would prefer streams
with video codecs that can be decoded with gpu support.

We can also use the profiles to detect and set upper limit for
video streams quality.
e.g: Don't start UHD video stream if client's hardware don't
support it.

This patch makes usage of libva in spice-session and exposes this
information to all available channel-displays with the internal
function spice_session_get_hw_accel_video_codecs()

Signed-off-by: Victor Toso <victortoso at redhat.com>
---
 configure.ac             |  20 +++++++
 src/Makefile.am          |  12 ++++
 src/spice-session-priv.h |   1 +
 src/spice-session.c      | 139 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 172 insertions(+)

diff --git a/configure.ac b/configure.ac
index 2a14055..0b0db0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,6 +321,25 @@ AC_SUBST(Z_LIBS)
 SPICE_CHECK_SMARTCARD
 AM_CONDITIONAL([WITH_SMARTCARD], [test "x$have_smartcard" = "xyes"])
 
+AC_ARG_ENABLE([libva],
+  AS_HELP_STRING([--enable-libva=@<:@auto/yes/no@:>@], [Enable auto detection of hardware accelerate video decoding support @<:@default=auto@:>@]),
+  [],
+  [enable_libva="auto"])
+AS_IF([test "x$enable_libva" != "xno"],
+      [PKG_CHECK_MODULES(LIBVA, [libva >= 1.0.0],
+         [AC_DEFINE([HAVE_LIBVA], 1, [Have libva support?])
+          enable_libva="yes"],
+         [AS_IF([test "x$enable_libva" = "xyes"],
+                AC_MSG_ERROR([Auto detection of hardware accelerated video decoding explicitly requested, but some required packages are not available]))
+          enable_libva="no"
+      ])
+    PKG_CHECK_MODULES([LIBVA_X11], [libva-x11 >= 1.0.0])
+    PKG_CHECK_MODULES([LIBVA_WAYLAND], [libva-wayland >= 1.0.0])
+    PKG_CHECK_MODULES([GDK_X11], [gdk-x11-3.0])
+    PKG_CHECK_MODULES([GDK_WAYLAND], [gdk-wayland-3.0])
+])
+AM_CONDITIONAL([HAVE_LIBVA], [test "x$enable_libva" = "xyes"])
+
 AC_ARG_ENABLE([usbredir],
   AS_HELP_STRING([--enable-usbredir=@<:@auto/yes/no@:>@],
                  [Enable usbredir support @<:@default=auto@:>@]),
@@ -635,6 +654,7 @@ AC_MSG_NOTICE([
         DBus:                     ${have_dbus}
         WebDAV support:           ${have_phodav}
         LZ4 support:              ${have_lz4}
+        Libva support:            ${enable_libva}
 
         Now type 'make' to build $PACKAGE
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 4b6e46d..7b74220 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,9 @@ SPICE_COMMON_CPPFLAGS =						\
 	$(PIXMAN_CFLAGS)					\
 	$(PULSE_CFLAGS)						\
 	$(GTK_CFLAGS)						\
+	$(GDK_CFLAGS)						\
+	$(GDK_X11_CFLAGS)					\
+	$(GDK_WAYLAND_CFLAGS)					\
 	$(CAIRO_CFLAGS)						\
 	$(GLIB2_CFLAGS)						\
 	$(GIO_CFLAGS)						\
@@ -88,6 +91,9 @@ SPICE_COMMON_CPPFLAGS =						\
 	$(GUDEV_CFLAGS)						\
 	$(SOUP_CFLAGS)						\
 	$(PHODAV_CFLAGS)					\
+	$(LIBVA_CFLAGS)						\
+	$(LIBVA_X11_CFLAGS)					\
+	$(LIBVA_WAYLAND_CFLAGS)					\
 	$(X11_CFLAGS)					\
 	$(LZ4_CFLAGS)					\
 	$(NULL)
@@ -195,6 +201,12 @@ libspice_client_glib_2_0_la_LIBADD =					\
 	$(USBREDIR_LIBS)						\
 	$(GUDEV_LIBS)							\
 	$(PHODAV_LIBS)							\
+	$(GDK_LIBS)							\
+	$(GDK_X11_LIBS)							\
+	$(GDK_WAYLAND_LIBS)						\
+	$(LIBVA_LIBS)							\
+	$(LIBVA_X11_LIBS)						\
+	$(LIBVA_WAYLAND_LIBS)						\
 	$(NULL)
 
 if WITH_POLKIT
diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h
index 03005aa..7137cf6 100644
--- a/src/spice-session-priv.h
+++ b/src/spice-session-priv.h
@@ -100,6 +100,7 @@ void spice_session_set_main_channel(SpiceSession *session, SpiceChannel *channel
 gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session);
 SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context);
 const gchar* spice_audio_data_mode_to_string(gint mode);
+const GArray *spice_session_get_hw_accel_video_codecs(SpiceSession *session);
 G_END_DECLS
 
 #endif /* __SPICE_CLIENT_SESSION_PRIV_H__ */
diff --git a/src/spice-session.c b/src/spice-session.c
index 2aabf58..e26d375 100644
--- a/src/spice-session.c
+++ b/src/spice-session.c
@@ -23,6 +23,19 @@
 #include <gio/gunixsocketaddress.h>
 #endif
 #include "common/ring.h"
+#ifdef HAVE_LIBVA
+#include <gdk/gdk.h>
+#include <va/va.h>
+#include <va/va_str.h>
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <va/va_wayland.h>
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#include <va/va_x11.h>
+#endif
+#endif
 
 #include "spice-client.h"
 #include "spice-common.h"
@@ -33,6 +46,7 @@
 #include "spice-uri-priv.h"
 #include "channel-playback-priv.h"
 #include "spice-audio-priv.h"
+#include "channel-display-priv.h"
 
 struct channel {
     SpiceChannel      *channel;
@@ -116,6 +130,9 @@ struct _SpiceSessionPrivate {
     guint8            uuid[16];
     gchar             *name;
     SpiceImageCompression preferred_compression;
+ 
+    /* Array of SpiceVideoCodecType with hw accelerated video decoding capability */
+    GArray            *video_codecs;
 
     /* associated objects */
     SpiceAudio        *audio_manager;
@@ -248,6 +265,7 @@ spice_image_compress_get_type (void)
 static guint signals[SPICE_SESSION_LAST_SIGNAL];
 
 static void spice_session_channel_destroy(SpiceSession *session, SpiceChannel *channel);
+static void spice_session_check_video_hw_caps(SpiceSession *session);
 
 static void update_proxy(SpiceSession *self, const gchar *str)
 {
@@ -299,6 +317,9 @@ static void spice_session_init(SpiceSession *session)
         SPICE_DEBUG("Could not initialize SpiceUsbDeviceManager - %s", err->message);
         g_clear_error(&err);
     }
+
+    session->priv->video_codecs = NULL;
+    spice_session_check_video_hw_caps(session);
 }
 
 static void
@@ -2801,3 +2822,121 @@ gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession
 
     return TRUE;
 }
+
+G_GNUC_INTERNAL
+const GArray *spice_session_get_hw_accel_video_codecs(SpiceSession *session)
+{
+    g_return_val_if_fail(SPICE_IS_SESSION(session), NULL);
+    return session->priv->video_codecs;
+}
+
+static void
+spice_session_check_video_hw_caps(SpiceSession *session)
+{
+#ifdef HAVE_LIBVA
+    VADisplay va_dpy = NULL;
+    VAStatus va_status;
+    GdkDisplay *display;
+    int major_version, minor_version;
+    GArray *codecs;
+    const gchar *last_profile = NULL;
+    VAProfile *profile_list = NULL;
+    int num_profiles, max_num_profiles, i;
+    int num_entrypoint;
+
+    display = gdk_display_get_default();
+    spice_debug("Display: %s", gdk_display_get_name(display));
+
+#ifdef GDK_WINDOWING_X11
+    if (GDK_IS_X11_DISPLAY(display))
+        va_dpy = vaGetDisplay(gdk_x11_display_get_xdisplay(display));
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+    if (GDK_IS_WAYLAND_DISPLAY(display))
+        va_dpy = vaGetDisplayWl(gdk_wayland_display_get_wl_display(display));
+#endif
+
+    if (va_dpy == NULL) {
+        spice_warning("Failed to get VADisplay, unable to detect hardware capabilities");
+        return;
+    }
+
+    va_status = vaInitialize(va_dpy, &major_version, &minor_version);
+    if (va_status != VA_STATUS_SUCCESS) {
+        spice_warning("Failed to initialize libva");
+        return;
+    }
+
+    max_num_profiles = vaMaxNumProfiles(va_dpy);
+    profile_list = g_new(VAProfile, max_num_profiles);
+
+    if (!profile_list) {
+        spice_warning("libva: failed to allocate memory for profile list");
+        vaTerminate(va_dpy);
+        return;
+    }
+
+    va_status = vaQueryConfigProfiles(va_dpy, profile_list, &num_profiles);
+    if (va_status != VA_STATUS_SUCCESS) {
+        spice_warning("libva: failed to query profiles");
+        g_free(profile_list);
+        vaTerminate(va_dpy);
+        return;
+    }
+
+    codecs = g_array_new(FALSE, FALSE, sizeof(gint));
+    for (i = 0; i < num_profiles; i++) {
+        int j;
+        VAEntrypoint entrypoints[50];
+        VAProfile profile = profile_list[i];
+        const char *profile_str = vaProfileStr(profile);
+
+        /* Spice protocol does not support different profiles for a given codec
+         * at the moment, which means that we can jump to the next codec. */
+        if (last_profile != NULL &&
+                g_ascii_strncasecmp(profile_str + strlen("VAProfile"),
+                                    last_profile,
+                                    strlen(last_profile)) == 0)
+            continue;
+
+        va_status = vaQueryConfigEntrypoints(va_dpy, profile, entrypoints, &num_entrypoint);
+        if (va_status == VA_STATUS_ERROR_UNSUPPORTED_PROFILE)
+            continue;
+        else if (va_status != VA_STATUS_SUCCESS) {
+            spice_warning("Error on vaQueryConfigEntrypoints()");
+            break;
+        }
+
+        /* Find if current profile has decoding support */
+        for (j = 0; j < num_entrypoint; j++) {
+            int k;
+
+            if (entrypoints[j] != VAEntrypointVLD)
+                continue;
+
+            /* Found decoding entrypoing, check if it is supported by Spice protocol */
+            for (k = 1; k < SPICE_VIDEO_CODEC_TYPE_ENUM_END; k++) {
+                if (g_ascii_strncasecmp(profile_str + strlen("VAProfile"),
+                                        gst_opts[k].name,
+                                        strlen(gst_opts[k].name)) == 0) {
+                    last_profile = gst_opts[k].name;
+                    g_array_append_val(codecs, k);
+                    spice_debug("Support to decode %s found with profile %s",
+                                gst_opts[k].name, profile_str);
+                    break;
+                }
+            }
+            break;
+        }
+    }
+
+    if (codecs->len > 0) {
+        g_clear_pointer(&session->priv->video_codecs, g_array_unref);
+        session->priv->video_codecs = g_array_ref(codecs);
+    }
+
+    g_array_unref(codecs);
+    g_free(profile_list);
+    vaTerminate(va_dpy);
+#endif
+}
-- 
2.16.1



More information about the Spice-devel mailing list