]> git.sesse.net Git - casparcg/blob - core/renderer/render_device.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / core / renderer / render_device.cpp
1 #include "..\StdAfx.h"\r
2 \r
3 #include "render_device.h"\r
4 #include "layer.h"\r
5 \r
6 #include "../protocol/monitor/Monitor.h"\r
7 #include "../consumer/frame_consumer.h"\r
8 \r
9 #include "../frame/system_frame.h"\r
10 #include "../frame/frame_format.h"\r
11 \r
12 #include "../../common/utility/scope_exit.h"\r
13 #include "../../common/image/image.h"\r
14 \r
15 #include <numeric>\r
16 \r
17 #include <boost/filesystem.hpp>\r
18 #include <boost/thread.hpp>\r
19 #include <boost/date_time/posix_time/posix_time.hpp>\r
20 #include <boost/range/algorithm.hpp>\r
21 #include <boost/range/algorithm_ext/erase.hpp>\r
22 #include <boost/range/sub_range.hpp>\r
23 #include <boost/range/adaptor/indirected.hpp>\r
24 #include <boost/foreach.hpp>\r
25 \r
26 #include <tbb/parallel_for.h>\r
27 #include <tbb/mutex.h>\r
28 \r
29 using namespace boost::assign;\r
30         \r
31 namespace caspar{ namespace renderer{\r
32         \r
33 std::vector<frame_ptr> render_frames(std::map<int, layer>& layers)\r
34 {       \r
35         std::vector<frame_ptr> frames(layers.size(), nullptr);\r
36         tbb::parallel_for(tbb::blocked_range<size_t>(0, frames.size()), [&](const tbb::blocked_range<size_t>& r)\r
37         {\r
38                 auto it = layers.begin();\r
39                 std::advance(it, r.begin());\r
40                 for(size_t i = r.begin(); i != r.end(); ++i, ++it)\r
41                         frames[i] = it->second.get_frame();\r
42         });                                     \r
43         boost::range::remove_erase(frames, nullptr);\r
44         boost::range::remove_erase_if(frames, [](const frame_const_ptr& frame) { return *frame == *frame::null();});\r
45         return frames;\r
46 }\r
47 \r
48 struct render_device::implementation : boost::noncopyable\r
49 {       \r
50         implementation(const caspar::frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers)  \r
51                 : consumers_(consumers), monitor_(index), fmt_(format_desc)\r
52         {       \r
53                 is_running_ = true;\r
54                 if(consumers.empty())\r
55                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
56                                                                                                                 << msg_info("render_device requires atleast one consumer"));\r
57 \r
58                 if(std::any_of(consumers.begin(), consumers.end(), [&](const frame_consumer_ptr& pConsumer){ return pConsumer->get_frame_format_desc() != format_desc;}))\r
59                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
60                                                                                                                 << msg_info("All consumers must have same frameformat as renderdevice."));\r
61                 \r
62                 frame_buffer_.set_capacity(3);\r
63                 display_thread_ = boost::thread([=]{display();});\r
64                 render_thread_ = boost::thread([=]{render();});\r
65 \r
66                 CASPAR_LOG(info) << L"Initialized render_device with " << format_desc;\r
67         }\r
68                         \r
69         ~implementation()\r
70         {\r
71                 is_running_ = false;\r
72                 frame_buffer_.clear();\r
73                 frame_buffer_.push(nullptr);\r
74                 render_thread_.join();\r
75                 display_thread_.join();\r
76         }\r
77                 \r
78         void render()\r
79         {               \r
80                 CASPAR_LOG(info) << L"Started render_device::render Thread";\r
81                 win32_exception::install_handler();\r
82                 \r
83                 std::vector<frame_ptr> current_frames;\r
84 \r
85                 while(is_running_)\r
86                 {\r
87                         try\r
88                         {       \r
89                                 std::vector<frame_ptr> next_frames;\r
90                                 frame_ptr composite_frame;              \r
91 \r
92                                 {\r
93                                         tbb::mutex::scoped_lock lock(layers_mutex_);    \r
94                                         tbb::parallel_invoke(\r
95                                                 [&]{next_frames = render_frames(layers_);}, \r
96                                                 [&]{composite_frame = compose_frames(current_frames.empty() ? std::make_shared<system_frame>(fmt_.size) : current_frames[0], current_frames);});\r
97                                 }\r
98 \r
99                                 current_frames = std::move(next_frames);                \r
100                                 frame_buffer_.push(std::move(composite_frame));\r
101                         }\r
102                         catch(...)\r
103                         {\r
104                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
105                                 layers_.clear();\r
106                                 CASPAR_LOG(error) << "Unexpected exception. Cleared layers in render-device";\r
107                         }\r
108                 }\r
109 \r
110                 CASPAR_LOG(info) << L"Ended render_device::render Thread";\r
111         }\r
112         \r
113         void display()\r
114         {\r
115                 CASPAR_LOG(info) << L"Started render_device::display Thread";\r
116                 win32_exception::install_handler();\r
117                                 \r
118                 frame_ptr frame = clear_frame(std::make_shared<system_frame>(fmt_.size));\r
119                 std::deque<frame_ptr> prepared(3, frame);\r
120                                 \r
121                 while(is_running_)\r
122                 {\r
123                         if(!frame_buffer_.try_pop(frame))\r
124                         {\r
125                                 CASPAR_LOG(trace) << "Display Buffer Underrun";\r
126                                 frame_buffer_.pop(frame);\r
127                         }\r
128                         if(frame != nullptr)\r
129                         {\r
130                                 send_frame(prepared.front(), frame);\r
131                                 prepared.push_back(frame);\r
132                                 prepared.pop_front();\r
133                         }\r
134                 }\r
135                 \r
136                 CASPAR_LOG(info) << L"Ended render_device::display Thread";\r
137         }\r
138 \r
139         void send_frame(const frame_ptr& pPreparedFrame, const frame_ptr& pNextFrame)\r
140         {\r
141                 BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)\r
142                 {\r
143                         try\r
144                         {\r
145                                 consumer->prepare(pNextFrame); // Could block\r
146                                 consumer->display(pPreparedFrame); // Could block\r
147                         }\r
148                         catch(...)\r
149                         {\r
150                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
151                                 boost::range::remove_erase(consumers_, consumer);\r
152                                 CASPAR_LOG(warning) << "Removed consumer from render-device.";\r
153                                 if(consumers_.empty())\r
154                                 {\r
155                                         CASPAR_LOG(warning) << "No consumers available. Shutting down render-device.";\r
156                                         is_running_ = false;\r
157                                 }\r
158                         }\r
159                 }\r
160         }\r
161                 \r
162         void load(int exLayer, const frame_producer_ptr& producer, load_option option)\r
163         {\r
164                 if(producer->get_frame_format_desc() != fmt_)\r
165                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("pProducer") << msg_info("Invalid frame format"));\r
166 \r
167                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
168                 layers_[exLayer].load(producer, option);\r
169         }\r
170                         \r
171         void play(int exLayer)\r
172         {               \r
173                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
174                 auto it = layers_.find(exLayer);\r
175                 if(it != layers_.end())\r
176                         it->second.play();              \r
177         }\r
178 \r
179         void stop(int exLayer)\r
180         {               \r
181                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
182                 auto it = layers_.find(exLayer);\r
183                 if(it != layers_.end())\r
184                         it->second.stop();\r
185         }\r
186 \r
187         void clear(int exLayer)\r
188         {\r
189                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
190                 auto it = layers_.find(exLayer);\r
191                 if(it != layers_.end())\r
192                         it->second.clear();             \r
193         }\r
194                 \r
195         void clear()\r
196         {\r
197                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
198                 layers_.clear();\r
199         }               \r
200 \r
201         frame_producer_ptr active(int exLayer) const\r
202         {\r
203                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
204                 auto it = layers_.find(exLayer);\r
205                 return it != layers_.end() ? it->second.active() : nullptr;\r
206         }\r
207         \r
208         frame_producer_ptr background(int exLayer) const\r
209         {\r
210                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
211                 auto it = layers_.find(exLayer);\r
212                 return it != layers_.end() ? it->second.background() : nullptr;\r
213         }\r
214                 \r
215         boost::thread render_thread_;\r
216         boost::thread display_thread_;\r
217                 \r
218         caspar::frame_format_desc fmt_;\r
219         tbb::concurrent_bounded_queue<frame_ptr> frame_buffer_;\r
220         \r
221         std::vector<frame_consumer_ptr> consumers_;\r
222         \r
223         mutable tbb::mutex layers_mutex_;\r
224         std::map<int, layer> layers_;\r
225         \r
226         tbb::atomic<bool> is_running_;  \r
227 \r
228         caspar::Monitor monitor_;\r
229 };\r
230 \r
231 render_device::render_device(const caspar::frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers) \r
232         : impl_(new implementation(format_desc, index, consumers)){}\r
233 void render_device::load(int exLayer, const frame_producer_ptr& pProducer, load_option option){impl_->load(exLayer, pProducer, option);}\r
234 void render_device::play(int exLayer){impl_->play(exLayer);}\r
235 void render_device::stop(int exLayer){impl_->stop(exLayer);}\r
236 void render_device::clear(int exLayer){impl_->clear(exLayer);}\r
237 void render_device::clear(){impl_->clear();}\r
238 frame_producer_ptr render_device::active(int exLayer) const {return impl_->active(exLayer);}\r
239 frame_producer_ptr render_device::background(int exLayer) const {return impl_->background(exLayer);}\r
240 const frame_format_desc& render_device::frame_format_desc() const{return impl_->fmt_;}\r
241 caspar::Monitor& render_device::monitor(){return impl_->monitor_;}\r
242 }}\r
243 \r