\r
namespace caspar { namespace core {\r
\r
+ \r
+template<typename T>\r
+class basic_animated_value\r
+{\r
+ T source_;\r
+ T dest_;\r
+ int duration_;\r
+ int time_;\r
+public: \r
+ basic_animated_value()\r
+ : duration_(0)\r
+ , time_(0){}\r
+ basic_animated_value(const T& dest)\r
+ : source_(dest)\r
+ , dest_(dest)\r
+ , duration_(0)\r
+ , time_(0){}\r
+\r
+ basic_animated_value(const T& source, const T& dest, int duration)\r
+ : source_(source)\r
+ , dest_(dest)\r
+ , duration_(duration)\r
+ , time_(0){}\r
+ \r
+ virtual T fetch()\r
+ {\r
+ return lerp(source_, dest_, duration_ < 1 ? 1.0f : static_cast<float>(time_)/static_cast<float>(duration_));\r
+ }\r
+ virtual T fetch_and_tick(int num)\r
+ { \r
+ time_ = std::min(time_+num, duration_);\r
+ return fetch();\r
+ }\r
+};\r
+\r
struct frame_mixer_device::implementation : boost::noncopyable\r
{ \r
const printer parent_printer_;\r
std::unordered_map<int, image_transform> image_transforms_;\r
std::unordered_map<int, audio_transform> audio_transforms_;\r
\r
- image_transform root_image_transform_;\r
- audio_transform root_audio_transform_;\r
+ basic_animated_value<image_transform> root_image_transform_;\r
+ basic_animated_value<audio_transform> root_audio_transform_;\r
\r
executor executor_;\r
public:\r
{ \r
executor_.begin_invoke([=]\r
{\r
+ int num = format_desc_.mode == video_mode::progressive ? 1 : 2;\r
+\r
perf_timer_.reset();\r
auto image = image_mixer_.begin_pass();\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- image_mixer_.begin(root_image_transform_*image_transforms_[frame->get_layer_index()]);\r
+ auto transform = root_image_transform_.fetch_and_tick(num)*image_transforms_[frame->get_layer_index()];\r
+ image_mixer_.begin(transform);\r
frame->process_image(image_mixer_);\r
image_mixer_.end();\r
}\r
auto audio = audio_mixer_.begin_pass();\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- audio_mixer_.begin(root_audio_transform_*audio_transforms_[frame->get_layer_index()]);\r
+ auto transform = root_audio_transform_.fetch_and_tick(num)*audio_transforms_[frame->get_layer_index()];\r
+ audio_mixer_.begin(transform);\r
frame->process_audio(audio_mixer_);\r
audio_mixer_.end();\r
}\r
});\r
}\r
\r
- void apply_image_transform(const std::function<image_transform(const image_transform&)> transform, int)\r
+ void apply_image_transform(const std::function<image_transform(const image_transform&)>& transform, int mix_duration)\r
{\r
return executor_.invoke([&]\r
{\r
- root_image_transform_ = transform(root_image_transform_);\r
+ auto src = root_image_transform_.fetch();\r
+ auto dst = transform(src);\r
+ root_image_transform_ = basic_animated_value<image_transform>(src, dst, mix_duration);\r
});\r
}\r
\r
- void apply_audio_transform(const std::function<audio_transform(audio_transform)> transform, int)\r
+ void apply_audio_transform(const std::function<audio_transform(audio_transform)>& transform, int mix_duration)\r
{\r
return executor_.invoke([&]\r
{\r
- root_audio_transform_ = transform(root_audio_transform_);\r
+ auto src = root_audio_transform_.fetch();\r
+ auto dst = transform(src);\r
+ root_audio_transform_ = basic_animated_value<audio_transform>(src, dst, mix_duration);\r
});\r
}\r
\r
- void apply_image_transform(int index, const std::function<image_transform(image_transform)> transform, int)\r
+ void apply_image_transform(int index, const std::function<image_transform(image_transform)>& transform, int)\r
{\r
executor_.invoke([&]\r
{\r
});\r
}\r
\r
- void apply_audio_transform(int index, const std::function<audio_transform(audio_transform)> transform, int)\r
+ void apply_audio_transform(int index, const std::function<audio_transform(audio_transform)>& transform, int)\r
{\r
executor_.invoke([&]\r
{\r
{\r
if(_parameters[1] == L"OPACITY")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
double value = boost::lexical_cast<double>(_parameters.at(2));\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
}\r
else if(_parameters[1] == L"GAIN")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
double value = boost::lexical_cast<double>(_parameters.at(2));\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
}\r
else if(_parameters[1] == L"FILL_RECT")\r
{\r
- int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
+ int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double y = boost::lexical_cast<double>(_parameters.at(3));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r
}\r
else if(_parameters[1] == L"KEY_RECT")\r
{\r
- int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
+ int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double y = boost::lexical_cast<double>(_parameters.at(3));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r