]> git.sesse.net Git - casparcg/blob - mixer/frame_mixer_device.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / mixer / frame_mixer_device.cpp
1 #include "StdAfx.h"\r
2 \r
3 #include "frame_mixer_device.h"\r
4 \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
11 \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
18 \r
19 #include <core/video_format.h>\r
20 \r
21 #include <tbb/concurrent_queue.h>\r
22 #include <tbb/concurrent_unordered_map.h>\r
23 \r
24 #include <boost/range/algorithm.hpp>\r
25 \r
26 #include <unordered_map>\r
27 \r
28 namespace caspar { namespace core {\r
29         \r
30         \r
31 template<typename T>\r
32 class basic_animated_value\r
33 {\r
34         T source_;\r
35         T dest_;\r
36         int duration_;\r
37         int time_;\r
38 public: \r
39         basic_animated_value()\r
40                 : duration_(0)\r
41                 , time_(0){}\r
42         basic_animated_value(const T& source, const T& dest, int duration)\r
43                 : source_(source)\r
44                 , dest_(dest)\r
45                 , duration_(duration)\r
46                 , time_(0){}\r
47         \r
48         virtual T fetch()\r
49         {\r
50                 return lerp(source_, dest_, duration_ < 1 ? 1.0f : static_cast<float>(time_)/static_cast<float>(duration_));\r
51         }\r
52         virtual T fetch_and_tick(int num)\r
53         {                                               \r
54                 time_ = std::min(time_+num, duration_);\r
55                 return fetch();\r
56         }\r
57 };\r
58 \r
59 struct frame_mixer_device::implementation : boost::noncopyable\r
60 {               \r
61         const printer                   parent_printer_;\r
62         const video_format_desc format_desc_;\r
63 \r
64         safe_ptr<diagnostics::graph> graph_;\r
65         timer perf_timer_;\r
66         timer wait_perf_timer_;\r
67 \r
68         audio_mixer     audio_mixer_;\r
69         image_mixer image_mixer_;\r
70 \r
71         output_func output_;\r
72 \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
75 \r
76         basic_animated_value<image_transform> root_image_transform_;\r
77         basic_animated_value<audio_transform> root_audio_transform_;\r
78 \r
79         executor executor_;\r
80 public:\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
86                 , output_(output)\r
87                 , executor_(print())\r
88         {\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
93                 executor_.start();\r
94                 executor_.set_capacity(2);\r
95                 CASPAR_LOG(info) << print() << L" Successfully initialized.";   \r
96         }\r
97                 \r
98         void send(const std::vector<safe_ptr<draw_frame>>& frames)\r
99         {                       \r
100                 executor_.begin_invoke([=]\r
101                 {\r
102                         perf_timer_.reset();\r
103                         auto image = image_mixer_.begin_pass();\r
104                         BOOST_FOREACH(auto& frame, frames)\r
105                         {\r
106                                 if(format_desc_.mode != video_mode::progressive)\r
107                                 {\r
108                                         auto frame1 = make_safe<draw_frame>(frame);\r
109                                         auto frame2 = make_safe<draw_frame>(frame);\r
110 \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
113 \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
116                                         else\r
117                                                 frame2->process_image(image_mixer_);\r
118                                 }\r
119                                 else\r
120                                 {\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
124                                 }\r
125                         }\r
126                         image_mixer_.end_pass();\r
127 \r
128                         auto audio = audio_mixer_.begin_pass();\r
129                         BOOST_FOREACH(auto& frame, frames)\r
130                         {\r
131                                 int num = format_desc_.mode == video_mode::progressive ? 1 : 2;\r
132 \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
136                         }\r
137                         audio_mixer_.end_pass();\r
138 \r
139                         graph_->update("frame-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));\r
140 \r
141                         output_(make_safe<const read_frame>(std::move(image.get()), std::move(audio)));\r
142 \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
145 \r
146                         graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
147                 });\r
148                 graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
149         }\r
150                 \r
151         safe_ptr<write_frame> create_frame(const pixel_format_desc& desc)\r
152         {\r
153                 return make_safe<write_frame>(desc, image_mixer_.create_buffers(desc));\r
154         }\r
155                                 \r
156         void set_image_transform(const image_transform& transform, int mix_duration)\r
157         {\r
158                 executor_.invoke([&]\r
159                 {\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
163                 });\r
164         }\r
165 \r
166         void set_audio_transform(const audio_transform& transform, int mix_duration)\r
167         {\r
168                 executor_.invoke([&]\r
169                 {\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
173                 });\r
174         }\r
175 \r
176         void set_image_transform(int index, const image_transform& transform, int mix_duration)\r
177         {\r
178                 executor_.invoke([&]\r
179                 {\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
183                 });\r
184         }\r
185 \r
186         void set_audio_transform(int index, const audio_transform& transform, int mix_duration)\r
187         {\r
188                 executor_.invoke([&]\r
189                 {\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
193                 });\r
194         }\r
195         \r
196         void apply_image_transform(const std::function<image_transform(const image_transform&)>& transform, int mix_duration)\r
197         {\r
198                 return executor_.invoke([&]\r
199                 {\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
203                 });\r
204         }\r
205 \r
206         void apply_audio_transform(const std::function<audio_transform(audio_transform)>& transform, int mix_duration)\r
207         {\r
208                 return executor_.invoke([&]\r
209                 {\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
213                 });\r
214         }\r
215 \r
216         void apply_image_transform(int index, const std::function<image_transform(image_transform)>& transform, int mix_duration)\r
217         {\r
218                 executor_.invoke([&]\r
219                 {\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
223                 });\r
224         }\r
225 \r
226         void apply_audio_transform(int index, const std::function<audio_transform(audio_transform)>& transform, int mix_duration)\r
227         {\r
228                 executor_.invoke([&]\r
229                 {\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
233                 });\r
234         }\r
235 \r
236         std::wstring print() const\r
237         {\r
238                 return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"mixer";\r
239         }\r
240 };\r
241         \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
248 {\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
254 }\r
255                         \r
256 safe_ptr<write_frame> frame_mixer_device::create_frame(pixel_format::type pix_fmt)\r
257 {\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
263 }\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
272 \r
273 }}