]> git.sesse.net Git - casparcg/blob - core/renderer/display_device.cpp
a165624250e1ab0b1c4e224a54237f90ddf47a97
[casparcg] / core / renderer / display_device.cpp
1 #include "../StdAfx.h"\r
2 \r
3 #ifdef _MSC_VER\r
4 #pragma warning (disable : 4244)\r
5 #endif\r
6 \r
7 #include "display_device.h"\r
8 \r
9 #include "../frame/frame_format.h"\r
10 #include "../frame/gpu_frame.h"\r
11 \r
12 #include <tbb/concurrent_queue.h>\r
13 #include <tbb/atomic.h>\r
14 \r
15 #include <boost/foreach.hpp>\r
16 #include <boost/thread.hpp>\r
17 \r
18 #include <boost/date_time/posix_time/posix_time.hpp>\r
19 \r
20 #include <boost/range/algorithm_ext/erase.hpp>\r
21 \r
22 namespace caspar { namespace core { namespace renderer {\r
23 \r
24 class video_sync_clock\r
25 {\r
26 public:\r
27         video_sync_clock(const frame_format_desc& format_desc)\r
28         {\r
29                 period_ = static_cast<long>(get_frame_format_period(format_desc)*1000000.0);\r
30                 time_ = boost::posix_time::microsec_clock::local_time();\r
31         }\r
32 \r
33         void synchronize()\r
34         {\r
35                 auto remaining = boost::posix_time::microseconds(period_) - \r
36                                                 (boost::posix_time::microsec_clock::local_time() - time_);\r
37                 if(remaining > boost::posix_time::microseconds(5000))\r
38                         boost::this_thread::sleep(remaining - \r
39                                                                                 boost::posix_time::microseconds(5000));\r
40                 time_ = boost::posix_time::microsec_clock::local_time();\r
41         }\r
42 private:\r
43         boost::posix_time::ptime time_;\r
44         long period_;\r
45 };\r
46 \r
47 struct display_device::implementation\r
48 {\r
49 public:\r
50         implementation(const frame_format_desc& format_desc, \r
51                                         const std::vector<frame_consumer_ptr>& consumers) \r
52                 : consumers_(consumers), fmt_(format_desc)\r
53         {\r
54                 if(consumers.empty())\r
55                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
56                                 << msg_info("display_device requires atleast one consumer"));\r
57 \r
58                 if(std::any_of(consumers.begin(), consumers.end(), \r
59                         [&](const frame_consumer_ptr& pConsumer)\r
60                         { return pConsumer->get_frame_format_desc() != format_desc;}))\r
61                 {\r
62                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
63                                 << msg_info("All consumers must have same frameformat as display_device."));\r
64                 }\r
65 \r
66                 needs_clock_ = !std::any_of(consumers.begin(), consumers.end(), \r
67                                                                         std::mem_fn(&frame_consumer::has_sync_clock));\r
68                 frame_buffer_.set_capacity(3);\r
69                 is_running_ = true;\r
70                 display_thread_ = boost::thread([=]{run();});\r
71         }\r
72 \r
73         ~implementation()\r
74         {\r
75                 is_running_ = false;\r
76                 frame_buffer_.clear();\r
77                 display_thread_.join();\r
78         }\r
79 \r
80         void display(const gpu_frame_ptr& frame)\r
81         {\r
82                 if(is_running_)\r
83                         frame_buffer_.push(frame);\r
84         }\r
85                         \r
86         void run()\r
87         {\r
88                 CASPAR_LOG(info) << L"Started display_device thread";\r
89                 win32_exception::install_handler();\r
90                                 \r
91                 video_sync_clock clock(fmt_);\r
92                                 \r
93                 while(is_running_)\r
94                 {\r
95                         if(needs_clock_)\r
96                                 clock.synchronize();\r
97                         \r
98                         gpu_frame_ptr frame;\r
99                         if(!frame_buffer_.try_pop(frame))\r
100                         {\r
101                                 CASPAR_LOG(trace) << "Display Buffer Underrun";\r
102                                 frame_buffer_.pop(frame);\r
103                         }\r
104                         if(frame != nullptr)                    \r
105                                 display_frame(frame);                   \r
106                 }\r
107                 \r
108                 CASPAR_LOG(info) << L"Ended display_device thread";\r
109         }\r
110 \r
111         void display_frame(const gpu_frame_ptr& frame)\r
112         {\r
113                 BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)\r
114                 {\r
115                         try\r
116                         {\r
117                                 consumer->prepare(frame);\r
118                                 prepared_frames_.push_back(frame);\r
119 \r
120                                 if(prepared_frames_.size() > 2)\r
121                                 {\r
122                                         consumer->display(prepared_frames_.front());\r
123                                         prepared_frames_.pop_front();\r
124                                 }\r
125                         }\r
126                         catch(...)\r
127                         {\r
128                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
129                                 boost::range::remove_erase(consumers_, consumer);\r
130                                 CASPAR_LOG(warning) << "Removed consumer from render-device.";\r
131                                 if(consumers_.empty())\r
132                                 {\r
133                                         CASPAR_LOG(warning) \r
134                                                 << "No consumers available. Shutting down display-device.";\r
135                                         is_running_ = false;\r
136                                 }\r
137                         }\r
138                 }\r
139         }\r
140 \r
141         std::deque<gpu_frame_ptr> prepared_frames_;\r
142                 \r
143         boost::thread display_thread_;\r
144 \r
145         tbb::atomic<bool> is_running_;\r
146         tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
147 \r
148         bool needs_clock_;\r
149         std::vector<frame_consumer_ptr> consumers_;\r
150 \r
151         frame_format_desc fmt_;\r
152 };\r
153 \r
154 display_device::display_device(const frame_format_desc& format_desc, const std::vector<frame_consumer_ptr>& consumers) : impl_(new implementation(format_desc, consumers)){}\r
155 void display_device::display(const gpu_frame_ptr& frame){impl_->display(frame);}\r
156 }}}