<div dir="ltr"><br>---<br> modules/gui/qt/Makefile.am                    |   1 +<br> .../qt/components/sout/profile_selector.cpp   |   2 +-<br> .../gui/qt/components/sout/sout_widgets.cpp   |   1 +<br> modules/gui/qt/dialogs/sout.hpp               |  71 +---------<br> modules/gui/qt/util/soutmrl.cpp               |  81 +++++++++++<br> modules/gui/qt/util/soutmrl.hpp               | 128 ++++++++++++++++++<br> 6 files changed, 213 insertions(+), 71 deletions(-)<br> create mode 100644 modules/gui/qt/util/soutmrl.cpp<br> create mode 100644 modules/gui/qt/util/soutmrl.hpp<br><br>diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am<br>index af4a5d05cc..71f96a5910 100644<br>--- a/modules/gui/qt/Makefile.am<br>+++ b/modules/gui/qt/Makefile.am<br>@@ -186,6 +186,7 @@ libqt_plugin_la_SOURCES = \<br>   gui/qt/util/customwidgets.cpp gui/qt/util/customwidgets.hpp \<br>        gui/qt/util/searchlineedit.cpp gui/qt/util/searchlineedit.hpp \<br>      gui/qt/util/registry.cpp gui/qt/util/registry.hpp \<br>+  gui/qt/util/soutmrl.cpp gui/qt/util/soutmrl.hpp \<br>    gui/qt/util/qt_dirs.cpp gui/qt/util/qt_dirs.hpp \<br>    gui/qt/util/validators.cpp gui/qt/util/validators.hpp \<br>      gui/qt/util/buttons/BrowseButton.cpp \<br>diff --git a/modules/gui/qt/components/sout/profile_selector.cpp b/modules/gui/qt/components/sout/profile_selector.cpp<br>index 586a01f681..55fc5c9c28 100644<br>--- a/modules/gui/qt/components/sout/profile_selector.cpp<br>+++ b/modules/gui/qt/components/sout/profile_selector.cpp<br>@@ -23,7 +23,7 @@<br><br> #include "components/sout/profile_selector.hpp"<br> #include "components/sout/profiles.hpp"<br>-#include "dialogs/sout.hpp"<br>+#include "util/soutmrl.hpp"<br><br> #include <QHBoxLayout><br> #include <QToolButton><br>diff --git a/modules/gui/qt/components/sout/sout_widgets.cpp b/modules/gui/qt/components/sout/sout_widgets.cpp<br>index 8eb9cccea3..88e64f42cc 100644<br>--- a/modules/gui/qt/components/sout/sout_widgets.cpp<br>+++ b/modules/gui/qt/components/sout/sout_widgets.cpp<br>@@ -26,6 +26,7 @@<br> #include "components/sout/sout_widgets.hpp"<br> #include "dialogs/sout.hpp"<br> #include "util/qt_dirs.hpp"<br>+#include "util/soutmrl.hpp"<br> #include <vlc_intf_strings.h><br><br> #include <QGroupBox><br>diff --git a/modules/gui/qt/dialogs/sout.hpp b/modules/gui/qt/dialogs/sout.hpp<br>index 909dcf80bf..9380a0bc5b 100644<br>--- a/modules/gui/qt/dialogs/sout.hpp<br>+++ b/modules/gui/qt/dialogs/sout.hpp<br>@@ -31,81 +31,12 @@<br><br> #include "ui_sout.h"<br> #include "util/qvlcframe.hpp"<br>+#include "util/soutmrl.hpp"<br><br> #include <QWizard><br><br> class QPushButton;<br><br>-class SoutMrl<br>-{<br>-public:<br>-    SoutMrl( const QString& head = "")<br>-    {<br>-        mrl = head;<br>-        b_first = true;<br>-        b_has_bracket = false;<br>-    }<br>-<br>-    QString getMrl()<br>-    {<br>-        return mrl;<br>-    }<br>-<br>-    void begin( const QString& module )<br>-    {<br>-        if( !b_first )<br>-            mrl += ":";<br>-        b_first = false;<br>-<br>-        mrl += module;<br>-        b_has_bracket = false;<br>-    }<br>-    void end()<br>-    {<br>-        if( b_has_bracket )<br>-            mrl += "}";<br>-    }<br>-    void option( const QString& option, const QString& value = "" )<br>-    {<br>-        if( !b_has_bracket )<br>-            mrl += "{";<br>-        else<br>-            mrl += ",";<br>-        b_has_bracket = true;<br>-<br>-        mrl += option;<br>-<br>-        if( !value.isEmpty() )<br>-        {<br>-            char *psz = config_StringEscape( qtu(value) );<br>-            if( psz )<br>-            {<br>-                mrl += "=" + qfu( psz );<br>-                free( psz );<br>-            }<br>-        }<br>-    }<br>-    void option( const QString& name, const int i_value, const int i_precision = 10 )<br>-    {<br>-        option( name, QString::number( i_value, i_precision ) );<br>-    }<br>-    void option( const QString& name, const double f_value )<br>-    {<br>-        option( name, QString::number( f_value ) );<br>-    }<br>-<br>-    void option( const QString& name, const QString& base, const int i_value, const int i_precision = 10 )<br>-    {<br>-        option( name, base + ":" + QString::number( i_value, i_precision ) );<br>-    }<br>-<br>-private:<br>-    QString mrl;<br>-    bool b_has_bracket;<br>-    bool b_first;<br>-};<br>-<br>-<br> class SoutDialog : public QWizard<br> {<br>     Q_OBJECT<br>diff --git a/modules/gui/qt/util/soutmrl.cpp b/modules/gui/qt/util/soutmrl.cpp<br>new file mode 100644<br>index 0000000000..92aa10ae90<br>--- /dev/null<br>+++ b/modules/gui/qt/util/soutmrl.cpp<br>@@ -0,0 +1,81 @@<br>+#include "soutmrl.hpp"<br>+<br>+QString MrlModule::to_string() const<br>+{<br>+    QString s = moduleName;<br>+<br>+    if( options.size() > 0 )<br>+    {<br>+        s += "{";<br>+    }<br>+    OptionsType::const_iterator it;<br>+    for( it=options.begin(); it!=options.end(); )<br>+    {<br>+        s += it->first;<br>+        if( it->second.to_string().compare("") != 0 )<br>+        {<br>+            s += "=" + it->second.to_string();<br>+        }<br>+        ++it;<br>+        if( it != options.end() )<br>+        {<br>+            s += ",";<br>+        }<br>+    }<br>+    if( options.size() > 0 )<br>+    {<br>+        s += "}";<br>+    }<br>+    return s;<br>+}<br>+<br>+void MrlModule::option( const QString& option, const MrlOption& value )<br>+{<br>+    options.append( OptionPairType( option, value ) );<br>+}<br>+void MrlModule::option( const QString& option )<br>+{<br>+    options.append( OptionPairType( option, "" ) );<br>+}<br>+<br>+QString SoutMrl::getMrl() const<br>+{<br>+    QString mrl = hdr;<br>+    for( int m=0; m<modules.size(); m++ )<br>+    {<br>+        mrl += modules[m].to_string();<br>+        if( m < modules.size() - 1 )<br>+        {<br>+            mrl += ":";<br>+        }<br>+    }<br>+<br>+    return mrl;<br>+}<br>+<br>+void SoutMrl::option( const QString& name, const QString& value )<br>+{<br>+    if( modules.size() > 0 )<br>+    {<br>+        modules.back().option( name, value );<br>+    }<br>+}<br>+void SoutMrl::option( const QString& name, const int i_value, const int i_precision )<br>+{<br>+    option( name, QString::number( i_value, i_precision ) );<br>+}<br>+void SoutMrl::option( const QString& name, const double f_value )<br>+{<br>+    option( name, QString::number( f_value ) );<br>+}<br>+void SoutMrl::option( const QString& name, const QString& base, const int i_value, const int i_precision )<br>+{<br>+    option( name, base + ":" + QString::number( i_value, i_precision ) );<br>+}<br>+void SoutMrl::option( const QString& name, const MrlModule& nested )<br>+{<br>+    if( modules.size() > 0 )<br>+    {<br>+        modules.back().option( name, nested );<br>+    }<br>+}<br>\ No newline at end of file<br>diff --git a/modules/gui/qt/util/soutmrl.hpp b/modules/gui/qt/util/soutmrl.hpp<br>new file mode 100644<br>index 0000000000..f584e1162b<br>--- /dev/null<br>+++ b/modules/gui/qt/util/soutmrl.hpp<br>@@ -0,0 +1,128 @@<br>+/*****************************************************************************<br>+ * soutmrl.hpp: A class to generate sout MRLs<br>+ ****************************************************************************<br>+ *<br>+ *<br>+ *<br>+ *<br>+ * What should we put here?<br>+ * Authors: Jérôme Froissart <<a href="mailto:software@froissart.eu">software@froissart.eu</a>><br>+ *<br>+ *<br>+ *<br>+ *<br>+ *<br>+ *****************************************************************************/<br>+<br>+#ifndef VLC_QT_SOUTMRL_HPP_<br>+#define VLC_QT_SOUTMRL_HPP_<br>+<br>+#include "qt.hpp"<br>+<br>+#include <QMap><br>+<br>+<br>+class MrlOption;<br>+<br>+class MrlModule<br>+{<br>+public:<br>+    MrlModule( const QString& name ) :<br>+        moduleName( name )<br>+    {<br>+    }<br>+<br>+    void option( const QString& option, const MrlOption& value );<br>+    void option( const QString& option );<br>+    QString to_string() const;<br>+<br>+private:<br>+    typedef QPair<QString, MrlOption> OptionPairType;<br>+    typedef QList<OptionPairType> OptionsType;<br>+    const QString moduleName;<br>+    OptionsType options;<br>+};<br>+<br>+<br>+class MrlOption<br>+{<br>+public:<br>+    MrlOption( const QString& value ) :<br>+        kind( String ),<br>+        stringValue( value ),<br>+        nestedModule("")<br>+    {}<br>+<br>+    MrlOption( const char* s ) :<br>+        MrlOption( QString(s) )<br>+    {}<br>+<br>+    MrlOption( const MrlModule& module ) :<br>+        kind(Nested),<br>+        nestedModule(module)<br>+    {}<br>+<br>+    QString to_string() const{<br>+        if( kind == String )<br>+        {<br>+            return stringValue;<br>+        }<br>+        else<br>+        {<br>+            return nestedModule.to_string();<br>+        }<br>+    }<br>+<br>+private:<br>+    enum Kind{ String, Nested };<br>+    const Kind kind;<br>+    const QString stringValue;<br>+    const MrlModule nestedModule;<br>+};<br>+<br>+<br>+/// This class helps building MRLs<br>+///<br>+/// An MRL has the following structure:<br>+///  * a header<br>+///  * any number of modules, which have<br>+///     - a name<br>+///     - any number of key(=value) pairs<br>+///       values can be nested modules<br>+///<br>+/// Example of MRL: HEADERmodule1{a,b=val}:module2:module3{opt,arg=1,stuff=nestedModule{subkey=subvalue}}<br>+class SoutMrl<br>+{<br>+public:<br>+    SoutMrl( const QString& header="" ) :<br>+        hdr(header)<br>+    {<br>+    }<br>+<br>+    MrlModule& begin( const QString& module )<br>+    {<br>+        modules.append( MrlModule( module ) );<br>+        return modules.back();<br>+    }<br>+<br>+    // Useless, kept for compatibility with an older API<br>+    void end()<br>+    {<br>+    }<br>+<br>+    // These should be only in MRLModule, but they are kept in this parent class for compatibility with an older API<br>+    void option( const QString& name, const QString& value = "" );<br>+    void option( const QString& name, const int i_value, const int i_precision = 10 );<br>+    void option( const QString& name, const double f_value );<br>+    void option( const QString& name, const QString& base, const int i_value, const int i_precision = 10 );<br>+    void option( const QString& name, const MrlModule& nested );<br>+<br>+    QString getMrl() const;<br>+<br>+private:<br>+    QString hdr;<br>+    QList<MrlModule> modules;<br>+};<br>+<br>+#endif // include guard<br>+<br>--<br>2.20.1<br><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le jeu. 19 sept. 2019 à 22:44, Jérôme Froissart <<a href="mailto:software@froissart.eu">software@froissart.eu</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi,<br><br>When converting a video, an MRL is poorly handled.<br>When the user checks the "deinterlace" statement, convert.cpp inserts this statement after removing closing braces to the MRL (using "mrl.remove( '}' );").<br>This causes issues in case the passed mrl already contains closing braces (which is the case when the selected profile has custom video codec options).<br>A quick-and-dirty fix would be to remove only the latest brace before inserting the "deinterlace" statement, but, well...that would be quick-and-dirty.<br><br>I rather send you a more object-oriented implementation of SoutMrl, that avoids problems because it better handles "modules" and "options" within SoutMrls.<br>The first and second patches are drop-in replacement for the current implementation.<br>Then the thirs patch actually fixes the bug.<br><br>To reproduce the bug:<br>* open VLC, then "Open capture device" (I am not using the English version, my description can be slightly wrong)<br>* Choose whatever source you want, e.g. screen<br>* Choose "convert"<br>* Choose a profile, and edit it. In the "video codec" tab, add a custom argument (e.g. "preset=faster", which makes sense with the x264 codec)<br>* Start the process. VLC will fail because the MRL will lack a closing brace.<br>  You can see the MRL in the "messages" dialog.<br>  Without my patch, the MRL may look like<br>  sout=#transcode{vcodec=MJPG,venc=x264{preset=faster,scale=Auto,acodec=mp4a,ab=128,channels=1,samplerate=44100,scodec=none,deinterlace}:std{access=file{no-overwrite},mux=mp4,dst='/path/to/output.mp4'}<br>  whereas my patch correctly handles the closing brace<br><br><br>Sorry, I did not know what to put in the .hpp header, I could have copied/pasted the GPL license, but I do not know how to properly set the copyright. Maybe you'll know this better than I do.<br><br>These patches can be applied (almost painlessly) to the 3.0.8 branch (there may only be slight issues with a Makefile.am and a convert.cpp that was slightly changed)<br><br><br>Jérôme Froissart (3):<br>  Re-implemented SoutMrl in a more object-oriented way<br>  Additions to the new SoutMrl implementation<br>  Deinterlace can be associated with codec arguments<br><br> modules/gui/qt/Makefile.am                    |   1 +<br> .../qt/components/sout/profile_selector.cpp   |  80 +++++-----<br> .../qt/components/sout/profile_selector.hpp   |   5 +-<br> .../gui/qt/components/sout/sout_widgets.cpp   |   1 +<br> modules/gui/qt/dialogs/convert.cpp            |  37 +++--<br> modules/gui/qt/dialogs/sout.cpp               |   4 +-<br> modules/gui/qt/dialogs/sout.hpp               |  71 +--------<br> modules/gui/qt/util/soutmrl.cpp               |  86 +++++++++++<br> modules/gui/qt/util/soutmrl.hpp               | 145 ++++++++++++++++++<br> 9 files changed, 301 insertions(+), 129 deletions(-)<br> create mode 100644 modules/gui/qt/util/soutmrl.cpp<br> create mode 100644 modules/gui/qt/util/soutmrl.hpp<br><br>--<br>2.20.1<br><br></div>
</blockquote></div>