<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">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">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>