]> git.sesse.net Git - casparcg/blob - core/channel.cpp
2.0.0.2:
[casparcg] / core / channel.cpp
1 #include "StdAfx.h"\r
2 \r
3 #include "channel.h"\r
4 \r
5 #include "producer/layer.h"\r
6 \r
7 #include "consumer/frame_consumer_device.h"\r
8 \r
9 #include "processor/draw_frame.h"\r
10 #include "processor/frame_processor_device.h"\r
11 \r
12 #include <common/concurrency/executor.h>\r
13 \r
14 #include <boost/range/algorithm_ext/erase.hpp>\r
15 \r
16 #include <tbb/parallel_for.h>\r
17 \r
18 #include <memory>\r
19 \r
20 namespace caspar { namespace core {\r
21 \r
22 class clock\r
23 {\r
24 public:\r
25         clock(int fps = 25) : fps_(fps)\r
26         {\r
27                 QueryPerformanceFrequency(&freq_);\r
28                 time_.QuadPart = 0;\r
29         }\r
30                 \r
31         // Author: Ryan M. Geiss\r
32         // http://www.geisswerks.com/ryan/FAQS/timing.html\r
33         void wait()\r
34         {       \r
35                 LARGE_INTEGER t;\r
36                 QueryPerformanceCounter(&t);\r
37 \r
38                 if (time_.QuadPart != 0)\r
39                 {\r
40                         int ticks_to_wait = static_cast<int>(freq_.QuadPart / fps_);\r
41                         int done = 0;\r
42                         do\r
43                         {\r
44                                 QueryPerformanceCounter(&t);\r
45                                 \r
46                                 int ticks_passed = static_cast<int>(static_cast<__int64>(t.QuadPart) - static_cast<__int64>(time_.QuadPart));\r
47                                 int ticks_left = ticks_to_wait - ticks_passed;\r
48 \r
49                                 if (t.QuadPart < time_.QuadPart)    // time wrap\r
50                                         done = 1;\r
51                                 if (ticks_passed >= ticks_to_wait)\r
52                                         done = 1;\r
53                                 \r
54                                 if (!done)\r
55                                 {\r
56                                         // if > 0.002s left, do Sleep(1), which will actually sleep some \r
57                                         //   steady amount, probably 1-2 ms,\r
58                                         //   and do so in a nice way (cpu meter drops; laptop battery spared).\r
59                                         // otherwise, do a few Sleep(0)'s, which just give up the timeslice,\r
60                                         //   but don't really save cpu or battery, but do pass a tiny\r
61                                         //   amount of time.\r
62                                         if (ticks_left > static_cast<int>((freq_.QuadPart*2)/1000))\r
63                                                 Sleep(1);\r
64                                         else                        \r
65                                                 for (int i = 0; i < 10; ++i) \r
66                                                         Sleep(0);  // causes thread to give up its timeslice\r
67                                 }\r
68                         }\r
69                         while (!done);            \r
70                 }\r
71 \r
72                 time_ = t;\r
73         }\r
74 private:\r
75         LARGE_INTEGER freq_;\r
76         LARGE_INTEGER time_;\r
77         int fps_;\r
78 };\r
79 \r
80 struct channel::implementation : boost::noncopyable\r
81 {       \r
82         implementation(const video_format_desc& format_desc, const std::vector<safe_ptr<frame_consumer>>& consumers)  \r
83                 : format_desc_(format_desc), processor_device_(frame_processor_device(format_desc)), consumer_device_(format_desc, consumers)\r
84         {\r
85                 executor_.start();\r
86                 executor_.begin_invoke([=]{tick();});\r
87         }\r
88                                         \r
89         void tick()\r
90         {               \r
91                 auto drawed_frame = draw();\r
92                 auto processed_frame = processor_device_->process(std::move(drawed_frame));\r
93                 consumer_device_.consume(std::move(processed_frame));\r
94 \r
95                 executor_.begin_invoke([=]{tick();});\r
96 \r
97                 clock_.wait();\r
98         }\r
99         \r
100         safe_ptr<draw_frame> draw()\r
101         {       \r
102                 std::vector<safe_ptr<draw_frame>> frames(layers_.size(), draw_frame::empty());\r
103                 tbb::parallel_for(tbb::blocked_range<size_t>(0, frames.size()), \r
104                 [&](const tbb::blocked_range<size_t>& r)\r
105                 {\r
106                         auto it = layers_.begin();\r
107                         std::advance(it, r.begin());\r
108                         for(size_t i = r.begin(); i != r.end(); ++i, ++it)\r
109                                 frames[i] = it->second.receive();\r
110                 });             \r
111                 boost::range::remove_erase(frames, draw_frame::eof());\r
112                 boost::range::remove_erase(frames, draw_frame::empty());\r
113                 return draw_frame(frames);\r
114         }\r
115 \r
116         void load(int index, const safe_ptr<frame_producer>& producer, bool autoplay)\r
117         {\r
118                 producer->initialize(processor_device_);\r
119                 executor_.begin_invoke([=]\r
120                 {\r
121                         auto it = layers_.insert(std::make_pair(index, layer(index))).first;\r
122                         it->second.load(producer, autoplay);\r
123                 });\r
124         }\r
125                         \r
126         void preview(int index, const safe_ptr<frame_producer>& producer)\r
127         {\r
128                 producer->initialize(processor_device_);\r
129                 executor_.begin_invoke([=]\r
130                 {\r
131                         auto it = layers_.insert(std::make_pair(index, layer(index))).first;\r
132                         it->second.preview(producer);\r
133                 });\r
134         }\r
135 \r
136         void pause(int index)\r
137         {               \r
138                 executor_.begin_invoke([=]\r
139                 {                       \r
140                         auto it = layers_.find(index);\r
141                         if(it != layers_.end())\r
142                                 it->second.pause();             \r
143                 });\r
144         }\r
145 \r
146         void play(int index)\r
147         {               \r
148                 executor_.begin_invoke([=]\r
149                 {\r
150                         auto it = layers_.find(index);\r
151                         if(it != layers_.end())\r
152                                 it->second.play();              \r
153                 });\r
154         }\r
155 \r
156         void stop(int index)\r
157         {               \r
158                 executor_.begin_invoke([=]\r
159                 {\r
160                         auto it = layers_.find(index);\r
161                         if(it != layers_.end())                 \r
162                         {\r
163                                 it->second.stop();      \r
164                                 if(it->second.empty())\r
165                                         layers_.erase(it);\r
166                         }\r
167                 });\r
168         }\r
169 \r
170         void clear(int index)\r
171         {\r
172                 executor_.begin_invoke([=]\r
173                 {                       \r
174                         auto it = layers_.find(index);\r
175                         if(it != layers_.end())\r
176                         {\r
177                                 it->second.clear();             \r
178                                 layers_.erase(it);\r
179                         }\r
180                 });\r
181         }\r
182                 \r
183         void clear()\r
184         {\r
185                 executor_.begin_invoke([=]\r
186                 {                       \r
187                         layers_.clear();\r
188                 });\r
189         }               \r
190 \r
191         boost::unique_future<safe_ptr<frame_producer>> foreground(int index) const\r
192         {\r
193                 return executor_.begin_invoke([=]() -> safe_ptr<frame_producer>\r
194                 {                       \r
195                         auto it = layers_.find(index);\r
196                         return it != layers_.end() ? it->second.foreground() : frame_producer::empty();\r
197                 });\r
198         }\r
199         \r
200         boost::unique_future<safe_ptr<frame_producer>> background(int index) const\r
201         {\r
202                 return executor_.begin_invoke([=]() -> safe_ptr<frame_producer>\r
203                 {\r
204                         auto it = layers_.find(index);\r
205                         return it != layers_.end() ? it->second.background() : frame_producer::empty();\r
206                 });\r
207         }\r
208 \r
209         clock clock_;\r
210 \r
211         mutable executor executor_;\r
212                                 \r
213         safe_ptr<frame_processor_device> processor_device_;\r
214         frame_consumer_device consumer_device_;\r
215                                                 \r
216         std::map<int, layer> layers_;           \r
217 \r
218         const video_format_desc format_desc_;\r
219 };\r
220 \r
221 channel::channel(channel&& other) : impl_(std::move(other.impl_)){}\r
222 channel::channel(const video_format_desc& format_desc, const std::vector<safe_ptr<frame_consumer>>& consumers)\r
223         : impl_(new implementation(format_desc, consumers)){}\r
224 void channel::load(int index, const safe_ptr<frame_producer>& producer, bool autoplay){impl_->load(index, producer, autoplay);}\r
225 void channel::preview(int index, const safe_ptr<frame_producer>& producer){impl_->preview(index, producer);}\r
226 void channel::pause(int index){impl_->pause(index);}\r
227 void channel::play(int index){impl_->play(index);}\r
228 void channel::stop(int index){impl_->stop(index);}\r
229 void channel::clear(int index){impl_->clear(index);}\r
230 void channel::clear(){impl_->clear();}\r
231 boost::unique_future<safe_ptr<frame_producer>> channel::foreground(int index) const{    return impl_->foreground(index);}\r
232 boost::unique_future<safe_ptr<frame_producer>> channel::background(int index) const{return impl_->background(index);}\r
233 const video_format_desc& channel::get_video_format_desc() const{        return impl_->format_desc_;}\r
234 \r
235 }}