#include <common/exception/exceptions.h>\r
#include <common/concurrency/executor.h>\r
#include <common/diagnostics/graph.h>\r
+#include <common/utility/assert.h>\r
#include <common/utility/timer.h>\r
#include <common/gl/gl_check.h>\r
\r
\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
const video_format_desc format_desc_;\r
\r
safe_ptr<diagnostics::graph> graph_;\r
std::unordered_map<int, image_transform> image_transforms_;\r
std::unordered_map<int, audio_transform> audio_transforms_;\r
\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
- implementation(const video_format_desc& format_desc, const output_func& output) \r
- : format_desc_(format_desc)\r
+ implementation(const printer& parent_printer, const video_format_desc& format_desc, const output_func& output) \r
+ : parent_printer_(parent_printer)\r
+ , format_desc_(format_desc)\r
, graph_(diagnostics::create_graph(narrow(print())))\r
, image_mixer_(format_desc)\r
, output_(output)\r
- , executor_(L"frame_mixer_device")\r
+ , executor_(print())\r
{\r
graph_->guide("frame-time", 0.5f); \r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
graph_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
- graph_->set_color("output-buffer", diagnostics::color( 0.0f, 1.0f, 0.0f)); \r
+ graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
executor_.start();\r
executor_.set_capacity(2);\r
CASPAR_LOG(info) << print() << L" Successfully initialized."; \r
}\r
\r
- ~implementation()\r
- {\r
- CASPAR_LOG(info) << print() << L" Shutting down."; \r
- }\r
-\r
void send(const std::vector<safe_ptr<draw_frame>>& frames)\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(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(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
audio_mixer_.end_pass();\r
+\r
graph_->update("frame-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));\r
\r
output_(make_safe<const read_frame>(std::move(image.get()), std::move(audio)));\r
graph_->update("tick-time", static_cast<float>(wait_perf_timer_.elapsed()/format_desc_.interval*0.5));\r
wait_perf_timer_.reset();\r
\r
- graph_->set("output-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
+ graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
});\r
- graph_->set("output-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
+ graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
}\r
\r
safe_ptr<write_frame> create_frame(const pixel_format_desc& desc)\r
{\r
return make_safe<write_frame>(desc, image_mixer_.create_buffers(desc));\r
}\r
- \r
- image_transform get_image_transform(int index)\r
+ \r
+ void set_image_transform(const image_transform& transform, int)\r
{\r
- return executor_.invoke([&]{return image_transforms_[index];});\r
+ executor_.invoke([&]\r
+ {\r
+ root_image_transform_ = transform;\r
+ });\r
}\r
\r
- audio_transform get_audio_transform(int index)\r
+ void set_audio_transform(const audio_transform& transform, int)\r
{\r
- return executor_.invoke([&]{return audio_transforms_[index];});\r
+ executor_.invoke([&]\r
+ {\r
+ root_audio_transform_ = transform;\r
+ });\r
}\r
\r
- void set_image_transform(int index, image_transform&& transform)\r
+ void set_image_transform(int index, const image_transform& transform, int)\r
{\r
- return executor_.invoke([&]{image_transforms_[index] = std::move(transform);});\r
+ executor_.invoke([&]\r
+ {\r
+ image_transforms_[index] = transform;\r
+ });\r
}\r
\r
- void set_audio_transform(int index, audio_transform&& transform)\r
+ void set_audio_transform(int index, const audio_transform& transform, int)\r
{\r
- return executor_.invoke([&]{audio_transforms_[index] = std::move(transform);});\r
+ executor_.invoke([&]\r
+ {\r
+ audio_transforms_[index] = transform;\r
+ });\r
+ }\r
+ \r
+ void apply_image_transform(const std::function<image_transform(const image_transform&)>& transform, int mix_duration)\r
+ {\r
+ return executor_.invoke([&]\r
+ {\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 mix_duration)\r
+ {\r
+ return executor_.invoke([&]\r
+ {\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
+ {\r
+ executor_.invoke([&]\r
+ {\r
+ image_transforms_[index] = transform(image_transforms_[index]);\r
+ });\r
+ }\r
+\r
+ void apply_audio_transform(int index, const std::function<audio_transform(audio_transform)>& transform, int)\r
+ {\r
+ executor_.invoke([&]\r
+ {\r
+ audio_transforms_[index] = transform(audio_transforms_[index]);\r
+ });\r
}\r
\r
std::wstring print() const\r
{\r
- return L"Video/Audio Mixer [" + format_desc_.name + L"]";\r
+ return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"mixer";\r
}\r
};\r
\r
-frame_mixer_device::frame_mixer_device(const video_format_desc& format_desc, const output_func& output) : impl_(new implementation(format_desc, output)){}\r
+frame_mixer_device::frame_mixer_device(const printer& parent_printer, const video_format_desc& format_desc, const output_func& output) : impl_(new implementation(parent_printer, format_desc, output)){}\r
frame_mixer_device::frame_mixer_device(frame_mixer_device&& other) : impl_(std::move(other.impl_)){}\r
void frame_mixer_device::send(const std::vector<safe_ptr<draw_frame>>& frames){impl_->send(frames);}\r
const video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->format_desc_; }\r
desc.planes.push_back(pixel_format_desc::plane(get_video_format_desc().width, get_video_format_desc().height, 4));\r
return create_frame(desc);\r
}\r
-image_transform frame_mixer_device::get_image_transform(int index){return impl_->get_image_transform(index);}\r
-audio_transform frame_mixer_device::get_audio_transform(int index){return impl_->get_audio_transform(index);}\r
-void frame_mixer_device::set_image_transform(int index, image_transform&& transform){impl_->set_image_transform(index, std::move(transform));}\r
-void frame_mixer_device::set_audio_transform(int index, audio_transform&& transform){impl_->set_audio_transform(index, std::move(transform));}\r
+void frame_mixer_device::set_image_transform(const image_transform& transform, int mix_duration){impl_->set_image_transform(transform, mix_duration);}\r
+void frame_mixer_device::set_image_transform(int index, const image_transform& transform, int mix_duration){impl_->set_image_transform(index, transform, mix_duration);}\r
+void frame_mixer_device::set_audio_transform(const audio_transform& transform, int mix_duration){impl_->set_audio_transform(transform, mix_duration);}\r
+void frame_mixer_device::set_audio_transform(int index, const audio_transform& transform, int mix_duration){impl_->set_audio_transform(index, transform, mix_duration);}\r
+void frame_mixer_device::apply_image_transform(const std::function<image_transform(image_transform)>& transform, int mix_duration ){impl_->apply_image_transform(transform, mix_duration);}\r
+void frame_mixer_device::apply_image_transform(int index, const std::function<image_transform(image_transform)>& transform, int mix_duration){impl_->apply_image_transform(index, transform, mix_duration);}\r
+void frame_mixer_device::apply_audio_transform(const std::function<audio_transform(audio_transform)>& transform, int mix_duration){impl_->apply_audio_transform(transform, mix_duration);}\r
+void frame_mixer_device::apply_audio_transform(int index, const std::function<audio_transform(audio_transform)>& transform, int mix_duration ){impl_->apply_audio_transform(index, transform, mix_duration);}\r
\r
}}
\ No newline at end of file