#include "rotor.h" using namespace Rotor; using Poco::Logger; const string Graph::toString(){ string xmlgraph; if (loaded) { xml.copyXmlToString(xmlgraph); return xmlgraph; } else return ""; } vector Graph::find_nodes(const string &type){ vector found; for (std::unordered_map::iterator it=nodes.begin();it!=nodes.end();++it) { if (it->second->type==type) found.push_back(it->second); } return found; }; Node* Graph::find_node(const string &type){ for (std::unordered_map::iterator it=nodes.begin();it!=nodes.end();++it) { if (it->second->type==type) return it->second; } return nullptr; //can be tested against }; /* bool Graph::signal_render(string &signal_xml,const float framerate) { if (find_node("signal_output")) { Signal_output *signal_output=dynamic_cast(find_node("signal_output")); return signal_output->render(duration,framerate,signal_xml); } cerr<<"Rotor: signal output node not found"<(nodes[node])){ XML.addValue("features",dynamic_cast(nodes[node])->get_features()); return true; } XML.addValue("error","node /"+node+"/ is not an Audio processor"); return false; } XML.addValue("error","could not find node /"+node+"/"); return false; } bool Graph::preview(xmlIO &XML,string &node,string &_format,int frame,int w,int h){ if (nodes.find(node)!=nodes.end()){ float t=frame/framerate; XML.addTag("preview"); XML.addAttribute("preview","frame",ofToString(frame),0); XML.addAttribute("preview","nodeID",node,0); XML.pushTag("preview"); if (dynamic_cast(nodes[node])){ Time_spec ts=Time_spec(t,framerate,0.0f); XML.addValue("signal",dynamic_cast(nodes[node])->get_output(ts)); } if (dynamic_cast(nodes[node])){ Frame_spec fs=Frame_spec(t,framerate,0.0f,w,h); Image *img=dynamic_cast(nodes[node])->get_output(fs); vector buf; string format=(_format==""?".png":_format); if (cv::imencode(format,img->rgb,buf)){ //, const vector& params=vector()) stringstream output; Poco::Base64Encoder *enc=new Poco::Base64Encoder(output); enc->write(buf.data(),buf.size()); enc->close(); delete enc; XML.addValue("image",output.str()); XML.addAttribute("image","format",format,0); } } XML.popTag(); return true; } return false; } bool Graph::video_render(const string &output_filename,const float framerate,float& progress) { //vector loaders=find_nodes("video_loader"); //for (auto i:loaders){ // if (!dynamic_cast(i)->isLoaded) { // cerr<<"Rotor: all loaders must be populated before rendering"<(find_node("video_output")); for (auto f: find_nodes("video_feedback")){ (dynamic_cast(f))->set_feedback(&(video_output->image)); } // //setup defaults int bitRate=5000000; AVCodecID codecId=AV_CODEC_ID_H264; //MPEG4; std::string container ="mp4"; //at the moment it crashes if you render before audio is loaded and also on 2nd render libav::exporter exporter; float spct=100.0f/duration; if (exporter.setup(outW,outH,bitRate,framerate,container)) { //codecId, if (exporter.record(output_filename)) { libav::audioloader audioloader; bool usingaudio=audioloader.setup(audio_filename); float *avframe=nullptr; Logger& logger = Logger::get("Rotor"); logger.information("Video_output rendering "+output_filename+": "+ofToString(duration)+" seconds at "+ofToString(framerate)+" fps, audio frame size: "+ofToString(exporter.get_audio_framesize())); //25fps video and 43.06640625fps audio? hmm //how to get the timecodes correct for the interleaved files struct timeval start, end; gettimeofday(&start, NULL); uint16_t *audioframe; int samples_in_frame; if (usingaudio){ //does audioloader output interleaved samples? samples_in_frame=(audioloader.codecContext->sample_rate)/framerate; string whether=usingaudio?"Loading":"Cannot load"; logger.information(whether+" audio file: "+audio_filename+", each frame contains "+ofToString(samples_in_frame)+" samples at "+ofToString(audioloader.codecContext->sample_rate)+" hz"); audioframe=new uint16_t[(samples_in_frame+exporter.get_audio_framesize())*audioloader.codecContext->channels]; } float vstep=1.0f/framerate; float v=0.0f; float vf=0.0f; float af=0.0f; int aoffs=0; int audioend=0; Audio_frame *a; while (vf0){ //shift down samples int s=0; while ((s+aoffs)channels;j++){ audioframe[s*audioloader.codecContext->channels+j]=audioframe[(s+aoffs)*audioloader.codecContext->channels+j]; } s++; } aoffs=s; } for (int i=0;ichannels;j++){ audioframe[(aoffs+i)*audioloader.codecContext->channels+j]=audio[i*audioloader.codecContext->channels+j]; } } audioend=aoffs+samples_in_frame; aoffs=0; //while (fless(vf+vstep,af+exporter.get_audio_step())) { while (aoffs+exporter.get_audio_framesize()channels)); af+=exporter.get_audio_step(); aoffs+=exporter.get_audio_framesize(); } a=new Audio_frame(audio,audioloader.codecContext->channels,samples_in_frame); } //[mp3 @ 0x7fffe40330e0] max_analyze_duration 5000000 reached at 5015510 microseconds //[mp3 @ 0x7fffe4033ec0] Insufficient thread locking around avcodec_open/close() //[mp3 @ 0x7fffe40330e0] Estimating duration from bitrate, this may be inaccurate //[libx264 @ 0x7fffe8003940] using cpu capabilities: MMX2 SSE2Fast SSSE3 FastShuffle SSE4.2 //[libx264 @ 0x7fffe8003940] profile High, level 3.0 //[libx264 @ 0x7fffe8003940] 264 - core 123 r2189 35cf912 - H.264/MPEG-4 AVC codec - Copyleft 2003-2012 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=10 keyint_min=1 scenecut=40 intra_refresh=0 rc_lookahead=10 rc=abr mbtree=1 bitrate=400 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00 //Assertion ff_avcodec_locked failed at libavcodec/utils.c:2967 //cerr<<"videoloader: "<get_output(Frame_spec(vf,framerate,duration,outW,outH,a)); } else i=video_output->get_output(Frame_spec(vf,framerate,duration,outW,outH)); if (i) { exporter.encodeFrame(i->RGBdata); } vf+=vstep; progress=vf/duration; if (usingaudio) {delete a;}; } exporter.finishRecord(); gettimeofday(&end, NULL); float mtime = ((end.tv_sec-start.tv_sec) + (end.tv_usec-start.tv_usec)/1000000.0) + 0.5; logger.information("Video_output: rendered "+output_filename+": in "+ofToString(mtime)+" seconds"); if (usingaudio) { audioloader.close(); delete[] audioframe; } return true; } } return false; } cerr<<"Rotor: video output node not found"<64&&h>48){ outW=w; outH=h; return true; } else return false; } bool Graph::load(string data,string media_path){ if (xml.loadFromBuffer(data)){ return parseXml(media_path); } return parseJson(data,media_path); return false; } bool Graph::loadFile(string &filename,string media_path){ //if (loaded) printf("loading graph: %s\n",(filename).c_str()); if (xml.loadFile(filename)){ return parseXml(media_path); } Poco::FileInputStream fis(filename); Poco::CountingInputStream countingIstr(fis); std::string str; Poco::StreamCopier::copyToString(countingIstr, str); return parseJson(str,media_path); } bool Graph::check_audio(string audio,string path){ if (audio!="") { Poco::File f=Poco::File(path+audio); if (f.exists()) { audio_filename=path+audio; audio_loaded=true; cerr<<"Rotor: loading "< settings; vector attrs; //iterate members settings["type"]=jnodes[i]["type"].asString(); settings["title"]=jnodes[i]["title"].asString(); settings["description"]=jnodes[i]["description"].asString(); Node* node=factory.create(settings); if (node) { string nodeID=jnodes[i]["id"].asString(); if (nodes.find(nodeID)==nodes.end()){ cerr << "Rotor: creating node '"<inputs.size()>j) { string fromID=jnodes[i]["signal_inputs"][j]["from"].asString(); if (fromID!=""){ if (nodes.find(fromID)!=nodes.end()) { if (!nodes[nodeID]->inputs[j]->connect((Signal_node*)nodes[fromID])){ cerr << "Rotor: graph loader cannot connect input " << j << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked input " << j << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else cerr << "Rotor: linking input " << j << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } } else cerr << "Rotor: input " << j << " of node: '" << nodeID << "' does not exist" << endl; } //image inputs if (dynamic_cast(nodes[nodeID])!=nullptr) { for (int k=0;kimage_inputs.size()<=k) { if (nodes[nodeID]->duplicate_inputs) { while(((Image_node*)nodes[nodeID])->image_inputs.size()<=k){ ((Image_node*)nodes[nodeID])->create_image_input(settings["description"],settings["title"]); } } } if (((Image_node*)nodes[nodeID])->image_inputs.size()>k) { string fromID=jnodes[i]["image_inputs"][k]["from"].asString(); if (fromID!=""){ if (nodes.find(fromID)!=nodes.end()) { if (dynamic_cast(nodes[fromID])!=nullptr) { if (!dynamic_cast(nodes[nodeID])->image_inputs[k]->connect((Image_node*)nodes[fromID])){ cerr << "Rotor: graph loader cannot connect image input " << k << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked image input " << k << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else cerr << "Rotor: cannot link image input "<< k << " of node '" << nodeID << "' to node '" << fromID << "' : not an image node" << endl; } else cerr << "Rotor: linking image input " << k << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } } else cerr << "Rotor: image input number " << k << " of node: '" << nodeID << "' does not exist" << endl; } } //parameters for (int l=0;lparameters.find(parameter)!=nodes[nodeID]->parameters.end()) { float val=jnodes[i]["parameters"][l]["value"].asFloat(); nodes[nodeID]->parameters.find(parameter)->second->value=val; string fromID=jnodes[i]["parameters"][l]["from"].asString(); if (nodes.find(fromID)!=nodes.end()) { if (!nodes[nodeID]->parameters[parameter]->connect(nodes[fromID])){ cerr << "Rotor: graph loader cannot connect parameter " << parameter << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked parameter " << parameter << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else if (fromID!="") cerr << "Rotor: linking parameter " << parameter << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } else cerr << "Rotor: cannot find parameter '" << parameter << "' of "<attributes.find(attribute)!=nodes[nodeID]->attributes.end()) { string val=jnodes[i]["attributes"][m]["value"].asString(); nodes[nodeID]->attributes.find(attribute)->second->value=val; } else cerr << "Rotor: cannot find attribute '" << attribute << "' of "< settings; vector attrs; xml.getAttributeNames("node",attrs,i1); for (auto& attr: attrs) { settings[attr]=xml.getAttribute("node",attr,"",i1); //cerr << "Got attribute: " << attr << ":" << xml.getAttribute("node",attr,"",i1) << endl; } settings["media_path"]=media_path; Node* node=factory.create(settings); if (node) { string nodeID=xml.getAttribute("node","ID","",i1); if (nodes.find(nodeID)==nodes.end()){ string nodetype=xml.getAttribute("node","type","",i1); cerr << "Rotor: creating node '"<create_signal_input(xml.getAttribute("signal_input","description","",i2),xml.getAttribute("signal_input","title","",i2)); if ((nodes[nodeID])->inputs.size()>i2) { string fromID=xml.getAttribute("signal_input","from","",i2); if (nodes.find(fromID)!=nodes.end()) { if (!nodes[nodeID]->inputs[i2]->connect((Signal_node*)nodes[fromID])){ cerr << "Rotor: graph loader cannot connect input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked input " << i2 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else cerr << "Rotor: linking input " << i2 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } else cerr << "Rotor: input " << i2 << " of node: '" << nodeID << "' does not exist" << endl; } int n3=xml.getNumTags("image_input"); for (int i3=0;i3image_inputs.size()<=i3&((Image_node*)nodes[nodeID])->duplicate_inputs){ string desc=((Image_node*)nodes[nodeID])->image_inputs[0]->description; string title=((Image_node*)nodes[nodeID])->image_inputs[0]->title; while(((Image_node*)nodes[nodeID])->image_inputs.size()<=i3){ ((Image_node*)nodes[nodeID])->create_image_input(desc,title); } } //((Image_node*)nodes[nodeID])->create_image_input(xml.getValue("image_input","",i3)); if (((Image_node*)nodes[nodeID])->image_inputs.size()>i3) { string fromID=xml.getAttribute("image_input","from","",i3); if (nodes.find(fromID)!=nodes.end()) { if (!(((Image_node*)nodes[nodeID])->image_inputs[i3]->connect((Image_node*)nodes[fromID]))){ cerr << "Rotor: graph loader cannot connect image input " << i3 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked image input " << i3 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else cerr << "Rotor: linking image input " << i3 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } else cerr << "Rotor: image input number " << i3 << " of node: '" << nodeID << "' does not exist" << endl; } int n4=xml.getNumTags("parameter"); for (int i4=0;i4parameters.find(parameter)!=nodes[nodeID]->parameters.end()) { string val=xml.getAttribute("parameter","value","",i4); if (val!="") nodes[nodeID]->parameters.find(parameter)->second->value=ofToFloat(val); string fromID=xml.getAttribute("parameter","from","",i4); if (nodes.find(fromID)!=nodes.end()) { if (!nodes[nodeID]->parameters[parameter]->connect(nodes[fromID])){ cerr << "Rotor: graph loader cannot connect parameter input " << i4 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked parameter input " << i4 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else if (fromID!="") cerr << "Rotor: linking parameter input " << i4 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } else cerr << "Rotor: cannot find parameter input '" << parameter << "' of "<parameters.find(parameter)!=nodes[nodeID]->parameters.end()) { string val=xml.getAttribute("parameter_input","value","",i4); if (val!="") nodes[nodeID]->parameters.find(parameter)->second->value=ofToFloat(val); string fromID=xml.getAttribute("parameter_input","from","",i4); if (nodes.find(fromID)!=nodes.end()) { if (!nodes[nodeID]->parameters[parameter]->connect(nodes[fromID])){ cerr << "Rotor: graph loader cannot connect parameter input " << i4 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; return false; } else cerr << "Rotor: linked parameter input " << i4 << " of node '" << nodeID << "' to node '" << fromID << "'" << endl; } else cerr << "Rotor: linking parameter input " << i4 << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl; } else cerr << "Rotor: cannot find parameter input '" << parameter << "' of "<set_parameter(xml.getAttribute("parameter","name","",i5),xml.getAttribute("parameter","value","",i5)); //} //if (n5>0) cerr << "Rotor: found " << n5 << " extra parameters for node '" << nodeID << "'" << endl; xml.popTag(); } } else cerr << "Rotor: duplicate node '"<