aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--visualization/renderer/main.cpp15
-rw-r--r--visualization/renderer/qmpvisrendercore.cpp159
-rw-r--r--visualization/renderer/qmpvisrendercore.hpp10
3 files changed, 142 insertions, 42 deletions
diff --git a/visualization/renderer/main.cpp b/visualization/renderer/main.cpp
index c15d846..82e8983 100644
--- a/visualization/renderer/main.cpp
+++ b/visualization/renderer/main.cpp
@@ -14,9 +14,18 @@ int main(int argc,char **argv)
clp.addHelpOption();
clp.addVersionOption();
clp.addOption({{"f","output-file"},"File name of the output file.","filename","output.mp4"});
- clp.addOption({"ffmpeg-args","Custom output option arguments for ffmpeg.","args","-vf vflip -pix_fmt yuv420p -c:v libx264 -preset slow -crf 22"});
- clp.addOption({"ffmpeg-pre-args","Custom arguments passed to ffmpeg before input arguments.","args",""});
- clp.addOption({"ffmpeg-exec","Specify the path to the ffmpeg executable.","exec","ffmpeg"});
+ clp.addOption({
+ "receiver",
+ "Specify a program and its arguments to process the rendered frames. Supports parameter substitution. See documentation for details.",
+ "command",
+ "ffmpeg -f rawvideo -pixel_format rgba -video_size %wx%h -framerate %r -i pipe: -vf vflip -pix_fmt yuv420p -c:v libx264 -preset slow -crf 22 %o"
+ });
+ clp.addOption({
+ {"e","receiver-execution"},
+ "Execution mode of the receiver command. Valid options are 'one-shot' and 'per-frame'",
+ "mode",
+ "one-shot"
+ });
clp.addOption({{"s","show-window"},"Do not hide the visualization window."});
clp.addOption({{"c","config"},"Load options from the configuration file.","qmprc file"});
clp.addOption({{"o","option"},"Set option for the visualization module.","key-value pair"});
diff --git a/visualization/renderer/qmpvisrendercore.cpp b/visualization/renderer/qmpvisrendercore.cpp
index d4acea9..304d061 100644
--- a/visualization/renderer/qmpvisrendercore.cpp
+++ b/visualization/renderer/qmpvisrendercore.cpp
@@ -4,6 +4,7 @@
#include "qmpmidiplay.hpp"
#include "qmpcorepublic.hpp"
+#include <algorithm>
#include <cassert>
#include <dlfcn.h>
@@ -19,6 +20,7 @@ qmpVisRenderCore::qmpVisRenderCore(QCommandLineParser *_clp):QObject(nullptr),cl
player=new CMidiPlayer();
api=new qmpPluginAPIStub(this);
msettings=new qmpSettingsRO();
+ frameno=0;
msettings->registerOptionEnumInt("MIDI","Text encoding","Midi/TextEncoding",{"Unicode","Big5","Big5-HKSCS","CP949","EUC-JP","EUC-KR","GB18030","KOI8-R","KOI8-U","Macintosh","Shift-JIS"},0);
}
@@ -72,62 +74,120 @@ void qmpVisRenderCore::setMIDIFile(const char *url)
void qmpVisRenderCore::startRender()
{
assert(vf);
- ffmpegproc=new QProcess();
- ffmpegproc->setProgram("ffmpeg");
- QStringList arguments;
- arguments.append(split_arguments(clp->value("ffmpeg-pre-args")));
- arguments
- <<"-f"<<"rawvideo"
- <<"-pixel_format"<<"rgba"
- <<"-video_size"<<QString("%1x%2").arg(msettings->getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight"))
- <<"-framerate"<<QString::number(msettings->getOptionInt("Visualization/tfps"))
- <<"-i"<<"pipe:";
- arguments.append(split_arguments(clp->value("ffmpeg-args")));
- arguments<<clp->value("output-file");
- ffmpegproc->setArguments(arguments);
- QMetaObject::Connection frameconn=connect(this,&qmpVisRenderCore::frameRendered,this,
- [this,&frameconn](void* px,size_t sz,uint32_t c,uint32_t t)
+ subst={
+ {'w',QString::number(msettings->getOptionInt("Visualization/wwidth"))},
+ {'h',QString::number(msettings->getOptionInt("Visualization/wheight"))},
+ {'r',QString::number(msettings->getOptionInt("Visualization/tfps"))},
+ {'i',
+ QStringList()
+ <<"-f"<<"rawvideo"
+ <<"-pixel_format"<<"rgba"
+ <<"-video_size"<<QString("%1x%2").arg(msettings->getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight"))
+ <<"-framerate"<<QString::number(msettings->getOptionInt("Visualization/tfps"))
+ <<"-i"<<"pipe:"
+ },
+ {'o',clp->value("output-file")}
+ };
+ if(clp->value("receiver-execution")=="per-frame")
+ {
+ subst['o']=clp->value("output-file").replace("%f",QString("%1").arg(frameno,6,10,QChar('0')));
+ oneshot=false;
+ }
+ else
+ {
+ oneshot=true;
+ if(clp->value("receiver-execution")!="one-shot")
+ qWarning("Invalid value set for --receiver-execution. Using default value.");
+ }
+ rxproc=new QProcess();
+ QStringList arguments=process_arguments(clp->value("receiver"),subst);
+ assert(arguments.length()>0);
+ rxproc->setProgram(arguments.front());
+ arguments.pop_front();
+ rxproc->setArguments(arguments);
+ frameconn=connect(this,&qmpVisRenderCore::frameRendered,this,
+ [this](void* px,size_t sz,uint32_t c,uint32_t t)
{
if(sz)
{
- if(!ffmpegproc->isOpen())return;
- ffmpegproc->write(static_cast<const char*>(px),static_cast<qint64>(sz));
- while(ffmpegproc->bytesToWrite()>1<<26)
- ffmpegproc->waitForBytesWritten();
+ if(!oneshot)
+ {
+ subst['f']=QString("%1").arg(frameno,6,10,QChar('0'));
+ subst['o']=clp->value("output-file").replace("%f",QString("%1").arg(frameno,6,10,QChar('0')));
+ frameno++;
+ QStringList arguments=process_arguments(clp->value("receiver"),subst);
+ arguments.pop_front();
+ rxproc->setArguments(arguments);
+ rxproc->start();
+ rxproc->waitForStarted();
+ }
+ if(!rxproc->isOpen())return;
+ rxproc->write(static_cast<const char*>(px),static_cast<qint64>(sz));
+ while(rxproc->bytesToWrite()>(oneshot?(1<<26):0))
+ rxproc->waitForBytesWritten();
+ if(!oneshot)
+ {
+ rxproc->closeWriteChannel();
+ rxproc->waitForFinished(-1);
+ }
}
- fprintf(stderr,"Rendered tick %u of %u, %.2f%% done.\r",c,t,100.*c/t);
+ fprintf(stderr,"Rendered tick %u of %u, %.2f%% done.\r",c,t,std::min(100.,100.*c/t));
if(c>t)
{
- this->ffmpegproc->closeWriteChannel();
+ this->rxproc->closeWriteChannel();
disconnect(frameconn);
qApp->exit(0);
}
},Qt::ConnectionType::BlockingQueuedConnection);
- connect(ffmpegproc,QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
- [this,&frameconn](int x,QProcess::ExitStatus st){
- qDebug("%s",this->ffmpegproc->readAllStandardError().data());
- disconnect(frameconn);
- if(x||st==QProcess::ExitStatus::CrashExit)
- qApp->exit(1);
- else
- qApp->exit(0);
+ connect(rxproc,QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
+ [this](int x,QProcess::ExitStatus st){
+ qDebug("%s",this->rxproc->readAllStandardError().data());
+ if(oneshot)
+ {
+ disconnect(frameconn);
+ if(x||st==QProcess::ExitStatus::CrashExit)
+ qApp->exit(1);
+ else
+ qApp->exit(0);
+ }
});
QMetaObject::invokeMethod(this,[this](){
- ffmpegproc->start();
+ if(oneshot)
+ rxproc->start();
vf->show();
startcb(nullptr,nullptr);
},Qt::ConnectionType::QueuedConnection);
}
-QStringList qmpVisRenderCore::split_arguments(QString a)
+QStringList qmpVisRenderCore::process_arguments(QString a,QMap<QChar,QVariant> subst)
{
QStringList ret;
QString buf;
bool escaped=false;
+ bool substi=false;
for(int i=0;i<a.length();++i)
{
- if(a[i]=='\\')
+ if(a[i]=='%')
+ {
+ if(escaped)
+ {
+ buf+='%';
+ escaped=false;
+ }
+ else if(substi)
+ {
+ buf+='%';
+ substi=false;
+ }
+ else substi=true;
+ }
+ else if(a[i]=='\\')
{
+ if(substi)
+ {
+ buf+='%';
+ substi=false;
+ }
if(escaped)
{
buf+='\\';
@@ -137,22 +197,47 @@ QStringList qmpVisRenderCore::split_arguments(QString a)
}
else if(a[i]==' ')
{
+ if(substi)
+ {
+ buf+='%';
+ substi=false;
+ }
if(escaped)buf+=' ';
else
{
- ret.append(buf);
+ if(buf.length())
+ ret.append(buf);
buf.clear();
}
escaped=false;
}
else
{
- if(escaped)
+ if(substi&&subst.contains(a[i]))
{
- buf+='\\';
- escaped=false;
+ if(subst[a[i]].canConvert(QMetaType::QString))
+ buf+=subst[a[i]].toString();
+ else
+ {
+ if(buf.length())
+ {
+ ret.append(buf);
+ buf.clear();
+ }
+ for(auto &it:subst[a[i]].toStringList())
+ ret.append(it);
+ }
+ substi=false;
+ }
+ else
+ {
+ if(escaped)
+ {
+ buf+='\\';
+ escaped=false;
+ }
+ buf+=a[i];
}
- buf+=a[i];
}
}
if(buf.length())
diff --git a/visualization/renderer/qmpvisrendercore.hpp b/visualization/renderer/qmpvisrendercore.hpp
index d32e4d7..71eeaed 100644
--- a/visualization/renderer/qmpvisrendercore.hpp
+++ b/visualization/renderer/qmpvisrendercore.hpp
@@ -4,6 +4,8 @@
#include <cstddef>
#include <QObject>
+#include <QVariant>
+#include <QMap>
#include "qmpcorepublic.hpp"
class qmpPluginAPIStub;
@@ -38,9 +40,13 @@ private:
qmpPluginAPIStub *api;
CMidiPlayer *player;
qmpSettingsRO *msettings;
- QProcess *ffmpegproc;
+ QProcess *rxproc;
+ QMap<QChar,QVariant> subst;
QCommandLineParser *clp;
- QStringList split_arguments(QString a);
+ QStringList process_arguments(QString a, QMap<QChar,QVariant> subst);
+ int frameno;
+ bool oneshot;
+ QMetaObject::Connection frameconn;
typedef qmpPluginIntf*(*GetInterface_func)(qmpPluginAPI*);
typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool hidewindow);