<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On 14 Nov 2017, at 16:41, Frediano Ziglio <<a href="mailto:fziglio@redhat.com" class="">fziglio@redhat.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div style="" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class=""><br class="">From: Christophe de Dinechin <<a href="mailto:dinechin@redhat.com" class="">dinechin@redhat.com</a>><br class=""><br class="">Several functional objectives:<br class=""><br class="">1. Be able to share some common options, e.g. fps<br class="">2. Prepare for the possibility to update options on the fly<br class=""><br class="">Also get rid of the null-terminated C++ vector<br class=""><br class="">Signed-off-by: Christophe de Dinechin <<a href="mailto:dinechin@redhat.com" class="">dinechin@redhat.com</a>><br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">This change is neither ABI not API compatible. Why?</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div><div>Compatible with what? We are still building the very first versions of this agent,</div><div>and nobody but us has tested it yet.</div><div><br class=""></div><div>So I think it’s still the right time to fix things that are broken. In particular the fact</div><div>that the curent API does not make it possible to adjust settings on the fly.</div><div>Half of the experiment I have been doing can’t be done with the curent API.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">---<br class="">include/spice-streaming-agent/plugin.hpp | 120<br class="">+++++++++++++++++++++++++++----<br class="">m4/spice-compile-warnings.m4 | 2 +<br class="">src/concrete-agent.cpp | 77 ++++++++++++++++----<br class="">src/concrete-agent.hpp | 30 ++++----<br class="">src/mjpeg-fallback.cpp | 90 +++++++++++------------<br class="">5 files changed, 227 insertions(+), 92 deletions(-)<br class=""><br class="">diff --git a/include/spice-streaming-agent/plugin.hpp<br class="">b/include/spice-streaming-agent/plugin.hpp<br class="">index 607fabf..41ad11f 100644<br class="">--- a/include/spice-streaming-agent/plugin.hpp<br class="">+++ b/include/spice-streaming-agent/plugin.hpp<br class="">@@ -8,6 +8,11 @@<br class="">#define SPICE_STREAMING_AGENT_PLUGIN_HPP<br class=""><br class="">#include <spice/enums.h><br class="">+#include <climits><br class="">+#include <sstream><br class="">+#include <string><br class="">+#include <vector><br class="">+<br class=""><br class="">/*!<br class=""> * \file<br class="">@@ -20,6 +25,7 @@<br class="">namespace SpiceStreamingAgent {<br class=""><br class="">class FrameCapture;<br class="">+class Agent;<br class=""><br class="">/*!<br class=""> * Plugin version, only using few bits, schema is 0xMMmm<br class="">@@ -42,13 +48,23 @@ enum Ranks : unsigned {<br class="">};<br class=""><br class="">/*!<br class="">- * Configuration option.<br class="">- * An array of these will be passed to the plugin.<br class="">- * Simply a couple name and value passed as string.<br class="">- * For instance "framerate" and "20".<br class="">+ * Settings that are common to all plugins<br class=""> */<br class="">-struct ConfigureOption<br class="">+struct Settings<br class="">{<br class="">+ unsigned framerate = 30; // Frames per second (1-240)<br class="">+ unsigned quality = 80; // Normalized in 0-100 (100=high)<br class="">+ unsigned avg_bitrate = 3000000; // Target average bitrate in bps<br class="">+ unsigned max_bitrate = 8000000; // Target maximum bitrate<br class="">+};<br class="">+<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">How are you going to extend this?</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div><div>If we see other shared settings, now is a good time to think about them.</div><div>Later additions to these settings would require an ABI break.</div><div><br class=""></div><div>(Of note, we have the same issue with the command line options too)</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">+/*!<br class="">+ * Command line option<br class="">+ */<br class="">+struct Option<br class="">+{<br class="">+ Option(const char *name, const char *value)<br class="">+ : name(name), value(value) {}<br class=""> const char *name;<br class=""> const char *value;<br class="">};<br class="">@@ -59,6 +75,9 @@ struct ConfigureOption<br class=""> * A plugin module can register multiple Plugin interfaces to handle<br class=""> * multiple codecs. In this case each Plugin will report data for a<br class=""> * specific codec.<br class="">+ *<br class="">+ * A plugin will typically extend the Settings struct to add its own<br class="">+ * settings.<br class=""> */<br class="">class Plugin<br class="">{<br class="">@@ -66,7 +85,17 @@ public:<br class=""> /*!<br class=""> * Allows to free the plugin when not needed<br class=""> */<br class="">- virtual ~Plugin() {};<br class="">+ virtual ~Plugin() {}<br class="">+<br class="">+ /*!<br class="">+ * Return the name of the plugin, e.g. for command-line usage<br class="">information<br class="">+ */<br class="">+ virtual const char *Name() = 0;<br class="">+<br class="">+ /*!<br class="">+ * Return usage information for the plug-in, e.g. command-line options<br class="">+ */<br class="">+ virtual const char *Usage() = 0;<br class=""><br class=""> /*!<br class=""> * Request an object for getting frames.<br class="">@@ -89,6 +118,19 @@ public:<br class=""> * Get video codec used to encode last frame<br class=""> */<br class=""> virtual SpiceVideoCodecType VideoCodecType() const=0;<br class="">+<br class="">+ /*!<br class="">+ * Return the concrete Settings for this plugin<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">For which usage? The result is not const, should we expect to</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">change them? How to notify the plugin of changes?</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div><div>See actual call. The usage is to let the agent manage options for</div><div>plugins without knowing the details of their settings.</div><div><br class=""></div><div>Ideally, I would have preferred a base class to deal with that, but</div><div>that does not work, because the base class code is in the agent and</div><div>the derived class in dynamically loaded plugins, so dlopen fails.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">+ */<br class="">+ virtual Settings & PluginSettings() = 0;<br class="">+<br class="">+ /*!<br class="">+ * Apply a given option to the plugin settings.<br class="">+ * \return true if the option is valid, false if it is not recognized<br class="">+ * If there is an error applying the settings, set \arg error.<br class="">+ * If this returns false, agent will try StandardOption.<br class="">+ */<br class="">+ virtual bool ApplyOption(const Option &o, std::string &error) = 0;<br class="">};<br class=""><br class="">/*!<br class="">@@ -113,24 +155,74 @@ public:<br class=""> * Check if a given plugin version is compatible with this agent<br class=""> * \return true is compatible<br class=""> */<br class="">- virtual bool PluginVersionIsCompatible(unsigned pluginVersion) const=0;<br class="">+ virtual bool PluginVersionIsCompatible(unsigned pluginVersion) const =<br class="">0;<br class=""><br class=""> /*!<br class="">- * Register a plugin in the system.<br class="">+ * Register a plugin in the system, which becomes the owner and should<br class="">delete it.<br class=""> */<br class="">- virtual void Register(Plugin& plugin)=0;<br class="">+ virtual void Register(Plugin *plugin)=0;<br class=""><br class=""> /*!<br class="">- * Get options array.<br class="">- * Array is terminated with {nullptr, nullptr}.<br class="">- * Never nullptr.<br class="">- * \todo passing options to entry point instead?<br class="">+ * Apply the standard options to the given settings (base Settings<br class="">class)<br class=""> */<br class="">- virtual const ConfigureOption* Options() const=0;<br class="">+ virtual bool StandardOption(Settings &, const Option &, std::string<br class="">&error) = 0;<br class="">+<br class="">};<br class=""><br class="">typedef bool PluginInitFunc(SpiceStreamingAgent::Agent* agent);<br class=""><br class="">+/*!<br class="">+ * Helper to get integer values from command-line options<br class="">+ */<br class="">+<br class="">+static inline int IntOption(const Option &opt, std::string &error,<br class="">+ int min=INT_MIN, int max=INT_MAX, bool<br class="">sizeSuffixOK = false)<br class="">+{<br class="">+ std::string name = opt.name;<br class="">+ std::string value = opt.value;<br class="">+ std::ostringstream err;<br class="">+ std::istringstream input(value);<br class="">+ int result = 0;<br class="">+ input >> result;<br class="">+<br class="">+ if (!input.fail() && !input.eof() && sizeSuffixOK) {<br class="">+ std::string suffix;<br class="">+ input >> suffix;<br class="">+ bool ok = false;<br class="">+ if (!input.fail() && suffix.length() == 1) {<br class="">+ switch (suffix[0]) {<br class="">+ case 'b': case 'B': ok = true; break;<br class="">+ case 'k': case 'K': ok = true; result *= 1000; break;<br class="">+ case 'm': case 'M': ok = true; result *= 1000000; break;<br class="">+ default: break;<br class="">+ }<br class="">+ }<br class="">+ if (!ok) {<br class="">+ err << "Unknown number suffix " << suffix<br class="">+ << " for " << name << "\n";<br class="">+ error = err.str();<br class="">+ }<br class="">+ }<br class="">+<br class="">+ if (input.fail()) {<br class="">+ err << "Value " << value << " for " << name << " is not a number\n";<br class="">+ error = err.str();<br class="">+ }<br class="">+ if (!input.eof()) {<br class="">+ err << "Value " << value << " for " << name << " does not end like<br class="">an integer\n";<br class="">+ error = err.str();<br class="">+ }<br class="">+ if (result < min || result > max) {<br class="">+ err << "The value " << value << " for " << name<br class="">+ << " is out of range (must be in " << min << ".." << max <<<br class="">")\n";<br class="">+ error = err.str(); // May actually combine an earlier error<br class="">+ result = (min + max) / 2; // Give a value acceptable by caller<br class="">+ }<br class="">+<br class="">+ return result;<br class="">+}<br class="">+<br class="">+<br class="">}<br class=""><br class="">#ifndef SPICE_STREAMING_AGENT_PROGRAM<br class="">diff --git a/m4/spice-compile-warnings.m4 b/m4/spice-compile-warnings.m4<br class="">index 66d7179..9e8bb6e 100644<br class="">--- a/m4/spice-compile-warnings.m4<br class="">+++ b/m4/spice-compile-warnings.m4<br class="">@@ -86,6 +86,8 @@ AC_DEFUN([SPICE_COMPILE_WARNINGS],[<br class=""> dontwarn="$dontwarn -Wpointer-sign"<br class=""> dontwarn="$dontwarn -Wpointer-to-int-cast"<br class=""> dontwarn="$dontwarn -Wstrict-prototypes"<br class="">+ dontwarn="$dontwarn -Wsuggest-final-types"<br class="">+ dontwarn="$dontwarn -Wsuggest-final-methods"<br class=""><br class=""> # We want to enable these, but need to sort out the<br class=""> # decl mess with gtk/generated_*.c<br class="">diff --git a/src/concrete-agent.cpp b/src/concrete-agent.cpp<br class="">index 192054a..59f11b2 100644<br class="">--- a/src/concrete-agent.cpp<br class="">+++ b/src/concrete-agent.cpp<br class="">@@ -9,6 +9,7 @@<br class="">#include <syslog.h><br class="">#include <glob.h><br class="">#include <dlfcn.h><br class="">+#include <mutex><br class=""><br class="">#include "concrete-agent.hpp"<br class="">#include "static-plugin.hpp"<br class="">@@ -28,7 +29,11 @@ static inline unsigned MinorVersion(unsigned version)<br class=""><br class="">ConcreteAgent::ConcreteAgent()<br class="">{<br class="">- options.push_back(ConcreteConfigureOption(nullptr, nullptr));<br class="">+}<br class="">+<br class="">+void ConcreteAgent::Register(Plugin *plugin)<br class="">+{<br class="">+ plugins.push_back(shared_ptr<Plugin>(plugin));<br class="">}<br class=""><br class="">bool ConcreteAgent::PluginVersionIsCompatible(unsigned pluginVersion) const<br class="">@@ -38,22 +43,34 @@ bool ConcreteAgent::PluginVersionIsCompatible(unsigned<br class="">pluginVersion) const<br class=""> MinorVersion(version) >= MinorVersion(pluginVersion);<br class="">}<br class=""><br class="">-void ConcreteAgent::Register(Plugin& plugin)<br class="">+bool ConcreteAgent::StandardOption(Settings &settings,<br class="">+ const Option &option,<br class="">+ string &error)<br class="">{<br class="">- plugins.push_back(shared_ptr<Plugin>(&plugin));<br class="">-}<br class="">+ string name = option.name;<br class="">+ if (name == "framerate" || name == "fps") {<br class="">+ settings.framerate = IntOption(option, error, 1, 240);<br class="">+ return true;<br class="">+ }<br class="">+ if (name == "quality") {<br class="">+ settings.quality = IntOption(option, error, 0, 100);<br class="">+ return true;<br class="">+ }<br class="">+ if (name == "avg_bitrate" || name == "bitrate") {<br class="">+ settings.avg_bitrate = IntOption(option, error, 10000, 32000000,<br class="">true);<br class="">+ return true;<br class="">+ }<br class="">+ if (name == "max_bitrate") {<br class="">+ settings.max_bitrate = IntOption(option, error, 10000, 32000000,<br class="">true);<br class="">+ return true;<br class="">+ }<br class=""><br class="">-const ConfigureOption* ConcreteAgent::Options() const<br class="">-{<br class="">- static_assert(sizeof(ConcreteConfigureOption) ==<br class="">sizeof(ConfigureOption),<br class="">- "ConcreteConfigureOption should be binary compatible with<br class="">ConfigureOption");<br class="">- return static_cast<const ConfigureOption*>(&options[0]);<br class="">+ return false; // Unrecognized option<br class="">}<br class=""><br class="">void ConcreteAgent::AddOption(const char *name, const char *value)<br class="">{<br class="">- // insert before the last {nullptr, nullptr} value<br class="">- options.insert(--options.end(), ConcreteConfigureOption(name, value));<br class="">+ options.push_back(Option(name, value));<br class="">}<br class=""><br class="">void ConcreteAgent::LoadPlugins(const char *directory)<br class="">@@ -81,7 +98,8 @@ void ConcreteAgent::LoadPlugin(const char *plugin_filename)<br class="">{<br class=""> void *dl = dlopen(plugin_filename, RTLD_LOCAL|RTLD_NOW);<br class=""> if (!dl) {<br class="">- syslog(LOG_ERR, "error loading plugin %s", plugin_filename);<br class="">+ syslog(LOG_ERR, "error loading plugin %s: %s",<br class="">+ plugin_filename, dlerror());<br class=""> return;<br class=""> }<br class=""><br class="">@@ -103,7 +121,7 @@ FrameCapture *ConcreteAgent::GetBestFrameCapture()<br class=""> vector<pair<unsigned, shared_ptr<Plugin>>> sorted_plugins;<br class=""><br class=""> // sort plugins base on ranking, reverse order<br class="">- for (const auto& plugin: plugins) {<br class="">+ for (auto& plugin: plugins) {<br class=""> sorted_plugins.push_back(make_pair(plugin->Rank(), plugin));<br class=""> }<br class=""> sort(sorted_plugins.rbegin(), sorted_plugins.rend());<br class="">@@ -113,6 +131,7 @@ FrameCapture *ConcreteAgent::GetBestFrameCapture()<br class=""> if (plugin.first == DontUse) {<br class=""> break;<br class=""> }<br class="">+ ApplyOptions(plugin.second.get());<br class=""> FrameCapture *capture = plugin.second->CreateCapture();<br class=""> if (capture) {<br class=""> return capture;<br class="">@@ -120,3 +139,35 @@ FrameCapture *ConcreteAgent::GetBestFrameCapture()<br class=""> }<br class=""> return nullptr;<br class="">}<br class="">+<br class="">+void ConcreteAgent::ApplyOptions(Plugin *plugin)<br class="">+{<br class="">+ bool usage = false;<br class="">+ for (const auto &opt : options) {<br class="">+ string error;<br class="">+ bool known = plugin->ApplyOption(opt, error);<br class="">+ if (!known) {<br class="">+ Settings &settings = plugin->PluginSettings();<br class="">+ known = StandardOption(settings, opt, error);<br class="">+ }<br class="">+ if (!known) {<br class="">+ syslog(LOG_ERR, "Option %s not recognized by plugin %s",<br class="">+ opt.name, plugin->Name());<br class="">+ usage = true;<br class="">+ }<br class="">+ if (error != "") {<br class="">+ syslog(LOG_ERR, "Plugin %s did not accept setting %s=%s: %s",<br class="">+ plugin->Name(), opt.name, opt.value, error.c_str());<br class="">+ usage = true;<br class="">+ }<br class="">+ }<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">How do you deal with options supported by another plugin but not this</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">and not standard? Looks like like an error. I think should be ignored.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div><div>This is why it goes to syslog but does not throw. Maybe log a warning instead</div><div>of an error?</div><br class=""><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">+ if (usage)<br class="">+ {<br class="">+ static std:: once_flag once;<br class="">+ std::call_once(once, [plugin]()<br class="">+ {<br class="">+ const char *usage = plugin->Usage();<br class="">+ syslog(LOG_ERR, "Usage: for plugin %s: %s", plugin->Name(),<br class="">usage);<br class="">+ });<br class="">+ }<br class="">+}<br class="">diff --git a/src/concrete-agent.hpp b/src/concrete-agent.hpp<br class="">index 828368b..b3d4e06 100644<br class="">--- a/src/concrete-agent.hpp<br class="">+++ b/src/concrete-agent.hpp<br class="">@@ -12,33 +12,33 @@<br class=""><br class="">namespace SpiceStreamingAgent {<br class=""><br class="">-struct ConcreteConfigureOption: ConfigureOption<br class="">-{<br class="">- ConcreteConfigureOption(const char *name, const char *value)<br class="">- {<br class="">- this->name = name;<br class="">- this->value = value;<br class="">- }<br class="">-};<br class="">-<br class="">class ConcreteAgent final : public Agent<br class="">{<br class="">public:<br class=""> ConcreteAgent();<br class="">+<br class="">+public:<br class="">+ // Implementation of the Agent class virtual methods<br class=""> unsigned Version() const override {<br class=""> return PluginVersion;<br class=""> }<br class="">- void Register(Plugin& plugin) override;<br class="">- const ConfigureOption* Options() const override;<br class="">- void LoadPlugins(const char *directory);<br class="">- // pointer must remain valid<br class="">+ void Register(Plugin *plugin) override;<br class="">+ bool PluginVersionIsCompatible(unsigned pluginVersion) const override;<br class="">+ bool StandardOption(Settings &settings,<br class="">+ const Option &option,<br class="">+ std::string &error) override;<br class="">+<br class="">+public:<br class="">+ // Interface used by the main agent program<br class=""> void AddOption(const char *name, const char *value);<br class="">+ void LoadPlugins(const char *directory);<br class="">+ void ApplyOptions(Plugin *plugin);<br class=""> FrameCapture *GetBestFrameCapture();<br class="">- bool PluginVersionIsCompatible(unsigned pluginVersion) const override;<br class="">+<br class="">private:<br class=""> void LoadPlugin(const char *plugin_filename);<br class=""> std::vector<std::shared_ptr<Plugin>> plugins;<br class="">- std::vector<ConcreteConfigureOption> options;<br class="">+ std::vector<Option> options;<br class="">};<br class=""><br class="">}<br class="">diff --git a/src/mjpeg-fallback.cpp b/src/mjpeg-fallback.cpp<br class="">index f41e68a..37df01a 100644<br class="">--- a/src/mjpeg-fallback.cpp<br class="">+++ b/src/mjpeg-fallback.cpp<br class="">@@ -41,16 +41,11 @@ static inline uint64_t get_time()<br class="">}<br class=""><br class="">namespace {<br class="">-struct MjpegSettings<br class="">-{<br class="">- int fps;<br class="">- int quality;<br class="">-};<br class=""><br class="">class MjpegFrameCapture final: public FrameCapture<br class="">{<br class="">public:<br class="">- MjpegFrameCapture(const MjpegSettings &settings);<br class="">+ MjpegFrameCapture(Settings &settings);<br class=""> ~MjpegFrameCapture();<br class=""> FrameInfo CaptureFrame() override;<br class=""> void Reset() override;<br class="">@@ -58,8 +53,8 @@ public:<br class=""> return SPICE_VIDEO_CODEC_TYPE_MJPEG;<br class=""> }<br class="">private:<br class="">- MjpegSettings settings;<br class=""> Display *dpy;<br class="">+ Settings &settings;<br class=""><br class=""> vector<uint8_t> frame;<br class=""><br class="">@@ -72,19 +67,20 @@ private:<br class="">class MjpegPlugin final: public Plugin<br class="">{<br class="">public:<br class="">+ virtual const char *Name() override;<br class="">+ virtual const char *Usage() override;<br class=""> FrameCapture *CreateCapture() override;<br class=""> unsigned Rank() override;<br class="">- void ParseOptions(const ConfigureOption *options);<br class="">- SpiceVideoCodecType VideoCodecType() const {<br class="">- return SPICE_VIDEO_CODEC_TYPE_MJPEG;<br class="">- }<br class="">+ SpiceVideoCodecType VideoCodecType() const override;<br class="">+ Settings &PluginSettings() override;<br class="">+ bool ApplyOption(const Option &o, string &error) override;<br class="">private:<br class="">- MjpegSettings settings = { 10, 80 };<br class="">+ Settings settings;<br class="">};<br class="">}<br class=""><br class="">-MjpegFrameCapture::MjpegFrameCapture(const MjpegSettings& settings):<br class="">- settings(settings)<br class="">+MjpegFrameCapture::MjpegFrameCapture(Settings &settings)<br class="">+ : settings(settings)<br class="">{<br class=""> dpy = XOpenDisplay(NULL);<br class=""> if (!dpy)<br class="">@@ -111,7 +107,7 @@ FrameInfo MjpegFrameCapture::CaptureFrame()<br class=""> if (last_time == 0) {<br class=""> last_time = now;<br class=""> } else {<br class="">- const uint64_t delta = 1000000000u / settings.fps;<br class="">+ const uint64_t delta = 1000000000u / settings.framerate;<br class=""> if (now >= last_time + delta) {<br class=""> last_time = now;<br class=""> } else {<br class="">@@ -148,13 +144,13 @@ FrameInfo MjpegFrameCapture::CaptureFrame()<br class=""><br class=""> int format = ZPixmap;<br class=""> // TODO handle errors<br class="">- XImage *image = XGetImage(dpy, win, win_info.x, win_info.y,<br class="">+ XImage *image = XGetImage(dpy, win, win_info.x, win_info.y,<br class=""> win_info.width, win_info.height, AllPlanes,<br class=""> format);<br class=""><br class=""> // TODO handle errors<br class=""> // TODO multiple formats (only 32 bit)<br class="">- write_JPEG_file(frame, settings.quality, (uint8_t*) image->data,<br class="">- image->width, image->height);<br class="">+ write_JPEG_file(frame, settings.quality,<br class="">+ (uint8_t*) image->data, image->width, image->height);<br class=""><br class=""> image->f.destroy_image(image);<br class=""><br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">don't get changes here. Just spaces?</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div>Yes. Grouping all image parameters together as a result of not-published</div><div>intermediate stages on this code.</div><div><br class=""><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">@@ -166,6 +162,18 @@ FrameInfo MjpegFrameCapture::CaptureFrame()<br class=""> return info;<br class="">}<br class=""><br class="">+const char *MjpegPlugin::Name()<br class="">+{<br class="">+ return "MJPEG";<br class="">+}<br class="">+<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">Yes, name is really helpful.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">+const char *MjpegPlugin::Usage()<br class="">+{<br class="">+ return<br class="">+ "quality = [0-100] Set picture quality\n"<br class="">+ "framerate = [1-240] Set number of frames per second\n";<br class="">+}<br class="">+<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">Wondering about the indentation here ("quality" and " = [").</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">There should be a standard maybe. Or a structured way to return</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">these informations.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></div></blockquote><div><br class=""></div><div>I considered returning an array and letting the caller do the formatting.</div><div>And I think you are right, it’s the way to go. I’ll do that.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">FrameCapture *MjpegPlugin::CreateCapture()<br class="">{<br class=""> return new MjpegFrameCapture(settings);<br class="">@@ -176,33 +184,20 @@ unsigned MjpegPlugin::Rank()<br class=""> return FallBackMin;<br class="">}<br class=""><br class="">-void MjpegPlugin::ParseOptions(const ConfigureOption *options)<br class="">+SpiceVideoCodecType MjpegPlugin::VideoCodecType() const<br class="">{<br class="">-#define arg_error(...) syslog(LOG_ERR, ## __VA_ARGS__);<br class="">-<br class="">- for (; options->name; ++options) {<br class="">- const char *name = options->name;<br class="">- const char *value = options->value;<br class="">-<br class="">- if (strcmp(name, "framerate") == 0) {<br class="">- int val = atoi(value);<br class="">- if (val > 0) {<br class="">- settings.fps = val;<br class="">- }<br class="">- else {<br class="">- arg_error("wrong framerate arg %s\n", value);<br class="">- }<br class="">- }<br class="">- if (strcmp(name, "mjpeg.quality") == 0) {<br class="">- int val = atoi(value);<br class="">- if (val > 0) {<br class="">- settings.quality = val;<br class="">- }<br class="">- else {<br class="">- arg_error("wrong mjpeg.quality arg %s\n", value);<br class="">- }<br class="">- }<br class="">- }<br class="">+ return SPICE_VIDEO_CODEC_TYPE_MJPEG;<br class="">+}<br class="">+<br class="">+Settings &MjpegPlugin::PluginSettings()<br class="">+{<br class="">+ return settings;<br class="">+}<br class="">+<br class="">+bool MjpegPlugin::ApplyOption(const Option &o, string &error)<br class="">+{<br class="">+ // This plugin only relies on base options<br class="">+ return false;<br class="">}<br class=""><br class="">static bool<br class="">@@ -211,12 +206,7 @@ mjpeg_plugin_init(Agent* agent)<br class=""> if (agent->Version() != PluginVersion)<br class=""> return false;<br class=""><br class="">- std::unique_ptr<MjpegPlugin> plugin(new MjpegPlugin());<br class="">-<br class="">- plugin->ParseOptions(agent->Options());<br class="">-<br class="">- agent->Register(*plugin.release());<br class="">-<br class="">+ agent->Register(new MjpegPlugin);<br class=""> return true;<br class="">}<br class=""><br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">Frediano</span></div></div></blockquote></div><br class=""></body></html>