diff options
Diffstat (limited to 'rotord/src/rendercontext.cpp')
| -rw-r--r-- | rotord/src/rendercontext.cpp | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/rotord/src/rendercontext.cpp b/rotord/src/rendercontext.cpp new file mode 100644 index 0000000..7362d64 --- /dev/null +++ b/rotord/src/rendercontext.cpp @@ -0,0 +1,386 @@ +#include "rotor.h" + + +using namespace Rotor; +void Render_context::runTask() { + while (!isCancelled()) { + int cmd=0; + mutex.lock(); + if (work_queue.size()){ + cmd=work_queue[0]; + work_queue.pop_front(); + } + mutex.unlock(); + if(cmd==ANALYSE_AUDIO) { + state=ANALYSING_AUDIO; + vector<Base_audio_processor*> processors; + processors.push_back(audio_thumb); + vector<Node*> analysers=graph.find_nodes("audio_analysis"); + for (auto a: analysers) { + processors.push_back(dynamic_cast<Base_audio_processor*>(a)); + } + if (load_audio(audio_filename,processors)) { + audio_loaded=true; + state=IDLE; + } + else { + //an error occurred: TODO have to clean up allocated data. autoptr? + audio_loaded=false; + state=IDLE; + } + } + if(cmd==RENDER) { + state=RENDERING; + if(graph.video_render(output_filename,audio_filename,output_framerate,progress)){ + state=IDLE; + } + else { + //an error occurred: TODO have to clean up allocated data. autoptr? + cerr<<"Rotor: render failed"<<endl; + state=IDLE; + } + } + sleep(100); + } + printf("Rotor: stopping thread\n"); +} +void Render_context::add_queue(int item) { + mutex.lock(); + work_queue.push_back(item); + mutex.unlock(); +} +void Render_context::session_command(const std::vector<std::string>& command,xmlIO& XML,HTTPResponse::HTTPStatus& status){ + Logger& logger = Logger::get("Rotor"); + status=HTTPResponse::HTTP_BAD_REQUEST; //error by default + if (command[2]=="resolution") { + if (command[0]=="PUT") { + if (command.size()>2) { + if (state==IDLE) { + Poco::StringTokenizer t1(command[3],","); + if (t1.count()>1){ + int w=ofToInt(t1[0]); + int h=ofToInt(t1[1]); + if (graph.set_resolution(w,h)){ + logger.information("resolution set to "+t1[0]+"x"+t1[1]); + XML.addValue("status","resolution set to "+t1[0]+"x"+t1[1]); + status=HTTPResponse::HTTP_OK; + } + else { + logger.error("ERROR: invalid resolution request: "+t1[0]+"x"+t1[1]); + XML.addValue("error","invalid resolution request: "+t1[0]+"x"+t1[1]); + } + } + } + else { + XML.addValue("error","session busy"); + } + } + } + } + if (command[2]=="audio") { + if (command[0]=="PUT") { //get audio file location and initiate analysis + if (command.size()>2) { + if (state==IDLE) { + audio_filename=media_dir+command[3]; //for now, store session variables in memory //check file exists + Poco::File f=Poco::File(audio_filename); + if (f.exists()) { + //pass to worker thread ??if engine is ready?? ??what if engine has finished but results aren't read?? + add_queue(ANALYSE_AUDIO); + status=HTTPResponse::HTTP_OK; + logger.information("Starting audio analysis: "+command[3]); + XML.addValue("status","Starting audio analysis: "+command[3]); + } + else { + status=HTTPResponse::HTTP_NOT_FOUND; + logger.error("ERROR: audio file "+command[3]+" not found"); + XML.addValue("error",command[3]+" not found"); + } + + } + else { + logger.error("ERROR: Session busy"); + XML.addValue("error","Session busy"); + } + } + } + if (command[0]=="GET") { + if (state==ANALYSING_AUDIO) { + status=HTTPResponse::HTTP_OK; + XML.addValue("status","Analysing audio"); + char c[20]; + sprintf(c,"%02f",progress); + XML.addValue("progress",string(c)); + } + else if (audio_loaded) { + //not sure about this-- should this state be retained? + //can the data only be read once? + //for now + status=HTTPResponse::HTTP_OK; + XML.addValue("status","Audio ready"); + XML.addValue("audio",audio_thumb->print()); + } + else { + logger.error("ERROR: audio thumbnail requested but no audio loaded"); + XML.addValue("error","No audio loaded"); + } + } + if (command[0]=="DELETE") { + if (state==IDLE) { + audio_filename=""; + logger.information("Audio deleted"); + XML.addValue("status","Audio deleted"); + status=HTTPResponse::HTTP_OK; + } + else { + logger.error("ERROR: Session busy"); + XML.addValue("error","Session busy"); + } + } + } + if (command[2]=="graph") { + if (command[0]=="GET") { + if (graph.loaded) { + status=HTTPResponse::HTTP_OK; + //XML.addValue("patchbay",graph.toString()); + logger.information("Requested graph"); + XML.loadFromBuffer(graph.toString()); + } + else { + logger.error("ERROR: graph not loaded: check XML"); + XML.addValue("error","graph not loaded: check XML"); + } + } + if (command[0]=="PUT") { //get new graph from file + if (command.size()>2) { + //should interrupt whatever is happening? + //before begining to load from xml + if (state==IDLE) { //eventually not like this + if (graph.load(command[3])) { + status=HTTPResponse::HTTP_OK; + logger.information("Loaded graph from http PUT body"); + XML.addValue("status","Loaded graph from PUT body"); + if (audio_loaded) { + add_queue(ANALYSE_AUDIO); + status=HTTPResponse::HTTP_OK; + logger.information("Starting audio analysis for graph: "+command[3]); + XML.addValue("status","Starting audio analysis for graph: "+command[3]); + } + } + else { + string graph_filename=graph_dir+command[3]; + Poco::File f=Poco::File(graph_filename); + if (f.exists()) { + if (graph.loadFile(graph_filename)) { + status=HTTPResponse::HTTP_OK; + //XML.addValue("patchbay",graph.toString()); + //XML.loadFromBuffer(graph.toString()); + XML=graph.xml; + //the graph could actually contain an xml object and we could just print it here? + //or could our nodes even be subclassed from xml nodes? + //the graph or the audio could load first- have to analyse the audio with vamp after the graph is loaded + //for now the graph must load 1st + if (audio_loaded) { + add_queue(ANALYSE_AUDIO); + status=HTTPResponse::HTTP_OK; + logger.information("Starting audio analysis for graph: "+command[3]); + XML.addValue("status","Starting audio analysis for graph: "+command[3]); + } + } + else { + status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; //~/sources/poco-1.4.6-all/Net/include/Poco/Net/HTTPResponse.h + logger.error("ERROR: graph not loaded: check XML"); + XML.addValue("error","graph not loaded: check XML"); + } + } + else { + status=HTTPResponse::HTTP_NOT_FOUND; + logger.error("ERROR: "+command[3]+" not found"); + XML.addValue("error",command[3]+" not found"); + } + } + } + } + } + if (command[0]=="DELETE") { + //for now + graph=Graph(); + logger.information("graph deleted"); + XML.addValue("status","graph deleted"); + status=HTTPResponse::HTTP_OK; + } + } + if (command[2]=="signal") { + if (command[0]=="GET") { //generate xml from 1st signal output + if (state==IDLE) { + //direct call for testing + float framerate=25.0f; + //if (command.size()>2) { + // framerate=ofToFloat(command[3]); + //} + string signal_xml; + if (graph.signal_render(signal_xml,framerate)){ + status=HTTPResponse::HTTP_OK; + logger.information("rendering signal to xml"); + XML.addValue("signal",signal_xml); //this doesn't work >> pseudo xml + } + else { + status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; + logger.error("ERROR: could not render output signal"); + XML.addValue("error","could not render output signal"); + } + //else { + // status=HTTPResponse::HTTP_NOT_FOUND; + // XML.addValue("error","Signal output not found in graph"); + //} + } + else { + status=HTTPResponse::HTTP_SERVICE_UNAVAILABLE; + logger.error("ERROR: context busy"); + XML.addValue("error","Context busy"); + } + } + } + if (command[2]=="video") { + if (command[0]=="PUT") { //get vide file location and initiate analysis + if (command.size()>4) { //there should be a filename + a destination node + if (state==IDLE) { + string video_filename=media_dir+command[4]; + //check file exists + Poco::File f=Poco::File(video_filename); + if (f.exists()) { + if (load_video(command[3],video_filename)) { + //pass to worker thread ??if engine is ready?? ??what if engine has finished but results aren't read?? + //DUMMY RESPONSE + status=HTTPResponse::HTTP_OK; + logger.information("Succesfully loaded "+command[4]+" into video node "+command[3]); + XML.addValue("status","Succesfully loaded "+command[4]+" into video node "+command[3]); + } + else { + status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; + logger.error("ERROR: could not load "+command[4]+" into video node "+command[3]); + XML.addValue("error","could not load "+command[4]+" into video node "+command[3]); + } + } + else { + status=HTTPResponse::HTTP_NOT_FOUND; + logger.error("ERROR: "+command[4]+" not found"); + XML.addValue("error",command[4]+" not found"); + } + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Session busy"); + XML.addValue("error","Session busy"); + } + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Bad request"); + XML.addValue("error","Bad request"); + } + } + } + if (command[2]=="render") { + if (command[0]=="GET") { + if(state==RENDERING){ + status=HTTPResponse::HTTP_OK; + XML.addValue("status","Rendering video"); + XML.addValue("progress",ofToString(progress)); + } + else { + logger.error("ERROR: Render progress requested but not rendering"); + XML.addValue("error","Not rendering"); + } + } + if (command[0]=="PUT") { + if (command.size()>2) { + if (state==IDLE) { + output_filename=output_dir+command[3]; + if (command.size()>3) { +// output_framerate=ofToFloat(command[4]); + } + add_queue(RENDER); + status=HTTPResponse::HTTP_OK; + logger.information("Starting render: "+command[3]); + XML.addValue("status","Starting render: "+command[3]); + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: Session busy"); + XML.addValue("error","Session busy"); + } + } + else { + status=HTTPResponse::HTTP_BAD_REQUEST; + logger.error("ERROR: No output file specified"); + XML.addValue("error","No output file specified"); + } + } + if (command[0]=="DELETE") { + status=HTTPResponse::HTTP_OK; + logger.error("ERROR: Not implemented"); + XML.addValue("status","DUMMY RESPONSE: cancelling render"); + } + } +} + +bool Render_context::load_audio(const string &filename,vector<Base_audio_processor*> processors){ + Logger& logger = Logger::get("Rotor"); + logger.information("Starting audio analysis"); + + libav::audioloader loader; + loader.setup(filename); + + graph.duration=((float)loader.formatContext->duration)/AV_TIME_BASE; + + int rate = loader.codecContext->sample_rate; + int samples = ((loader.formatContext->duration + 5000)*rate)/AV_TIME_BASE; //why 5000 more? + int channels= loader.codecContext->channels; + int bits = loader.codecContext->bits_per_raw_sample; + + for (auto p: processors) { + if(!p->init(channels,bits,samples,rate) ){ + logger.error("ERROR: Audio plugin failed to initialse"); + return false; + } + } + + AVFrame* frame=loader.get_frame(); + int sample_processed=0; + + while (frame) + { + //now we can pass the data to the processor(s) + for (auto p: processors) { + p->process_frame(frame->data[0],frame->nb_samples); + } + sample_processed+=frame->nb_samples; + //mutex.lock(); + progress=((float)sample_processed)/samples; //atomic on 64 bit? + //mutex.unlock(); + + frame=loader.get_frame(); + } + + loader.close(); + + for (auto p: processors) { + p->cleanup(); + p->print_summary(); + } + + logger.information("Finished audio analysis"); + return true; +} +bool Render_context::load_video(const string &nodeID,const string &filename){ + //this is a good standard example of how to find + //a node of a specific type by ID and do something + if (graph.nodes.find(nodeID)!=graph.nodes.end()){ + if (graph.nodes[nodeID]->type=="video_loader") { + if (((Video_loader*)graph.nodes[nodeID])->load(filename)) { + return true; + } + } + } + return false; +} |
