]> git.sesse.net Git - casparcg/blob - core/consumer/frame_consumer_device.cpp
b32b3a771e2f6e6ec2ff82e2aea4935c773e343f
[casparcg] / core / consumer / frame_consumer_device.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */\r
20 #include "../StdAfx.h"\r
21 \r
22 #ifdef _MSC_VER\r
23 #pragma warning (disable : 4244)\r
24 #endif\r
25 \r
26 #include "frame_consumer_device.h"\r
27 \r
28 #include "../video_format.h"\r
29 #include "../mixer/gpu/ogl_device.h"\r
30 #include "../mixer/read_frame.h"\r
31 \r
32 #include <common/concurrency/executor.h>\r
33 #include <common/diagnostics/graph.h>\r
34 #include <common/utility/assert.h>\r
35 #include <common/memory/memshfl.h>\r
36 \r
37 #include <boost/range/algorithm_ext/erase.hpp>\r
38 #include <boost/range/algorithm.hpp>\r
39 #include <boost/circular_buffer.hpp>\r
40 #include <boost/timer.hpp>\r
41 #include <boost/range/algorithm.hpp>\r
42 \r
43 namespace caspar { namespace core {\r
44         \r
45 struct frame_consumer_device::implementation\r
46 {       \r
47         boost::circular_buffer<std::pair<safe_ptr<const read_frame>,safe_ptr<const read_frame>>> buffer_;\r
48 \r
49         std::map<int, std::shared_ptr<frame_consumer>> consumers_; // Valid iterators after erase\r
50         \r
51         safe_ptr<diagnostics::graph> diag_;\r
52 \r
53         video_format_desc format_desc_;\r
54 \r
55         boost::timer frame_timer_;\r
56         boost::timer tick_timer_;\r
57         \r
58         executor executor_;     \r
59 public:\r
60         implementation( const video_format_desc& format_desc) \r
61                 : format_desc_(format_desc)\r
62                 , diag_(diagnostics::create_graph(std::string("frame_consumer_device")))\r
63                 , executor_(L"frame_consumer_device", true)\r
64         {               \r
65                 diag_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
66                 diag_->add_guide("frame-time", 0.5f);   \r
67                 diag_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
68                 diag_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
69 \r
70                 executor_.set_capacity(1);\r
71                 executor_.begin_invoke([]\r
72                 {\r
73                         SetThreadPriority(GetCurrentThread(), ABOVE_NORMAL_PRIORITY_CLASS);\r
74                 });\r
75         }\r
76 \r
77         void add(int index, safe_ptr<frame_consumer>&& consumer)\r
78         {               \r
79                 consumer->initialize(format_desc_);\r
80                 executor_.invoke([&]\r
81                 {\r
82                         if(buffer_.capacity() < consumer->buffer_depth())\r
83                                 buffer_.set_capacity(consumer->buffer_depth());\r
84                         consumers_[index] = std::move(consumer);\r
85                 });\r
86         }\r
87 \r
88         void remove(int index)\r
89         {\r
90                 executor_.invoke([&]\r
91                 {\r
92                         auto it = consumers_.find(index);\r
93                         if(it != consumers_.end())\r
94                         {\r
95                                 CASPAR_LOG(info) << print() << L" " << it->second->print() << L" Removed.";\r
96                                 consumers_.erase(it);\r
97                         }\r
98                 });\r
99         }\r
100                                 \r
101         void send(const safe_ptr<const read_frame>& frame)\r
102         {               \r
103                 executor_.begin_invoke([=]\r
104                 {\r
105                         diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
106                         frame_timer_.restart();\r
107                         \r
108                         auto key_frame = read_frame::empty();\r
109 \r
110                         if(boost::range::find_if(consumers_, [](const decltype(*consumers_.begin())& p){return p.second->key_only();}) != consumers_.end())\r
111                         {\r
112                                 // Currently do key_only transform on cpu. Unsure if the extra 400MB/s (1080p50) overhead is worth it to do it on gpu.\r
113                                 auto key_data = ogl_device::create_host_buffer(frame->image_data().size(), host_buffer::write_only);                            \r
114                                 fast_memsfhl(key_data->data(), frame->image_data().begin(), frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
115                                 std::vector<short> audio_data(frame->audio_data().begin(), frame->audio_data().end());\r
116                                 key_frame = make_safe<const read_frame>(std::move(key_data), std::move(audio_data));\r
117                         }\r
118 \r
119                         buffer_.push_back(std::make_pair(std::move(frame), std::move(key_frame)));\r
120 \r
121                         if(!buffer_.full())\r
122                                 return;\r
123 \r
124         \r
125                         auto it = consumers_.begin();\r
126                         while(it != consumers_.end())\r
127                         {\r
128                                 try\r
129                                 {\r
130                                         auto p = buffer_[it->second->buffer_depth()-1];\r
131                                         it->second->send(it->second->key_only() ? p.second : p.first);\r
132                                         ++it;\r
133                                 }\r
134                                 catch(...)\r
135                                 {\r
136                                         CASPAR_LOG_CURRENT_EXCEPTION();\r
137                                         consumers_.erase(it++);\r
138                                         CASPAR_LOG(error) << print() << L" " << it->second->print() << L" Removed.";\r
139                                 }\r
140                         }\r
141                         diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
142                         \r
143                         diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
144                         tick_timer_.restart();\r
145                 });\r
146                 diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
147         }\r
148 \r
149         std::wstring print() const\r
150         {\r
151                 return L"frame_consumer_device";\r
152         }\r
153         \r
154         void set_video_format_desc(const video_format_desc& format_desc)\r
155         {\r
156                 executor_.invoke([&]\r
157                 {\r
158                         format_desc_ = format_desc;\r
159                         buffer_.clear();\r
160                         \r
161                         auto it = consumers_.begin();\r
162                         while(it != consumers_.end())\r
163                         {\r
164                                 try\r
165                                 {\r
166                                         it->second->initialize(format_desc_);\r
167                                         ++it;\r
168                                 }\r
169                                 catch(...)\r
170                                 {\r
171                                         CASPAR_LOG_CURRENT_EXCEPTION();\r
172                                         consumers_.erase(it++);\r
173                                         CASPAR_LOG(error) << print() << L" " << it->second->print() << L" Removed.";\r
174                                 }\r
175                         }\r
176                 });\r
177         }\r
178 };\r
179 \r
180 frame_consumer_device::frame_consumer_device(const video_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
181 void frame_consumer_device::add(int index, safe_ptr<frame_consumer>&& consumer){impl_->add(index, std::move(consumer));}\r
182 void frame_consumer_device::remove(int index){impl_->remove(index);}\r
183 void frame_consumer_device::send(const safe_ptr<const read_frame>& future_frame) { impl_->send(future_frame); }\r
184 void frame_consumer_device::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
185 }}