1 #include "..\StdAfx.h"
\r
3 #include "render_device.h"
\r
6 #include "../protocol/monitor/Monitor.h"
\r
7 #include "../consumer/frame_consumer.h"
\r
9 #include "../frame/system_frame.h"
\r
10 #include "../frame/frame_format.h"
\r
12 #include "../../common/utility/scope_exit.h"
\r
13 #include "../../common/image/image.h"
\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
26 #include <tbb/parallel_for.h>
\r
27 #include <tbb/mutex.h>
\r
29 using namespace boost::assign;
\r
31 namespace caspar{ namespace renderer{
\r
33 std::vector<frame_ptr> render_frames(std::map<int, layer>& layers)
\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
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
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
48 struct render_device::implementation : boost::noncopyable
\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
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
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
62 frame_buffer_.set_capacity(3);
\r
63 display_thread_ = boost::thread([=]{display();});
\r
64 render_thread_ = boost::thread([=]{render();});
\r
66 CASPAR_LOG(info) << L"Initialized render_device with " << format_desc;
\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
80 CASPAR_LOG(info) << L"Started render_device::render Thread";
\r
81 win32_exception::install_handler();
\r
83 std::vector<frame_ptr> current_frames;
\r
89 std::vector<frame_ptr> next_frames;
\r
90 frame_ptr composite_frame;
\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
99 current_frames = std::move(next_frames);
\r
100 frame_buffer_.push(std::move(composite_frame));
\r
104 CASPAR_LOG_CURRENT_EXCEPTION();
\r
106 CASPAR_LOG(error) << "Unexpected exception. Cleared layers in render-device";
\r
110 CASPAR_LOG(info) << L"Ended render_device::render Thread";
\r
115 CASPAR_LOG(info) << L"Started render_device::display Thread";
\r
116 win32_exception::install_handler();
\r
118 frame_ptr frame = clear_frame(std::make_shared<system_frame>(fmt_.size));
\r
119 std::deque<frame_ptr> prepared(3, frame);
\r
123 if(!frame_buffer_.try_pop(frame))
\r
125 CASPAR_LOG(trace) << "Display Buffer Underrun";
\r
126 frame_buffer_.pop(frame);
\r
128 if(frame != nullptr)
\r
130 send_frame(prepared.front(), frame);
\r
131 prepared.push_back(frame);
\r
132 prepared.pop_front();
\r
136 CASPAR_LOG(info) << L"Ended render_device::display Thread";
\r
139 void send_frame(const frame_ptr& pPreparedFrame, const frame_ptr& pNextFrame)
\r
141 BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)
\r
145 consumer->prepare(pNextFrame); // Could block
\r
146 consumer->display(pPreparedFrame); // Could block
\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
155 CASPAR_LOG(warning) << "No consumers available. Shutting down render-device.";
\r
156 is_running_ = false;
\r
162 void load(int exLayer, const frame_producer_ptr& producer, load_option option)
\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
167 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
168 layers_[exLayer].load(producer, option);
\r
171 void play(int exLayer)
\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
179 void stop(int exLayer)
\r
181 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
182 auto it = layers_.find(exLayer);
\r
183 if(it != layers_.end())
\r
187 void clear(int exLayer)
\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
197 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
201 frame_producer_ptr active(int exLayer) const
\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
208 frame_producer_ptr background(int exLayer) const
\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
215 boost::thread render_thread_;
\r
216 boost::thread display_thread_;
\r
218 caspar::frame_format_desc fmt_;
\r
219 tbb::concurrent_bounded_queue<frame_ptr> frame_buffer_;
\r
221 std::vector<frame_consumer_ptr> consumers_;
\r
223 mutable tbb::mutex layers_mutex_;
\r
224 std::map<int, layer> layers_;
\r
226 tbb::atomic<bool> is_running_;
\r
228 caspar::Monitor monitor_;
\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