summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NT/src/nodes_audio_analysis.h9
-rw-r--r--NT/src/rotor.h25
-rw-r--r--rotord/src/graph.cpp169
-rw-r--r--rotord/src/graph.h17
-rw-r--r--rotord/src/rendercontext.cpp2
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;