mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-06 16:25:31 +00:00
gstreamer-encoder: Use a h/w based encoder with Intel GPUs if possible
Once it is determined that an Intel GPU is available/active (after looking into udev's database), we try to see if there is a h/w based encoder (element) available (in Gstreamer's registry cache) for the user selected video codec. In other words, if we find that the Intel Media SDK Gstreamer plugin (libgstmsdk.so) and associated libraries (such as va or vaapi) are all installed properly, we add the appropriate h/w based encoder and post-processor/converter elements to the pipeline (along with any relevant options) instead of the s/w based elements. For example, if the user selects h264 as the preferred codec format, msdkh264enc and vapostproc will be preferred instead of x264enc and videoconvert. Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Co-developed-by: Jin Chung Teng <jin.chung.teng@intel.com> Co-developed-by: Hazwan Arif Mazlan <hazwan.arif.mazlan@intel.com> Acked-by: Frediano Ziglio <freddy77@gmail.com>
This commit is contained in:
parent
8af20a7cf1
commit
0d7aecd623
@ -38,6 +38,7 @@
|
||||
#include "red-common.h"
|
||||
#include "video-encoder.h"
|
||||
#include "utils.h"
|
||||
#include "common/udev.h"
|
||||
|
||||
|
||||
#define SPICE_GST_DEFAULT_FPS 30
|
||||
@ -861,6 +862,116 @@ static const gchar* get_gst_codec_name(const SpiceGstEncoder *encoder)
|
||||
}
|
||||
}
|
||||
|
||||
static const char video_codecs[][8] = {
|
||||
{ "" },
|
||||
{ "mjpeg" },
|
||||
{ "vp8" },
|
||||
{ "h264" },
|
||||
{ "vp9" },
|
||||
{ "h265" },
|
||||
};
|
||||
|
||||
static bool gst_features_lookup(const gchar *feature_name)
|
||||
{
|
||||
GstRegistry *registry;
|
||||
GstPluginFeature *feature;
|
||||
|
||||
registry = gst_registry_get();
|
||||
if (!registry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
feature = gst_registry_lookup_feature(registry, feature_name);
|
||||
if (!feature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gst_object_unref(feature);
|
||||
return true;
|
||||
}
|
||||
|
||||
static gchar *find_best_hw_plugin(const gchar *codec_name)
|
||||
{
|
||||
static const char plugins[][8] = {"msdk", "va", "vaapi"};
|
||||
gchar *feature_name;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(plugins); i++) {
|
||||
feature_name = !codec_name ? g_strconcat(plugins[i], "postproc", NULL) :
|
||||
g_strconcat(plugins[i], codec_name, "enc", NULL);
|
||||
if (!gst_features_lookup(feature_name)) {
|
||||
g_free(feature_name);
|
||||
feature_name = NULL;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return feature_name;
|
||||
}
|
||||
|
||||
static gchar *get_hw_gstenc_opts(const gchar *encoder, const gchar *codec_name)
|
||||
{
|
||||
gchar *gstenc_opts;
|
||||
|
||||
if (strcmp(codec_name, "mjpeg") == 0) {
|
||||
return g_strdup("");
|
||||
}
|
||||
|
||||
if (g_str_has_prefix(encoder, "msdk")) {
|
||||
if (strcmp(codec_name, "vp9") == 0) {
|
||||
gstenc_opts = g_strdup("async-depth=1 b-frames=0 rate-control=3 target-usage=7");
|
||||
} else {
|
||||
gstenc_opts = g_strdup("async-depth=1 rate-control=3 gop-size=1 tune=16 b-frames=0 target-usage=7 min-qp=15 max-qp=35");
|
||||
}
|
||||
} else if (g_str_has_prefix(encoder, "vaapi")) {
|
||||
if (strcmp(codec_name, "vp9") == 0) {
|
||||
gstenc_opts = g_strdup("tune=3 rate-control=1");
|
||||
} else {
|
||||
gstenc_opts = g_strdup("rate-control=cqp max-bframes=0 min-qp=15 max-qp=35");
|
||||
}
|
||||
} else {
|
||||
if (strcmp(codec_name, "vp9") == 0) {
|
||||
gstenc_opts = g_strdup("min-qp=15 max-qp=35 rate-control=16 ref-frames=0 target-usage=7");
|
||||
} else {
|
||||
gstenc_opts = g_strdup("rate-control=16 b-frames=0 target-usage=7 min-qp=15 max-qp=35");
|
||||
}
|
||||
}
|
||||
return gstenc_opts;
|
||||
}
|
||||
|
||||
static void try_intel_hw_plugins(const gchar *codec_name, gchar **converter,
|
||||
gchar **gstenc_name, gchar **gstenc_opts)
|
||||
{
|
||||
gchar *encoder, *vpp;
|
||||
|
||||
if (strcmp(codec_name, "vp8") == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
encoder = find_best_hw_plugin(codec_name);
|
||||
if (!encoder) {
|
||||
return;
|
||||
}
|
||||
vpp = find_best_hw_plugin(NULL);
|
||||
if (!vpp) {
|
||||
g_free(encoder);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(*converter);
|
||||
g_free(*gstenc_name);
|
||||
g_free(*gstenc_opts);
|
||||
*gstenc_name = encoder;
|
||||
*gstenc_opts = get_hw_gstenc_opts(encoder, codec_name);
|
||||
|
||||
if (g_str_has_prefix(vpp, "vaapi")) {
|
||||
*converter = g_strconcat(vpp, " ! video/x-raw(memory:VASurface),format=NV12", NULL);
|
||||
} else {
|
||||
*converter = g_strconcat(vpp, " ! video/x-raw(memory:VAMemory),format=NV12", NULL);
|
||||
}
|
||||
g_free(vpp);
|
||||
}
|
||||
|
||||
/* At this time, only the following formats are supported by x264enc. */
|
||||
static const char valid_formats[][10] = {
|
||||
{ "Y444" },
|
||||
@ -900,7 +1011,7 @@ static gchar *get_gst_converter(void)
|
||||
|
||||
static gboolean create_pipeline(SpiceGstEncoder *encoder)
|
||||
{
|
||||
const gchar* gstenc_name = get_gst_codec_name(encoder);
|
||||
gchar* gstenc_name = g_strdup(get_gst_codec_name(encoder));
|
||||
if (!gstenc_name) {
|
||||
return FALSE;
|
||||
}
|
||||
@ -947,10 +1058,18 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
|
||||
default:
|
||||
/* gstreamer_encoder_new() should have rejected this codec type */
|
||||
spice_warning("unsupported codec type %d", encoder->base.codec_type);
|
||||
g_free(gstenc_name);
|
||||
g_free(converter);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char *codec_name = video_codecs[encoder->base.codec_type];
|
||||
GpuVendor vendor = spice_udev_detect_gpu(INTEL_VENDOR_ID);
|
||||
if (vendor == VENDOR_GPU_DETECTED) {
|
||||
try_intel_hw_plugins(codec_name, &converter, &gstenc_name,
|
||||
&gstenc_opts);
|
||||
}
|
||||
|
||||
GError *err = NULL;
|
||||
gchar *desc = g_strdup_printf("appsrc is-live=true format=time do-timestamp=true name=src !"
|
||||
" %s ! %s name=encoder %s ! appsink name=sink",
|
||||
@ -967,6 +1086,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
|
||||
gst_object_unref(encoder->pipeline);
|
||||
encoder->pipeline = NULL;
|
||||
}
|
||||
g_free(gstenc_name);
|
||||
return FALSE;
|
||||
}
|
||||
encoder->appsrc = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(encoder->pipeline), "src"));
|
||||
@ -1003,6 +1123,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
|
||||
SPICE_GST_VIDEO_PIPELINE_BITRATE |
|
||||
SPICE_GST_VIDEO_PIPELINE_CAPS);
|
||||
|
||||
g_free(gstenc_name);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user