[spice-streaming-agent] gst-plugin: Allow the instantiation/configuration of multiple plugins

Submitted by Kevin Pouget on Oct. 17, 2019, 11:54 a.m.

Details

Message ID 20191017115459.13743-1-kpouget@redhat.com
State New
Headers show
Series "gst-plugin: Allow the instantiation/configuration of multiple plugins" ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Kevin Pouget Oct. 17, 2019, 11:54 a.m.
This patch allows the instantiation and the configuration of multiple
GStreamer plugins from the commandline.

One plugin will be instantiated for each 'gst.codec=VALUE' found in
the command line arguments.

A plugin can be instantiated by codec, with the default gstreamer
encoder: 'gst.codec=CODEC_TYPE' (eg, mjpeg/vp8/vp9/...), or with a
codec and an encoder 'gst.codec=CODEC_TYPE=ENCODER'.

A plugin can be configured in different ways (see WantOption()):
- without prefix (eg, framerate=30) for all the plugins,
- with the GStreamer property prefix:
  - 'gst.prop=VALUE' for all the GStreamer plugins,
  - 'gst.CODEC_TYPE.prop=VALUE' for all the GStreamer plugin using the
    given codec type,
  - 'gst.ENCODER.prop=VALUE' for the GStreamer plugin using the given
    encoder.

The split between ParseOptions and UpdateOptions will later allow live
plugin reconfiguration (ie, without fully relaunching the
spice-streaming-agent).

Signed-off-by: Kevin Pouget <kpouget@redhat.com>
---
 src/gst-plugin.cpp | 121 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 94 insertions(+), 27 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/gst-plugin.cpp b/src/gst-plugin.cpp
index 922b90d..4b44d9d 100644
--- a/src/gst-plugin.cpp
+++ b/src/gst-plugin.cpp
@@ -33,7 +33,8 @@  namespace gstreamer_plugin {
 struct GstreamerEncoderSettings
 {
     int fps = 25;
-    SpiceVideoCodecType codec = SPICE_VIDEO_CODEC_TYPE_H264;
+    SpiceVideoCodecType codec;
+    std::string codec_name;
     std::string encoder;
     std::vector<std::pair<std::string, std::string>> prop_pairs;
 };
@@ -86,12 +87,14 @@  class GstreamerPlugin final: public Plugin
 public:
     FrameCapture *CreateCapture() override;
     unsigned Rank() override;
-    void ParseOptions(const ConfigureOption *options);
+    void ParseOptions(const ConfigureOption *options, const std::string codec_value);
+    void UpdateOptions(const ConfigureOption *options);
     SpiceVideoCodecType VideoCodecType() const override {
         return settings.codec;
     }
 private:
     GstreamerEncoderSettings settings;
+    bool WantOption(const std::string param_name, const std::string opt_name, bool gst_prfx);
 };
 
 GstElement *GstreamerFrameCapture::get_capture_plugin(const GstreamerEncoderSettings &settings)
@@ -420,45 +423,100 @@  unsigned GstreamerPlugin::Rank()
     return SoftwareMin;
 }
 
