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@xxxxxxxxxx> --- src/gst-plugin.cpp | 121 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 27 deletions(-) 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; } -- 2.21.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel