diff options
Diffstat (limited to 'visualization/renderer')
-rw-r--r-- | visualization/renderer/main.cpp | 15 | ||||
-rw-r--r-- | visualization/renderer/qmpvisrendercore.cpp | 159 | ||||
-rw-r--r-- | visualization/renderer/qmpvisrendercore.hpp | 10 |
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); |