aboutsummaryrefslogtreecommitdiff
path: root/visualization/renderer
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2020-05-01 01:31:45 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2020-05-01 01:31:45 +0800
commit893bec26cd1bc83fd85bb7dfdfd6225d9f6ee5b5 (patch)
tree3b7e7a4f772c8f8de93d61b549599b22902e83fc /visualization/renderer
parentbd165c0254b9095bb9e5ea54def56b6404033ebe (diff)
downloadQMidiPlayer-893bec26cd1bc83fd85bb7dfdfd6225d9f6ee5b5.tar.xz
Visualization renderer now understands command line arguments.
Fixed wrong tick calculation in render mode. Fixed most of the hangs and crashes associated with the renderer. Do not write pixel data in the visualization thread.
Diffstat (limited to 'visualization/renderer')
-rw-r--r--visualization/renderer/main.cpp21
-rw-r--r--visualization/renderer/qmpsettingsro.cpp59
-rw-r--r--visualization/renderer/qmpsettingsro.hpp1
-rw-r--r--visualization/renderer/qmpvisrendercore.cpp126
-rw-r--r--visualization/renderer/qmpvisrendercore.hpp15
5 files changed, 189 insertions, 33 deletions
diff --git a/visualization/renderer/main.cpp b/visualization/renderer/main.cpp
index ec4dd1d..cca8a12 100644
--- a/visualization/renderer/main.cpp
+++ b/visualization/renderer/main.cpp
@@ -2,17 +2,32 @@
#include <QCommandLineParser>
#include "qmpvisrendercore.hpp"
+#include "qmpsettingsro.hpp"
int main(int argc,char **argv)
{
QCoreApplication::setApplicationName("qmpvisrender");
+ QCoreApplication::setApplicationVersion("0.0.0");
QCoreApplication a(argc,argv);
QCommandLineParser clp;
clp.setApplicationDescription("Renderer a visualization of a midi file.");
clp.addHelpOption();
- clp.parse(a.arguments());
- qmpVisRenderCore core;
- core.loadVisualizationLibrary();
+ 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({{"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"});
+ clp.addPositionalArgument("file","MIDI file to render");
+ clp.process(a.arguments());
+ qmpVisRenderCore core(&clp);
+ if(clp.positionalArguments().empty())
+ clp.showHelp(1);
+ if(!core.loadVisualizationLibrary())
+ return 1;
+ core.loadSettings();
if(clp.positionalArguments().size())
core.setMIDIFile(clp.positionalArguments().front().toStdString().c_str());
core.startRender();
diff --git a/visualization/renderer/qmpsettingsro.cpp b/visualization/renderer/qmpsettingsro.cpp
index cc6e0bf..a66943b 100644
--- a/visualization/renderer/qmpsettingsro.cpp
+++ b/visualization/renderer/qmpsettingsro.cpp
@@ -165,3 +165,62 @@ void qmpSettingsRO::load(const char *path)
settings.insert(k,qsettings->value(k));
}
}
+
+void qmpSettingsRO::setopt(std::string key, std::string val)
+{
+ if(options.find(key)==options.end())
+ {
+ std::string nkey="Visualization/"+key;
+ if(options.find(nkey)==options.end())
+ {
+ qDebug("invalid option key %s",key.c_str());
+ return;
+ }
+ else key=nkey;
+ }
+ char *rptr;
+ switch(options[key].type)
+ {
+ case qmpOptionR::ParameterType::parameter_int:
+ {
+ long long v=strtoll(val.c_str(),&rptr,10);
+ if(rptr==val.c_str()||v>INT_MAX||v<INT_MIN)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionInt(key,static_cast<int>(v));
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_uint:
+ {
+ long long v=strtoll(val.c_str(),&rptr,10);
+ if(rptr==val.c_str()||v>UINT32_MAX||v<0)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionUint(key,static_cast<uint32_t>(v));
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_double:
+ {
+ errno=0;
+ double v=strtod(val.c_str(),&rptr);
+ if(rptr==val.c_str()||errno)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionDouble(key,v);
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_bool:
+ {
+ if(val!="true"&&val!="false")
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionBool(key,val=="true");
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_str:
+ case qmpOptionR::ParameterType::parameter_url:
+ setOptionString(key,val);
+ break;
+ case qmpOptionR::ParameterType::parameter_enum:
+ setOptionEnumIntOptName(key,val);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/visualization/renderer/qmpsettingsro.hpp b/visualization/renderer/qmpsettingsro.hpp
index 30ab9b6..5c5361d 100644
--- a/visualization/renderer/qmpsettingsro.hpp
+++ b/visualization/renderer/qmpsettingsro.hpp
@@ -64,6 +64,7 @@ public:
void setOptionEnumIntOptName(std::string key,std::string valname);
void load(const char* path);
+ void setopt(std::string key,std::string val);
private:
std::map<std::string,qmpOptionR> options;
std::vector<std::string> optionlist;
diff --git a/visualization/renderer/qmpvisrendercore.cpp b/visualization/renderer/qmpvisrendercore.cpp
index fb1a7ef..a2733a7 100644
--- a/visualization/renderer/qmpvisrendercore.cpp
+++ b/visualization/renderer/qmpvisrendercore.cpp
@@ -8,11 +8,12 @@
#include <dlfcn.h>
#include <QProcess>
+#include <QCommandLineParser>
#include <QDebug>
#include <QThread>
qmpVisRenderCore *qmpVisRenderCore::inst=nullptr;
-qmpVisRenderCore::qmpVisRenderCore():QObject(nullptr)
+qmpVisRenderCore::qmpVisRenderCore(QCommandLineParser *_clp):QObject(nullptr),clp(_clp)
{
inst=this;
player=new CMidiPlayer();
@@ -21,17 +22,21 @@ qmpVisRenderCore::qmpVisRenderCore():QObject(nullptr)
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);
}
-void qmpVisRenderCore::loadVisualizationLibrary()
+bool qmpVisRenderCore::loadVisualizationLibrary()
{
mp=dlopen("libvisualization.so",RTLD_LAZY);
- if(!mp)fprintf(stderr,"failed to load visualization module!\n");
+ if(!mp)
+ {
+ fprintf(stderr,"failed to load the visualization module!\n");
+ return false;
+ }
GetInterface_func getintf=reinterpret_cast<GetInterface_func>(dlsym(mp,"qmpPluginGetInterface"));
SwitchMode_func switchmode=reinterpret_cast<SwitchMode_func>(dlsym(mp,"switchToRenderMode"));
vf=nullptr;
vp=getintf(api);
- switchmode(&qmpVisRenderCore::framefunc,false);
+ switchmode(&qmpVisRenderCore::framefunc,!clp->isSet("show-window"));
vp->init();
- msettings->load("/home/chrisoft/.config/qmprc");
+ return true;
}
void qmpVisRenderCore::unloadVisualizationLibrary()
@@ -40,6 +45,21 @@ void qmpVisRenderCore::unloadVisualizationLibrary()
dlclose(mp);
}
+void qmpVisRenderCore::loadSettings()
+{
+ if(clp->isSet("config"))
+ msettings->load(clp->value("config").toStdString().c_str());
+ for(auto &o:clp->values("option"))
+ {
+ int sp=o.indexOf('=');
+ if(!~sp)
+ qDebug("invalid option pair: %s",o.toStdString().c_str());
+ QString key=o.left(sp);
+ QString value=o.mid(sp+1);
+ msettings->setopt(key.toStdString(),value.toStdString());
+ }
+}
+
void qmpVisRenderCore::setMIDIFile(const char *url)
{
player->playerLoadFile(url);
@@ -51,38 +71,92 @@ void qmpVisRenderCore::startRender()
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"<<"1600x900"
- <<"-framerate"<<"60"
+ <<"-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
- <<"-vf"<<"vflip"
- <<"-pix_fmt"<<"yuv420p"
- <<"-c:v"<<"libx264"
- <<"-preset"<<"fast"
- <<"-crf"<<"22";
- arguments<<"output.mp4";
+ arguments.append(split_arguments(clp->value("ffmpeg-args")));
+ arguments<<clp->value("output-file");
ffmpegproc->setArguments(arguments);
- ffmpegproc->start();
+ QMetaObject::Connection frameconn=connect(this,&qmpVisRenderCore::frameRendered,this,
+ [this,&frameconn](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();
+ }
+ fprintf(stderr,"Rendered tick %u of %u, %.2f%% done.\r",c,t,100.*c/t);
+ if(c>t)
+ {
+ this->ffmpegproc->closeWriteChannel();
+ disconnect(frameconn);
+ qApp->exit(0);
+ }
+ },Qt::ConnectionType::BlockingQueuedConnection);
connect(ffmpegproc,QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
- [this](int x,QProcess::ExitStatus){qDebug("%d",x);qDebug()<<this->ffmpegproc->readAllStandardError();});
- vf->show();
- startcb(nullptr,nullptr);
+ [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);
+ });
+ QMetaObject::invokeMethod(this,[this](){
+ ffmpegproc->start();
+ vf->show();
+ startcb(nullptr,nullptr);
+ },Qt::ConnectionType::QueuedConnection);
}
-void qmpVisRenderCore::framefunc(void *px, size_t sz)
+QStringList qmpVisRenderCore::split_arguments(QString a)
{
- if(sz)
+ QStringList ret;
+ QString buf;
+ bool escaped=false;
+ for(int i=0;i<a.length();++i)
{
- inst->ffmpegproc->write((const char*)px,sz);
- while(inst->ffmpegproc->bytesToWrite()>1<<26)
+ if(a[i]=='\\')
+ {
+ if(escaped)
+ {
+ buf+='\\';
+ escaped=false;
+ }
+ else escaped=true;
+ }
+ else if(a[i]==' ')
{
- inst->ffmpegproc->waitForBytesWritten();
- QThread::yieldCurrentThread();
+ if(escaped)buf+=' ';
+ else
+ {
+ ret.append(buf);
+ buf.clear();
+ }
+ escaped=false;
+ }
+ else
+ {
+ if(escaped)
+ {
+ buf+='\\';
+ escaped=false;
+ }
+ buf+=a[i];
}
}
- else
- inst->ffmpegproc->closeWriteChannel();
+ if(buf.length())
+ ret.append(buf);
+ return ret;
+}
+
+void qmpVisRenderCore::framefunc(void *px, size_t sz,uint32_t curf,uint32_t totf)
+{
+ emit inst->frameRendered(px,sz,curf,totf);
}
diff --git a/visualization/renderer/qmpvisrendercore.hpp b/visualization/renderer/qmpvisrendercore.hpp
index 0c024f1..27337ba 100644
--- a/visualization/renderer/qmpvisrendercore.hpp
+++ b/visualization/renderer/qmpvisrendercore.hpp
@@ -9,6 +9,7 @@
class qmpPluginAPIStub;
class CMidiPlayer;
class qmpSettingsRO;
+class QCommandLineParser;
class QProcess;
@@ -16,14 +17,18 @@ class qmpVisRenderCore:public QObject
{
Q_OBJECT
public:
- qmpVisRenderCore();
- void loadVisualizationLibrary();
+ qmpVisRenderCore(QCommandLineParser *_clp);
+ bool loadVisualizationLibrary();
void unloadVisualizationLibrary();
+ void loadSettings();
void setMIDIFile(const char* url);
void startRender();
qmpSettingsRO* settings(){return msettings;}
+signals:
+ void frameRendered(void* px,size_t sz,uint32_t current_tick,uint32_t total_ticks);
+
private:
qmpPluginIntf *vp;
qmpFuncBaseIntf *vf;
@@ -33,11 +38,13 @@ private:
CMidiPlayer *player;
qmpSettingsRO *msettings;
QProcess *ffmpegproc;
+ QCommandLineParser *clp;
+ QStringList split_arguments(QString a);
typedef qmpPluginIntf*(*GetInterface_func)(qmpPluginAPI*);
- typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t),bool hidewindow);
+ typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool hidewindow);
friend class qmpPluginAPIStub;
- static void framefunc(void* px,size_t sz);
+ static void framefunc(void* px, size_t sz, uint32_t curf, uint32_t totf);
static qmpVisRenderCore *inst;
};