From f4f80b19a27118275d5f925558f3cfb46bb5d14b Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sat, 2 May 2020 00:57:43 +0800 Subject: Reworked parameter passing for the frame processor program. --- visualization/renderer/main.cpp | 15 ++- visualization/renderer/qmpvisrendercore.cpp | 159 +++++++++++++++++++++------- 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 #include #include @@ -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"<getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight")) - <<"-framerate"<getOptionInt("Visualization/tfps")) - <<"-i"<<"pipe:"; - arguments.append(split_arguments(clp->value("ffmpeg-args"))); - arguments<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"<getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight")) + <<"-framerate"<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(px),static_cast(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(px),static_cast(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::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::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 subst) { QStringList ret; QString buf; bool escaped=false; + bool substi=false; for(int i=0;i #include +#include +#include #include "qmpcorepublic.hpp" class qmpPluginAPIStub; @@ -38,9 +40,13 @@ private: qmpPluginAPIStub *api; CMidiPlayer *player; qmpSettingsRO *msettings; - QProcess *ffmpegproc; + QProcess *rxproc; + QMap subst; QCommandLineParser *clp; - QStringList split_arguments(QString a); + QStringList process_arguments(QString a, QMap 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); -- cgit v1.2.3