summaryrefslogtreecommitdiff
path: root/rotord/src/rendercontext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/src/rendercontext.cpp')
-rw-r--r--rotord/src/rendercontext.cpp386
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;
+}