<div dir="ltr">I am sorry, I may not have sent the emails the way you wanted.<div>I never used plain emails to send patches and am not familiar with this technique, so please have forgiveness :-)</div><div><br></div><div>(Maybe you want me to send them again properly? But I wasn't sure about the command I found on your wiki, which send 4 consecutive emails instead of replying to the first one, which is --I think-- what you ask to do)</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le jeu. 19 sept. 2019 à 22:47, 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"><div dir="ltr">From c3e064e8985acf23d7af688167345335f80f568a Mon Sep 17 00:00:00 2001<br>From: Jérôme Froissart <<a href="mailto:software@froissart.eu" target="_blank">software@froissart.eu</a>><br>Date: Thu, 19 Sep 2019 12:48:29 +0200<br>Subject: [PATCH 3/3] Deinterlace can be associated with codec arguments<br><br>Previously, one could not convert a video into a video with both:<br> * deinterlace checked (in the Convert dialog)<br> * custom options (in "Profile edition" dialog, "Video codec" tab)<br>This was due to incorrectly handled braces in a SoutMrl.<br><br>This patches requires to make VLCProfileSelector::getTranscode() return a SoutMrl instead of a plain QString, which eases the handling of the Mrl<br>---<br> .../qt/components/sout/profile_selector.cpp   | 78 +++++++++----------<br> .../qt/components/sout/profile_selector.hpp   |  5 +-<br> modules/gui/qt/dialogs/convert.cpp            | 37 ++++++---<br> modules/gui/qt/dialogs/sout.cpp               |  4 +-<br> 4 files changed, 66 insertions(+), 58 deletions(-)<br><br>diff --git a/modules/gui/qt/components/sout/profile_selector.cpp b/modules/gui/qt/components/sout/profile_selector.cpp<br>index 55fc5c9c28..36867bc91b 100644<br>--- a/modules/gui/qt/components/sout/profile_selector.cpp<br>+++ b/modules/gui/qt/components/sout/profile_selector.cpp<br>@@ -203,7 +203,7 @@ void VLCProfileSelector::updateOptions( int i )<br>     if ( rx.indexIn( options ) != -1 )<br>         return updateOptionsOldFormat( i );<br> <br>-    transcode = "";<br>+    transcode.clear();<br> <br>     QStringList tuples = options.split( ";" );<br>     typedef QHash<QString, QString> proptovalueHashType;<br>@@ -242,8 +242,7 @@ void VLCProfileSelector::updateOptions( int i )<br>     }\<br>     else value = QString()<br> <br>-    SoutMrl smrl;<br>-    smrl.begin( "transcode" );<br>+    transcode.begin( "transcode" );<br> <br>     /* First muxer options */<br>     HASHPICK( "muxer", "mux" );<br>@@ -257,19 +256,19 @@ void VLCProfileSelector::updateOptions( int i )<br> <br>         if ( !value.isEmpty() )<br>         {<br>-            smrl.option( "vcodec", value );<br>+            transcode.option( "vcodec", value );<br> <br>             HASHPICK( "vcodec", "bitrate" );<br>             if ( value.toInt() > 0 )<br>             {<br>-                smrl.option( "vb", value.toInt() );<br>+                transcode.option( "vb", value.toInt() );<br>             }<br> <br>             HASHPICK( "video", "filters" );<br>             if ( !value.isEmpty() )<br>             {<br>                 QStringList valuesList = QUrl::fromPercentEncoding( value.toLatin1() ).split( ";" );<br>-                smrl.option( "vfilter", valuesList.join( ":" ) );<br>+                transcode.option( "vfilter", valuesList.join( ":" ) );<br>             }<br> <br>             /*if ( codec is h264 )*/<br>@@ -286,28 +285,28 @@ void VLCProfileSelector::updateOptions( int i )<br>                     codecoptions << QUrl::fromPercentEncoding( value.toLatin1() );<br> <br>                 if ( codecoptions.count() )<br>-                    smrl.option( "venc",<br>+                    transcode.option( "venc",<br>                         QString("x264{%1}").arg( codecoptions.join(",") ) );<br>             }<br> <br>             HASHPICK( "vcodec", "framerate" );<br>             if ( !value.isEmpty() && value.toInt() > 0 )<br>-                smrl.option( "fps", value );<br>+                transcode.option( "fps", value );<br> <br>             HASHPICK( "vcodec", "scale" );<br>             if ( !value.isEmpty() )<br>-                smrl.option( "scale", value );<br>+                transcode.option( "scale", value );<br> <br>             HASHPICK( "vcodec", "width" );<br>             if ( !value.isEmpty() && value.toInt() > 0 )<br>-                smrl.option( "width", value );<br>+                transcode.option( "width", value );<br> <br>             HASHPICK( "vcodec", "height" );<br>             if ( !value.isEmpty() && value.toInt() > 0 )<br>-                smrl.option( "height", value );<br>+                transcode.option( "height", value );<br>         }<br>     } else {<br>-        smrl.option( "vcodec", "none" );<br>+        transcode.option( "vcodec", "none" );<br>     }<br> <br>     HASHPICK( "audio", "enable" );<br>@@ -316,27 +315,27 @@ void VLCProfileSelector::updateOptions( int i )<br>         HASHPICK( "audio", "codec" );<br>         if ( !value.isEmpty() )<br>         {<br>-            smrl.option( "acodec", value );<br>+            transcode.option( "acodec", value );<br> <br>             HASHPICK( "acodec", "bitrate" );<br>-            smrl.option( "ab", value.toInt() );<br>+            transcode.option( "ab", value.toInt() );<br> <br>             HASHPICK( "acodec", "channels" );<br>-            smrl.option( "channels", value.toInt() );<br>+            transcode.option( "channels", value.toInt() );<br> <br>             HASHPICK( "acodec", "samplerate" );<br>-            smrl.option( "samplerate", value.toInt() );<br>+            transcode.option( "samplerate", value.toInt() );<br> <br>             HASHPICK( "audio", "filters" );<br>             if ( !value.isEmpty() )<br>             {<br>                 QStringList valuesList = QUrl::fromPercentEncoding( value.toLatin1() ).split( ";" );<br>-                smrl.option( "afilter", valuesList.join( ":" ) );<br>+                transcode.option( "afilter", valuesList.join( ":" ) );<br>             }<br> <br>         }<br>     } else {<br>-        smrl.option( "acodec", "none" );<br>+        transcode.option( "acodec", "none" );<br>     }<br> <br>     HASHPICK( "subtitles", "enable" );<br>@@ -346,20 +345,18 @@ void VLCProfileSelector::updateOptions( int i )<br>         if ( value.isEmpty() )<br>         {<br>             HASHPICK( "subtitles", "codec" );<br>-            smrl.option( "scodec", value );<br>+            transcode.option( "scodec", value );<br>         }<br>         else<br>         {<br>-            smrl.option( "soverlay" );<br>+            transcode.option( "soverlay" );<br>         }<br>     } else {<br>-        smrl.option( "scodec", "none" );<br>+        transcode.option( "scodec", "none" );<br>     }<br>-    smrl.end();<br>+    transcode.end();<br> #undef HASHPICK<br> <br>-    transcode = smrl.getMrl();<br>-<br>     cleanup:<br>     /* Temp hash tables cleanup */<br>     foreach( proptovalueHashType *hash, categtopropHash )<br>@@ -376,52 +373,49 @@ void VLCProfileSelector::updateOptionsOldFormat( int i )<br> <br>     mux = options[0];<br> <br>-    SoutMrl smrl;<br>     if( options[1].toInt() || options[2].toInt() || options[3].toInt() )<br>     {<br>-        smrl.begin( "transcode" );<br>+        transcode.begin( "transcode" );<br> <br>         if( options[1].toInt() )<br>         {<br>-            smrl.option( "vcodec", options[4] );<br>+            transcode.option( "vcodec", options[4] );<br>             if( options[4] != "none" )<br>             {<br>-                smrl.option( "vb", options[5].toInt() );<br>+                transcode.option( "vb", options[5].toInt() );<br>                 if( !options[7].isEmpty() && options[7].toInt() > 0 )<br>-                    smrl.option( "fps", options[7] );<br>+                    transcode.option( "fps", options[7] );<br>                 if( !options[6].isEmpty() )<br>-                    smrl.option( "scale", options[6] );<br>+                    transcode.option( "scale", options[6] );<br>                 if( !options[8].isEmpty() && options[8].toInt() > 0 )<br>-                    smrl.option( "width", options[8].toInt() );<br>+                    transcode.option( "width", options[8].toInt() );<br>                 if( !options[9].isEmpty() && options[9].toInt() > 0 )<br>-                    smrl.option( "height", options[9].toInt() );<br>+                    transcode.option( "height", options[9].toInt() );<br>             }<br>         }<br> <br>         if( options[2].toInt() )<br>         {<br>-            smrl.option( "acodec", options[10] );<br>+            transcode.option( "acodec", options[10] );<br>             if( options[10] != "none" )<br>             {<br>-                smrl.option( "ab", options[11].toInt() );<br>-                smrl.option( "channels", options[12].toInt() );<br>-                smrl.option( "samplerate", options[13].toInt() );<br>+                transcode.option( "ab", options[11].toInt() );<br>+                transcode.option( "channels", options[12].toInt() );<br>+                transcode.option( "samplerate", options[13].toInt() );<br>             }<br>         }<br> <br>         if( options[3].toInt() )<br>         {<br>-            smrl.option( "scodec", options[14] );<br>+            transcode.option( "scodec", options[14] );<br>             if( options[15].toInt() )<br>-                smrl.option( "soverlay" );<br>+                transcode.option( "soverlay" );<br>         }<br> <br>-        smrl.end();<br>-<br>-        transcode = smrl.getMrl();<br>+        transcode.end();<br>     }<br>     else<br>-        transcode = "";<br>+        transcode.clear();<br>     emit optionsChanged();<br> }<br> <br>diff --git a/modules/gui/qt/components/sout/profile_selector.hpp b/modules/gui/qt/components/sout/profile_selector.hpp<br>index 1074c7b220..61d3502eb7 100644<br>--- a/modules/gui/qt/components/sout/profile_selector.hpp<br>+++ b/modules/gui/qt/components/sout/profile_selector.hpp<br>@@ -30,6 +30,7 @@<br> #include <QHash><br> <br> #include "util/qvlcframe.hpp"<br>+#include "util/soutmrl.hpp"<br> #include "ui_profiles.h"<br> <br> class QComboBox;<br>@@ -42,14 +43,14 @@ public:<br>     VLCProfileSelector( QWidget *_parent );<br>     ~VLCProfileSelector();<br>     QString getMux() { return mux; }<br>-    QString getTranscode() { return transcode; }<br>+    SoutMrl getTranscode() { return transcode; }<br> private:<br>     QComboBox *profileBox;<br>     void fillProfilesCombo();<br>     void editProfile( const QString&, const QString& );<br>     void saveProfiles();<br>     QString mux;<br>-    QString transcode;<br>+    SoutMrl transcode;<br> private slots:<br>     void newProfile();<br>     void editProfile();<br>diff --git a/modules/gui/qt/dialogs/convert.cpp b/modules/gui/qt/dialogs/convert.cpp<br>index cf157472b6..332da28c70 100644<br>--- a/modules/gui/qt/dialogs/convert.cpp<br>+++ b/modules/gui/qt/dialogs/convert.cpp<br>@@ -176,21 +176,20 @@ void ConvertDialog::close()<br> <br>     for(int i = 0; i < incomingMRLs->length(); i++)<br>     {<br>-        QString mrl;<br>+        SoutMrl mrl;<br> <br>         if( dumpRadio->isChecked() )<br>         {<br>-            mrl = "demux=dump :demuxdump-file=" + fileLine->text();<br>+            mrl.header("demux=dump :demuxdump-file=" + fileLine->text());<br>         }<br>         else<br>         {<br>-            mrl = "sout=#" + profile->getTranscode();<br>+            mrl = profile->getTranscode();<br>+            mrl.header( "sout=#" + mrl.getHeader() );<br>             if( deinterBox->isChecked() )<br>             {<br>-                mrl.remove( '}' );<br>-                mrl += ",deinterlace}";<br>+                mrl.option("deinterlace");<br>             }<br>-            mrl += ":";<br> <br>             QString newFileName;<br> <br>@@ -229,15 +228,29 @@ void ConvertDialog::close()<br> <br>             newFileName.replace( QChar('\''), "\\\'" );<br> <br>-            QString chain = QString("std{access=file{no-overwrite},mux=%1,dst='%2'}")<br>-                                    .arg( profile->getMux() ).arg( newFileName );<br>+<br>+            mrl.end();<br>+            MrlModule dstModule("std");<br>+            MrlModule file("file");<br>+            file.option("no-overwrite");<br>+            dstModule.option("access", file);<br>+            dstModule.option("mux", profile->getMux());<br>+            dstModule.option("dst", "'" + newFileName + "'");<br>+<br>             if( displayBox->isChecked() )<br>-                mrl += QString( "duplicate{dst=display,dst=%1}" ).arg( chain );<br>+            {<br>+                MrlModule duplicate("duplicate");<br>+                duplicate.option("dst", "display");<br>+                duplicate.option("dst", dstModule);<br>+                mrl.module(duplicate);<br>+            }<br>             else<br>-                mrl += chain;<br>+            {<br>+                mrl.module(dstModule);<br>+            }<br>         }<br>-        msg_Dbg( p_intf, "Transcode MRL: %s", qtu( mrl ) );<br>-        mrls.append(mrl);<br>+        msg_Dbg( p_intf, "Transcode MRL: %s", qtu( mrl.getMrl() ) );<br>+        mrls.append(mrl.getMrl());<br>     }<br>     accept();<br> }<br>diff --git a/modules/gui/qt/dialogs/sout.cpp b/modules/gui/qt/dialogs/sout.cpp<br>index bbcc308d63..3dd764e418 100644<br>--- a/modules/gui/qt/dialogs/sout.cpp<br>+++ b/modules/gui/qt/dialogs/sout.cpp<br>@@ -179,9 +179,9 @@ void SoutDialog::updateMRL()<br>     QString qs_mux = ui.profileSelect->getMux();<br> <br>     SoutMrl smrl( ":sout=#" );<br>-    if( !ui.profileSelect->getTranscode().isEmpty() && ui.transcodeBox->isChecked() )<br>+    if( !ui.profileSelect->getTranscode().getMrl().isEmpty() && ui.transcodeBox->isChecked() )<br>     {<br>-        smrl.begin( ui.profileSelect->getTranscode() );<br>+        smrl.begin( ui.profileSelect->getTranscode().getMrl() );<br>         smrl.end();<br>     }<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:46, Jérôme Froissart <<a href="mailto:software@froissart.eu" target="_blank">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"><br>---<br> modules/gui/qt/util/soutmrl.cpp |  5 +++++<br> modules/gui/qt/util/soutmrl.hpp | 17 +++++++++++++++++<br> 2 files changed, 22 insertions(+)<br><br>diff --git a/modules/gui/qt/util/soutmrl.cpp b/modules/gui/qt/util/soutmrl.cpp<br>index 92aa10ae90..90615c6c86 100644<br>--- a/modules/gui/qt/util/soutmrl.cpp<br>+++ b/modules/gui/qt/util/soutmrl.cpp<br>@@ -38,6 +38,11 @@ void MrlModule::option( const QString& option )<br>     options.append( OptionPairType( option, "" ) );<br> }<br><br>+QString SoutMrl::getHeader() const<br>+{<br>+    return hdr;<br>+}<br>+<br> QString SoutMrl::getMrl() const<br> {<br>     QString mrl = hdr;<br>diff --git a/modules/gui/qt/util/soutmrl.hpp b/modules/gui/qt/util/soutmrl.hpp<br>index f584e1162b..69ba4458bb 100644<br>--- a/modules/gui/qt/util/soutmrl.hpp<br>+++ b/modules/gui/qt/util/soutmrl.hpp<br>@@ -99,6 +99,17 @@ public:<br>     {<br>     }<br><br>+    void clear()<br>+    {<br>+        hdr = "";<br>+        modules.clear();<br>+    }<br>+<br>+    void header( const QString& newHeader )<br>+    {<br>+        hdr = newHeader;<br>+    }<br>+<br>     MrlModule& begin( const QString& module )<br>     {<br>         modules.append( MrlModule( module ) );<br>@@ -110,6 +121,11 @@ public:<br>     {<br>     }<br><br>+    void module( const MrlModule& module )<br>+    {<br>+        modules.append( module );<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>@@ -117,6 +133,7 @@ public:<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 getHeader() const;<br>     QString getMrl() const;<br><br> private:<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:46, Jérôme Froissart <<a href="mailto:software@froissart.eu" target="_blank">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"><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" target="_blank">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" target="_blank">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>
</blockquote></div>
</blockquote></div></div>
</blockquote></div>