diff options
| -rw-r--r-- | NT/src/nodes_audio_analysis.h | 9 | ||||
| -rw-r--r-- | NT/src/rotor.h | 25 | ||||
| -rw-r--r-- | rotord/src/graph.cpp | 169 | ||||
| -rw-r--r-- | rotord/src/graph.h | 17 | ||||
| -rw-r--r-- | rotord/src/rendercontext.cpp | 2 |
5 files changed, 177 insertions, 45 deletions
diff --git a/NT/src/nodes_audio_analysis.h b/NT/src/nodes_audio_analysis.h index 4542e19..aa4abeb 100644 --- a/NT/src/nodes_audio_analysis.h +++ b/NT/src/nodes_audio_analysis.h @@ -39,7 +39,6 @@ namespace Rotor { void cleanup(){}; int process_frame(uint8_t *data,int samples_in_frame); void print_vector(xmlIO XML); - const double get_output(const Frame_parameters &frame){return 0.0;}; vector<double> audiodata; int height,width,samples_per_column; int out_sample,sample,samples; @@ -55,9 +54,9 @@ namespace Rotor { bool init(int _channels,int _bits,int _samples,int _rate); void cleanup(); int process_frame(uint8_t *data,int samples_in_frame); - const double output(const Time_spec &time) { + const double& output(const Frame_parameters &frame) { if (features.size()) { - auto i=features.upper_bound(time.time); //the first element in the container whose key is considered to go after k + auto i=features.upper_bound(frame.time); //the first element in the container whose key is considered to go after k double uk; double v1,v2; v1=v2=0.0; @@ -74,11 +73,11 @@ namespace Rotor { if (i->second.values.size()) v1=i->second.values[0]; switch (attributes["mode"]->intVal){ case VAMPHOST_Timeline: - return (((time.time-lk)/(uk-lk))+ln); + return (((frame.time-lk)/(uk-lk))+ln); case VAMPHOST_Timesteps: return (double)ln; case VAMPHOST_Valueline: - return ((time.time-lk)/(uk-lk))+v1; //((((time.time-lk)/(uk-lk))*(v2-v1))+v1); + return ((frame.time-lk)/(uk-lk))+v1; //((((time.time-lk)/(uk-lk))*(v2-v1))+v1); case VAMPHOST_Values: return v1; } diff --git a/NT/src/rotor.h b/NT/src/rotor.h index 1ed02dc..d2d7d00 100644 --- a/NT/src/rotor.h +++ b/NT/src/rotor.h @@ -78,11 +78,22 @@ struct TypeName<std::string> { + + namespace Rotor { class Node; template <class NT> class Node_type; + class Enum{ + //enumerated string type for menus + public: + Enum(std::initializer_list<std::string> init) : labels(init){}; + private: + std::vector<std::string> labels; + int value; + }; + class Audio_frame{ public: Audio_frame(uint16_t *_samples,int _channels,int _numsamples){ @@ -117,9 +128,7 @@ namespace Rotor { }; class Variable { //pure virtual base type for variable pointers public: - Variable(){ - connection=nullptr; - }; + Variable():name(""),description(""),title(""),connection(nullptr){}; virtual ~Variable(){}; virtual Json::Value to_json()=0; virtual void init(Json::Value s)=0; @@ -133,7 +142,7 @@ namespace Rotor { std::string get_connection_id(); std::string get_name(); protected: - std::string name; + std::string name,description,title; Node* connection; bool connectable; std::string input; @@ -300,7 +309,7 @@ namespace Rotor { }; template <class NT> class Node_type : public Node { public: - virtual const NT& get_output(const Frame_parameters &frame)=0; + virtual const NT& get_output(const Frame_parameters &frame){return value;}; void init(Json::Value settings){ if (!settings["vars"].empty()){ for ( uint32_t i = 0; i < settings["vars"].size(); ++i ) { @@ -316,7 +325,7 @@ namespace Rotor { vars[name]=new Variable_type<IT>(name,"",true); return (dynamic_cast<Variable_type<IT>*>(vars[name])); } - template <class IT> Variable_type<IT>* create_attribute(std::string name){ + template <class IT> Variable_type<IT>* create_attribute(std::string name="",std::string description="",std::string title="",IT _default=IT()){ vars[name]=new Variable_type<IT>(name,"",false); return (dynamic_cast<Variable_type<IT>*>(vars[name])); } @@ -324,6 +333,10 @@ namespace Rotor { vars[name]=new Variable_array_type<IT>(name); return (dynamic_cast<Variable_array_type<IT>*>(vars[name])); } + //enum will require specialisation + //1st, define the enum data type + protected: + NT value; //every node has a value so it can return a reference }; } diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp index 7c61c3e..5dd6e0b 100644 --- a/rotord/src/graph.cpp +++ b/rotord/src/graph.cpp @@ -124,52 +124,31 @@ bool Graph::video_render(const string &output_filename,const double framerate,in // //setup defaults - std::string container; - std::string filestub; + std::string suffix; + std::string namestub; Poco::StringTokenizer t(output_filename,"."); if (t.count()>1){ - filestub=t[t.count()-2]; - container="."+t[t.count()-1]; + namestub=t[t.count()-2]; + suffix="."+t[t.count()-1]; } else { - filestub=output_filename; - container=".mp4"; - } - - if (container==".mpd") { - use_dash=true; - container=".mp4"; - cerr<<"found mpd, writing "<<filestub<<container<<endl; + namestub=output_filename; + suffix=".mp4"; } libav::exporter exporter; - Image* i; - - - //dash implementation - write into a subfolder - - if (use_dash){ - struct stat st = {0}; - if (stat(filestub.c_str(), &st) == -1) { - if (mkdir(filestub.c_str(), 0700)!=0){ - logger.error("MPEG-DASH output: error creating directory "+filestub); - return false; - } - } - //directory exists - //now write the mpd file (for the basic static version) - } + Image *i; - if (exporter.setup(outW,outH,bitRate,framerate,container,use_fragmentation)) { //codecId, - if (exporter.record(filestub+container)) { + if (exporter.setup(outW,outH,bitRate,framerate,suffix,use_fragmentation)) { //codecId, + if (exporter.record(namestub+suffix)) { libav::audio_decoder audioloader; bool usingaudio=audioloader.open(audio_filename); - logger.information("Video_output rendering "+filestub+container+": "+toString(duration)+" seconds at "+toString(framerate)+" fps, audio frame size: "+toString(exporter.get_audio_framesize())); + logger.information("Video_output rendering "+namestub+suffix+": "+toString(duration)+" seconds at "+toString(framerate)+" fps, audio frame size: "+toString(exporter.get_audio_framesize())); //25fps video and 43.06640625fps audio? hmm //how to get the timecodes correct for the interleaved files @@ -270,7 +249,7 @@ bool Graph::video_render(const string &output_filename,const double framerate,in double mtime = ((_end.tv_sec-_start.tv_sec) + (_end.tv_usec-_start.tv_usec)/1000000.0); - logger.information("Video_output: rendered "+filestub+container+": in "+toString(mtime)+" seconds"); + logger.information("Video_output: rendered "+namestub+suffix+": in "+toString(mtime)+" seconds"); logger.information("compression codec took "+toString(mtime-video_output->time_taken)+" seconds"); for (auto n:nodes) { @@ -296,6 +275,132 @@ bool Graph::video_render(const string &output_filename,const double framerate,in return false; } +bool Graph::video_render2(const string &output_filename,const double framerate,int start, int stop) { + + // + //rewrite the video render process to: + //include the possibility of writing fragmented files into a subdirectory + // + + Logger& logger = Logger::get(Log_name); + + if (output_filename.size()==0) { + logger.error("ERROR: video render passed empty filename"); + return false; + } + + if (!find_node("video_output")) { + logger.error("ERROR: video output node not found"); + return false; + } + + Video_output *video_output=dynamic_cast<Video_output*>(find_node("video_output")); + + // + //should text handling and verification happen outside of here? + // + + //first seperate path, name and suffix + std::string suffix=""; + std::string path=""; + std::string namestub=""; + + int pathsep=output_filename.find_last_of("/"); + if (pathsep<output_filename.size()){ // "/" found + path=output_filename.substr(0,pathsep+1); + namestub=output_filename.substr(pathsep+1,string::npos); + } + else namestub=output_filename; + + int dotsep=namestub.find_last_of("."); + if (dotsep<namestub.size()){ // "/" found + suffix=namestub.substr(dotsep,string::npos); + namestub=namestub.substr(0,dotsep); + } + else { + suffix=".mp4"; + } + + + bool use_dash=false; + if (suffix==".mpd") { + use_dash=true; + suffix=".mp4"; + } + + //dash implementation - write into a subfolder + if (use_dash){ + struct stat st = {0}; + if (stat(namestub.c_str(), &st) == -1) { + if (mkdir(namestub.c_str(), 0700)!=0){ + logger.error("MPEG-DASH output: error creating directory "+namestub); + return false; + } + } + //directory exists + logger.information("beginning mpd, chunk length "+toString(mpd_chunk_length)+" seconds"); + path+=namestub; + //now write the mpd file (for the basic static version) + } + + libav::exporter exporter; + + if (!exporter.setup(outW,outH,bitRate,framerate,suffix,use_fragmentation)){ + logger.error("ERROR: could not open codec for "+suffix); + return false; + } + + //cerr<<"path= "<<path<<" ,stub= "<<namestub<<" ,suffix= "<<suffix<<endl; + + logger.information("Video_output rendering "+namestub+suffix+": "+toString(duration)+" seconds at "+toString(framerate)+" fps, audio frame size: "+toString(exporter.get_audio_framesize())); + + + Image *i; + libav::audio_decoder audioloader; + + bool usingaudio=false; + + if (audio_filename!=""&&audioloader.open(audio_filename)){ + video_output->create_envelope(audio_thumb->audiodata); + } + + //time render + struct timeval _start, _end; + gettimeofday(&_start, NULL); + uint16_t *audioframe=nullptr; + uint16_t *audio=nullptr; + int samples_in_frame; + + for (auto n:nodes) n.second->reset(); + + if (usingaudio){ + samples_in_frame=(audioloader.get_sample_rate())/framerate; + string whether=usingaudio?"Loading":"Cannot load"; + logger.information(whether+" audio file: "+audio_filename+", each frame contains "+toString(samples_in_frame)+" samples at "+toString(audioloader.get_sample_rate())+" hz"); + audioframe=new uint16_t[(samples_in_frame+exporter.get_audio_framesize())*audioloader.get_number_channels()]; + audio=new uint16_t[samples_in_frame*audioloader.get_number_channels()]; + } + + double vstep=1.0/framerate; + double vf=start*vstep; + double af=start*vstep; + int aoffs=0; + int audioend=0; + Audio_frame *a; + int64_t sample_start=(start*audioloader.get_sample_rate())/framerate; + + double end=duration; + if (use_dash) end=vf+mpd_chunk_length; + + while (vf<min(duration,stop*vstep)&&!cancelled){ + + vf+=vstep; + progress=vf/duration; + //end+= + } + +} + bool Graph::set_resolution(int w,int h){ if (w>64&&h>48){ outW=w; diff --git a/rotord/src/graph.h b/rotord/src/graph.h index bb8f83f..bfd175b 100644 --- a/rotord/src/graph.h +++ b/rotord/src/graph.h @@ -25,7 +25,19 @@ copy nodes ` namespace Rotor { class Graph{ public: - Graph(){duration=20.0;loaded = false;audio_loaded=false;bitRate=0;outW=640;outH=360;audio_thumb=new Audio_thumbnailer();use_fragmentation=false;analysis_seed=0;Log_name="";}; + Graph(){ + duration=20.0; + loaded = false; + audio_loaded=false; + bitRate=0; + outW=640; + outH=360; + audio_thumb=new Audio_thumbnailer(); + use_fragmentation=false; + analysis_seed=0; + Log_name=""; + mpd_chunk_length=2.0; + }; Graph(const string& _uid,const string& _desc){ Graph(); init(_uid,_desc); @@ -53,6 +65,7 @@ namespace Rotor { Node* find_node(const string &type); bool signal_render(xmlIO &XML,const string &node,const double framerate); bool video_render(const string &output_filename,const double framerate,int start, int end); + bool video_render2(const string &output_filename,const double framerate,int start, int end); bool load(string data,string media_path); bool loadFile(string &filename,string media_path); bool parseXml(string media_path); @@ -102,6 +115,8 @@ namespace Rotor { int analysis_seed; string Log_name; + double mpd_chunk_length; + }; class Thumbnailer{ public: diff --git a/rotord/src/rendercontext.cpp b/rotord/src/rendercontext.cpp index 813573b..39f9c9b 100644 --- a/rotord/src/rendercontext.cpp +++ b/rotord/src/rendercontext.cpp @@ -36,7 +36,7 @@ void Render_context::runTask() { if(cmd.task==RENDER) { state=RENDERING; renders[cmd.uid]=Render_status(RENDERING); - if(graph.video_render(output_filename,output_framerate,start,stop)){ + if(graph.video_render2(output_filename,output_framerate,start,stop)){ state=IDLE; if (graph.cancelled) renders[cmd.uid].status=CANCELLED; else renders[cmd.uid].status=RENDER_READY; |
