summaryrefslogtreecommitdiff
path: root/rotord/src/rotor.cpp
blob: 8905a6e2e11b3de61a978ffd7d6abcdb9528fc77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "rotor.h"
#include "nodes_audio_analysis.h"
#include "nodes_maths.h"
#include "nodes_drawing.h"
#include "nodes_filters.h"
#include "nodes_transform.h"

using namespace Rotor;
using Poco::Logger;
Node_factory::Node_factory(){
	//for now, statically load prototype map in constructor
	add_type("time",new Time());
	add_type("track_time",new Track_time());
	add_type("at_track_time",new At_track_time());
	add_type("signal_output",new Signal_output());
	add_type("testcard",new Testcard());
	add_type("invert",new Invert());
	add_type("video_cycler",new Video_cycler());
	add_type("signal_colour",new Signal_colour());
	add_type("signal_greyscale",new Signal_greyscale());
	add_type("image_arithmetic",new Image_arithmetic());
	add_type("blend",new Blend());
	add_type("mirror",new Mirror());
	add_type("monochrome",new Monochrome());
	add_type("alpha_merge",new Alpha_merge());
	add_type("difference_matte",new Difference_matte());
	//nodes_audio_analysis.h
	add_type("audio_analysis",new Audio_analysis());
	add_type("act_segmenter",new Act_segmenter());
	//nodes_maths.h
	add_type("comparison",new Comparison()); //TODO: alias to symbols
	add_type("arithmetic",new Arithmetic()); //TODO: alias to symbols
	add_type("bang",new Is_new_integer());
	add_type("on_off",new On_off());
	add_type("random",new Random());
	//nodes_drawing.h
	add_type("shape",new Shape());
	add_type("text",new Text());
	add_type("waves",new Waves());
	//nodes_filters.h
	add_type("blur",new Blur());
	add_type("vhs",new VHS());
	add_type("luma_levels",new Luma_levels());
	add_type("echo_trails",new Echo_trails());
	add_type("rgb_levels",new RGB_levels());
	//nodes_transform.h
	add_type("transform",new Transform());
	add_type("still_image",new Still_image());
	//video nodes
	add_type("video_loader",new Video_loader());
	add_type("video_output",new Video_output());
	add_type("video_feedback",new Video_feedback());
}
bool Signal_input::connect(Node* source) {
	connection=dynamic_cast<Signal_node*>(source);
	if (connection)	return true;
	else return false;
}
float Signal_input::get(const Time_spec& time){	//gets input and updates variable
	if (connection){
		return (((Signal_node*)connection)->get_output(time));
	}
	else return 0.0f;
}
bool Image_input::connect(Node* source) {
	connection=dynamic_cast<Image_node*>(source);
	if (connection)	return true;
	else return false;
}
Image* Image_input::get(const Frame_spec& time){	//gets input and updates variable
	if (connection){
		return (((Image_node*)connection)->get_output(time));
	}
	else return nullptr;
}
float Parameter::get(const Time_spec& time){	//gets input and updates variable
	if (connection){
		value = ((Signal_node*)connection)->get_output(time);
	}
	return value;
}

bool Video_loader::load(const string &_filename){
	Logger& logger = Logger::get("Rotor");
    if (isLoaded) {
    	player.cleanup(); ///should be in decoder class?
    	isLoaded=false;
    }
    Poco::Path path;
	string uri="file://"+_filename;
    isLoaded=player.open(uri);
	if (isLoaded){
		logger.information("Video_loader loaded "+_filename+": "\
			+ofToString(player.getNumberOfFrames())+" frames, "\
			+ofToString(player.getFrameRate())+" fps, "\
			+ofToString(player.getWidth())+"x"+ofToString(player.getHeight())\
			+", channels:"+ofToString(player.getNumberOfChannels()));
		return true;
   	}

	logger.error("Video_loader failed to load "+_filename);

    return false;
}
Image* Video_loader::output(const Frame_spec &frame){

	if (isLoaded){
		//this approach is running into the inability to seek when requesting playback speed > 1.
		//need to cache frames so as to avoid asking for a frame other than the next one.
		//need an algorithm to find the previous keyframe and seek forward

		float clipframerate=(parameters["framerate"]->value==0.0f?player.getFrameRate():parameters["framerate"]->value);

		float clipspeed=(clipframerate/frame.framerate)*parameters["speed"]->value;

		int wanted;
		if (attributes["mode"]->intVal==VIDEOFRAMES_frame) {
			wanted=(((int) ((frame.time*frame.framerate)+0.5))%max(1,player.getNumberOfFrames()-1))+1;  //+1 is necessary because 1st frame in a video is number 1?
		}
		if (attributes["mode"]->intVal==VIDEOFRAMES_blend) {
			wanted=(((int) ((frame.time*frame.framerate*clipspeed)+0.5))%max(1,player.getNumberOfFrames()-1))+1;  //+1 is necessary because 1st frame in a video is number 1?
		}

		if (wanted!=lastframe){
			if (!player.fetchFrame(frame.w,frame.h,wanted)) { //seek fail
				Logger& logger = Logger::get("Rotor");
				logger.error("Video_loader failed to seek frame "+ofToString(wanted)+" of "+attributes["filename"]->value);

				if (image.w>0) return &image; //just return the previous frame if possible
				else return nullptr;
			}
			image.setup_fromRGB(frame.w,frame.h,player.pFrameRGB->data[0],player.pFrameRGB->linesize[0]-(frame.w*3));
			lastframe=wanted;
		}
		return &image;
	}
    return nullptr;
};