3 #include "frame_mixer_device.h"
\r
5 #include "audio/audio_mixer.h"
\r
6 #include "audio/audio_transform.h"
\r
7 #include "frame/write_frame.h"
\r
8 #include "frame/read_frame.h"
\r
9 #include "image/image_mixer.h"
\r
10 #include "image/image_transform.h"
\r
12 #include <common/exception/exceptions.h>
\r
13 #include <common/concurrency/executor.h>
\r
14 #include <common/diagnostics/graph.h>
\r
15 #include <common/utility/assert.h>
\r
16 #include <common/utility/timer.h>
\r
17 #include <common/gl/gl_check.h>
\r
19 #include <core/video_format.h>
\r
21 #include <tbb/concurrent_queue.h>
\r
22 #include <tbb/concurrent_unordered_map.h>
\r
24 #include <boost/range/algorithm.hpp>
\r
26 #include <unordered_map>
\r
28 namespace caspar { namespace core {
\r
31 template<typename T>
\r
32 class basic_animated_value
\r
39 basic_animated_value()
\r
42 basic_animated_value(const T& source, const T& dest, int duration)
\r
45 , duration_(duration)
\r
50 return lerp(source_, dest_, duration_ < 1 ? 1.0f : static_cast<float>(time_)/static_cast<float>(duration_));
\r
52 virtual T fetch_and_tick(int num)
\r
54 time_ = std::min(time_+num, duration_);
\r
59 struct frame_mixer_device::implementation : boost::noncopyable
\r
61 const printer parent_printer_;
\r
62 const video_format_desc format_desc_;
\r
64 safe_ptr<diagnostics::graph> graph_;
\r
66 timer wait_perf_timer_;
\r
68 audio_mixer audio_mixer_;
\r
69 image_mixer image_mixer_;
\r
71 output_func output_;
\r
73 std::unordered_map<int, basic_animated_value<image_transform>> image_transforms_;
\r
74 std::unordered_map<int, basic_animated_value<audio_transform>> audio_transforms_;
\r
76 basic_animated_value<image_transform> root_image_transform_;
\r
77 basic_animated_value<audio_transform> root_audio_transform_;
\r
81 implementation(const printer& parent_printer, const video_format_desc& format_desc, const output_func& output)
\r
82 : parent_printer_(parent_printer)
\r
83 , format_desc_(format_desc)
\r
84 , graph_(diagnostics::create_graph(narrow(print())))
\r
85 , image_mixer_(format_desc)
\r
87 , executor_(print())
\r
89 graph_->guide("frame-time", 0.5f);
\r
90 graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));
\r
91 graph_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));
\r
92 graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));
\r
94 executor_.set_capacity(2);
\r
95 CASPAR_LOG(info) << print() << L" Successfully initialized.";
\r
98 void send(const std::vector<safe_ptr<draw_frame>>& frames)
\r
100 executor_.begin_invoke([=]
\r
102 perf_timer_.reset();
\r
103 auto image = image_mixer_.begin_pass();
\r
104 BOOST_FOREACH(auto& frame, frames)
\r
106 if(format_desc_.mode != video_mode::progressive)
\r
108 auto frame1 = make_safe<draw_frame>(frame);
\r
109 auto frame2 = make_safe<draw_frame>(frame);
\r
111 frame1->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);
\r
112 frame2->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);
\r
114 if(frame1->get_image_transform() != frame2->get_image_transform())
\r
115 draw_frame::interlace(frame1, frame2, format_desc_.mode)->process_image(image_mixer_);
\r
117 frame2->process_image(image_mixer_);
\r
121 auto frame1 = make_safe<draw_frame>(frame);
\r
122 frame1->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);
\r
123 frame1->process_image(image_mixer_);
\r
126 image_mixer_.end_pass();
\r
128 auto audio = audio_mixer_.begin_pass();
\r
129 BOOST_FOREACH(auto& frame, frames)
\r
131 int num = format_desc_.mode == video_mode::progressive ? 1 : 2;
\r
133 auto frame1 = make_safe<draw_frame>(frame);
\r
134 frame1->get_audio_transform() = root_audio_transform_.fetch_and_tick(num)*audio_transforms_[frame->get_layer_index()].fetch_and_tick(num);
\r
135 frame1->process_audio(audio_mixer_);
\r
137 audio_mixer_.end_pass();
\r
139 graph_->update("frame-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));
\r
141 output_(make_safe<const read_frame>(std::move(image.get()), std::move(audio)));
\r
143 graph_->update("tick-time", static_cast<float>(wait_perf_timer_.elapsed()/format_desc_.interval*0.5));
\r
144 wait_perf_timer_.reset();
\r
146 graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));
\r
148 graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));
\r
151 safe_ptr<write_frame> create_frame(const pixel_format_desc& desc)
\r
153 return make_safe<write_frame>(desc, image_mixer_.create_buffers(desc));
\r
156 void set_image_transform(const image_transform& transform, int mix_duration)
\r
158 executor_.invoke([&]
\r
160 auto src = root_image_transform_.fetch();
\r
161 auto dst = transform;
\r
162 root_image_transform_ = basic_animated_value<image_transform>(src, dst, mix_duration);
\r
166 void set_audio_transform(const audio_transform& transform, int mix_duration)
\r
168 executor_.invoke([&]
\r
170 auto src = root_audio_transform_.fetch();
\r
171 auto dst = transform;
\r
172 root_audio_transform_ = basic_animated_value<audio_transform>(src, dst, mix_duration);
\r
176 void set_image_transform(int index, const image_transform& transform, int mix_duration)
\r
178 executor_.invoke([&]
\r
180 auto src = image_transforms_[index].fetch();
\r
181 auto dst = transform;
\r
182 image_transforms_[index] = basic_animated_value<image_transform>(src, dst, mix_duration);
\r
186 void set_audio_transform(int index, const audio_transform& transform, int mix_duration)
\r
188 executor_.invoke([&]
\r
190 auto src = audio_transforms_[index].fetch();
\r
191 auto dst = transform;
\r
192 audio_transforms_[index] = basic_animated_value<audio_transform>(src, dst, mix_duration);
\r
196 void apply_image_transform(const std::function<image_transform(const image_transform&)>& transform, int mix_duration)
\r
198 return executor_.invoke([&]
\r
200 auto src = root_image_transform_.fetch();
\r
201 auto dst = transform(src);
\r
202 root_image_transform_ = basic_animated_value<image_transform>(src, dst, mix_duration);
\r
206 void apply_audio_transform(const std::function<audio_transform(audio_transform)>& transform, int mix_duration)
\r
208 return executor_.invoke([&]
\r
210 auto src = root_audio_transform_.fetch();
\r
211 auto dst = transform(src);
\r
212 root_audio_transform_ = basic_animated_value<audio_transform>(src, dst, mix_duration);
\r
216 void apply_image_transform(int index, const std::function<image_transform(image_transform)>& transform, int mix_duration)
\r
218 executor_.invoke([&]
\r
220 auto src = image_transforms_[index].fetch();
\r
221 auto dst = transform(src);
\r
222 image_transforms_[index] = basic_animated_value<image_transform>(src, dst, mix_duration);
\r
226 void apply_audio_transform(int index, const std::function<audio_transform(audio_transform)>& transform, int mix_duration)
\r
228 executor_.invoke([&]
\r
230 auto src = audio_transforms_[index].fetch();
\r
231 auto dst = transform(src);
\r
232 audio_transforms_[index] = basic_animated_value<audio_transform>(src, dst, mix_duration);
\r
236 std::wstring print() const
\r
238 return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"mixer";
\r
242 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
243 frame_mixer_device::frame_mixer_device(frame_mixer_device&& other) : impl_(std::move(other.impl_)){}
\r
244 void frame_mixer_device::send(const std::vector<safe_ptr<draw_frame>>& frames){impl_->send(frames);}
\r
245 const video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->format_desc_; }
\r
246 safe_ptr<write_frame> frame_mixer_device::create_frame(const pixel_format_desc& desc){ return impl_->create_frame(desc); }
\r
247 safe_ptr<write_frame> frame_mixer_device::create_frame(size_t width, size_t height, pixel_format::type pix_fmt)
\r
249 // Create bgra frame
\r
250 pixel_format_desc desc;
\r
251 desc.pix_fmt = pix_fmt;
\r
252 desc.planes.push_back(pixel_format_desc::plane(width, height, 4));
\r
253 return create_frame(desc);
\r
256 safe_ptr<write_frame> frame_mixer_device::create_frame(pixel_format::type pix_fmt)
\r
258 // Create bgra frame with output resolution
\r
259 pixel_format_desc desc;
\r
260 desc.pix_fmt = pix_fmt;
\r
261 desc.planes.push_back(pixel_format_desc::plane(get_video_format_desc().width, get_video_format_desc().height, 4));
\r
262 return create_frame(desc);
\r
264 void frame_mixer_device::set_image_transform(const image_transform& transform, int mix_duration){impl_->set_image_transform(transform, mix_duration);}
\r
265 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
266 void frame_mixer_device::set_audio_transform(const audio_transform& transform, int mix_duration){impl_->set_audio_transform(transform, mix_duration);}
\r
267 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
268 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
269 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
270 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
271 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