The search list is currently hardcoded to: ["x264enc", "openh264enc"]

x264enc: is probably the best available software encoder
openh264enc: lower quality, but available on more systems.

We restrict encoders to a known list because each encoder requires
fine tuning to get reasonable/usable results.

Signed-off-by: Dietmar Maurer <diet...@proxmox.com>
---
 ui/vnc-enc-h264.c | 76 +++++++++++++++++++++++++++++++++++++++--------
 ui/vnc.h          |  1 +
 2 files changed, 65 insertions(+), 12 deletions(-)

diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c
index 9e01b8a548..3eabfc2cfe 100644
--- a/ui/vnc-enc-h264.c
+++ b/ui/vnc-enc-h264.c
@@ -3,6 +3,60 @@
 
 #include <gst/gst.h>
 
+const char *encoder_list[] = { "x264enc", "openh264enc", NULL };
+
+static const char *get_available_encoder(void)
+{
+    int i = 0;
+    do {
+        const char *encoder_name = encoder_list[i];
+        if (encoder_name == NULL) {
+            break;
+        }
+        GstElement *element = gst_element_factory_make(
+            encoder_name, "video-encoder");
+        if (element != NULL) {
+            gst_object_unref(element);
+            return encoder_name;
+        }
+        i = i + 1;
+    } while (true);
+
+    return NULL;
+}
+
+static GstElement *create_encoder(const char *encoder_name)
+{
+    GstElement *encoder = gst_element_factory_make(
+        encoder_name, "video-encoder");
+    if (!encoder) {
+        VNC_DEBUG("Could not create gst '%s' video encoder\n", encoder_name);
+        return NULL;
+    }
+
+    if (!strcmp(encoder_name, "x264enc")) {
+        g_object_set(encoder, "tune", 4, NULL); /* zerolatency */
+        /*
+         * fix for zerolatency with novnc (without,
+         * noVNC displays green stripes)
+         */
+        g_object_set(encoder, "threads", 1, NULL);
+        g_object_set(encoder, "pass", 5, NULL); /* Constant Quality */
+        g_object_set(encoder, "quantizer", 26, NULL);
+        /* avoid access unit delimiters (Nal Unit Type 9) - not required */
+        g_object_set(encoder, "aud", false, NULL);
+    } else if (!strcmp(encoder_name, "openh264enc")) {
+        g_object_set(encoder, "usage-type", 1, NULL); /* screen content */
+        g_object_set(encoder, "complexity", 2, NULL); /* high */
+        g_object_set(encoder, "rate-control", 2, NULL); /* off (buffer) */
+    } else {
+        VNC_DEBUG("Unknown H264 encoder name '%s' - no setting any properties",
+            encoder_name);
+    }
+
+    return encoder;
+}
+
 static void destroy_encoder_context(VncState *vs)
 {
     if (!vs->h264) {
@@ -66,23 +120,12 @@ static bool create_encoder_context(VncState *vs, int w, 
int h)
         return FALSE;
     }
 
-    vs->h264->gst_encoder = gst_element_factory_make("x264enc", "gst-encoder");
+    vs->h264->gst_encoder = create_encoder(vs->h264->encoder_name);
     if (!vs->h264->gst_encoder) {
-        VNC_DEBUG("Could not create gst x264 encoder\n");
         destroy_encoder_context(vs);
         return FALSE;
     }
 
-    g_object_set(vs->h264->gst_encoder, "tune", 4, NULL); /* zerolatency */
-    /* fix for zerolatency with novnc (without, noVNC displays green stripes) 
*/
-    g_object_set(vs->h264->gst_encoder, "threads", 1, NULL);
-
-    g_object_set(vs->h264->gst_encoder, "pass", 5, NULL); /* Constant Quality 
*/
-    g_object_set(vs->h264->gst_encoder, "quantizer", 26, NULL);
-
-    /* avoid access unit delimiters (Nal Unit Type 9) - not required */
-    g_object_set(vs->h264->gst_encoder, "aud", false, NULL);
-
     vs->h264->sink = gst_element_factory_make("appsink", "sink");
     if (!vs->h264->sink) {
         VNC_DEBUG("Could not create gst sink\n");
@@ -173,7 +216,16 @@ int vnc_h264_encoder_init(VncState *vs)
 {
     g_assert(vs->h264 == NULL);
 
+    const char *encoder_name = get_available_encoder();
+    if (encoder_name == NULL) {
+        VNC_DEBUG("No H264 encoder available.\n");
+        return -1;
+    }
+
     vs->h264 = g_malloc0(sizeof(VncH264));
+    vs->h264->encoder_name = encoder_name;
+
+    VNC_DEBUG("Allow H264 using encoder '%s`\n", encoder_name);
 
     return 0;
 }
diff --git a/ui/vnc.h b/ui/vnc.h
index e1b81d6bcc..f39dbe21aa 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -239,6 +239,7 @@ typedef struct VncZywrle {
 /* Number of frames we send after the display is clean. */
 #define VNC_H264_KEEP_DIRTY 10
 typedef struct VncH264 {
+    const char *encoder_name;
     GstElement *pipeline, *source, *gst_encoder, *sink, *convert;
     size_t width;
     size_t height;
-- 
2.39.5


Reply via email to