-void GstreamerPlugin::ParseOptions(const ConfigureOption *options)
+bool GstreamerPlugin::WantOption(const std::string param_name, const std::string opt_name,
+                                  bool gst_prfx)
+{
+    const std::string name;
+    std::vector <std::string> names;
+
+    names.push_back(param_name); // eg. framerate or gst.codec
+    names.push_back(settings.codec_name + "." + param_name); // eg. gst.vp8.prop
+
+    if (!settings.encoder.empty()) {
+        names.push_back(settings.encoder + "." + param_name); // eg. gst.vp8enc.prop
+    }
+
+    for (auto name: names) {
+        if (opt_name == (gst_prfx ? "gst." : "") + name) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void GstreamerPlugin::UpdateOptions(const ConfigureOption *options)
 {
     for (; options->name; ++options) {
         const std::string name = options->name;
         const std::string value = options->value;
 
-        if (name == "framerate") {
+        if (WantOption("framerate", name, false)) {
             try {
                 settings.fps = std::stoi(value);
+
             } catch (const std::exception &e) {
-                throw std::runtime_error("Invalid value '" + value + "' for option 'framerate'.");
-            }
-        } else if (name == "gst.codec") {
-            if (value == "h264") {
-                settings.codec = SPICE_VIDEO_CODEC_TYPE_H264;
-            } else if (value == "vp9") {
-                settings.codec = SPICE_VIDEO_CODEC_TYPE_VP9;
-            } else if (value == "vp8") {
-                settings.codec = SPICE_VIDEO_CODEC_TYPE_VP8;
-            } else if (value == "mjpeg") {
-                settings.codec = SPICE_VIDEO_CODEC_TYPE_MJPEG;
-            } else if (value == "h265") {
-                settings.codec = SPICE_VIDEO_CODEC_TYPE_H265;
-            } else {
-                throw std::runtime_error("Invalid value '" + value + "' for option 'gst.codec'.");
+                throw std::runtime_error("Invalid value '" + value + "' "
+                                         "for option '" + name + "'.");
             }
-        } else if (name == "gst.encoder") {
-            settings.encoder = value;
-        } else if (name == "gst.prop") {
+        } else if (WantOption("prop", name, true)) {
             size_t pos = value.find('=');
             if (pos == 0 || pos >= value.size() - 1) {
                 gst_syslog(LOG_WARNING, "Property input is invalid ('%s' Ignored)", value.c_str());
                 continue;
             }
-            settings.prop_pairs.push_back(make_pair(value.substr(0, pos), value.substr(pos + 1)));
+            // add or update value in settings array
+            bool already_set = false;
+            const auto &new_name = value.substr(0, pos);
+            const auto &new_value = value.substr(pos + 1);
+
+            for (auto &prop : settings.prop_pairs) {
+                if (prop.first != new_name) {
+                    continue;
+                }
+                prop.second = new_value;
+                already_set = true;
+                break;
+            }
+            if (!already_set) {
+                settings.prop_pairs.push_back(make_pair(new_name, new_value));
+            }
         }
     }
 }
 
+void GstreamerPlugin::ParseOptions(const ConfigureOption *options,
+                                   const std::string codec_value)
+{
+    const std::string encoder_name;
+    size_t has_encoder = codec_value.find('=');
+
+    if (has_encoder != std::string::npos) {
+        settings.codec_name = codec_value.substr(0, has_encoder);
+        settings.encoder = codec_value.substr(has_encoder + 1);
+
+    } else {
+        settings.codec_name = codec_value;
+    }
+
+    if (settings.codec_name == "mjpeg") {
+        settings.codec = SPICE_VIDEO_CODEC_TYPE_MJPEG;
+    } else if (settings.codec_name == "h264") {
+        settings.codec = SPICE_VIDEO_CODEC_TYPE_H264;
+    } else if (settings.codec_name == "h265") {
+        settings.codec = SPICE_VIDEO_CODEC_TYPE_H265;
+    } else if (settings.codec_name == "vp8") {
+        settings.codec = SPICE_VIDEO_CODEC_TYPE_VP8;
+    } else if (settings.codec_name == "vp9") {
+        settings.codec = SPICE_VIDEO_CODEC_TYPE_VP9;
+    } else {
+        throw std::runtime_error("Invalid value '" + settings.codec_name +
+                                 "' for option 'gst.codec'.");
+    }
+
+    UpdateOptions(options);
+}
+
 }}} //namespace spice::streaming_agent::gstreamer_plugin
 
 using namespace spice::streaming_agent::gstreamer_plugin;
@@ -467,11 +525,20 @@  SPICE_STREAMING_AGENT_PLUGIN(agent)
 {
     gst_init(nullptr, nullptr);
 
-    auto plugin = std::make_shared<GstreamerPlugin>();
+    bool has_plugins = false;
+    auto options = agent->Options();
+    for (; options->name; ++options) {
+        const std::string name = options->name;
+        const std::string value = options->value;
 
-    plugin->ParseOptions(agent->Options());
+        if (name == "gst.codec") {
+            auto plugin = std::make_shared<GstreamerPlugin>();
 
-    agent->Register(plugin);
+            plugin->ParseOptions(agent->Options(), value);
+            agent->Register(plugin);
+            has_plugins = true;
+        }
+    }
 
-    return true;
+    return has_plugins;
 }