]> git.sesse.net Git - casparcg/blob - core/consumer/ogl/ogl_frame_consumer.cpp
4fc0fc2c12d0a94424349c4f382a52cf19fed9ba
[casparcg] / core / consumer / ogl / ogl_frame_consumer.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  \r
21 #include "../../StdAfx.h"\r
22 \r
23 #if defined(_MSC_VER)\r
24 #pragma warning (disable : 4244)\r
25 #endif\r
26 \r
27 #include "ogl_frame_consumer.h"\r
28 \r
29 #include "../../frame/frame_format.h"\r
30 #include "../../frame/gpu_frame.h"\r
31 #include "../../../common/image/image.h"\r
32 \r
33 #include <boost/thread.hpp>\r
34 \r
35 #include <Glee.h>\r
36 #include <SFML/Window.hpp>\r
37 \r
38 #include <windows.h>\r
39 \r
40 namespace caspar{ namespace ogl{        \r
41 \r
42 void GL_CHECK()\r
43 {\r
44         if(glGetError() != GL_NO_ERROR)\r
45                 BOOST_THROW_EXCEPTION(ogl_error() << msg_info(boost::lexical_cast<std::string>(glGetError())));\r
46 }\r
47 \r
48 struct ogl_frame_consumer::implementation : boost::noncopyable\r
49 {       \r
50         implementation(const frame_format_desc& format_desc, unsigned int screen_index, stretch stretch, bool windowed) \r
51                 : format_desc_(format_desc), stretch_(stretch), texture_(0), pbo_index_(0), screen_width_(0), screen_height_(0), windowed_(windowed)\r
52         {\r
53                 pbos_[0] = pbos_[1] = 0;\r
54                 \r
55 #ifdef _WIN32\r
56                 DISPLAY_DEVICE dDevice;                 \r
57                 memset(&dDevice,0,sizeof(dDevice));\r
58                 dDevice.cb = sizeof(dDevice);\r
59 \r
60                 std::vector<DISPLAY_DEVICE> displayDevices;\r
61                 for(int n = 0; EnumDisplayDevices(NULL, n, &dDevice, NULL); ++n)\r
62                 {\r
63                         displayDevices.push_back(dDevice);\r
64                         memset(&dDevice,0,sizeof(dDevice));\r
65                         dDevice.cb = sizeof(dDevice);\r
66                 }\r
67 \r
68                 if(screen_index >= displayDevices.size())\r
69                         BOOST_THROW_EXCEPTION(out_of_range() << arg_name_info("screen_index_"));\r
70                 \r
71                 DEVMODE devmode;\r
72                 memset(&devmode,0,sizeof(devmode));\r
73                 \r
74                 if(!EnumDisplaySettings(displayDevices[screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode))\r
75                         BOOST_THROW_EXCEPTION(invalid_operation() << arg_name_info("screen_index") << msg_info("EnumDisplaySettings"));\r
76                 \r
77                 screen_width_ = windowed ? format_desc_.width : devmode.dmPelsWidth;\r
78                 screen_height_ = windowed ? format_desc_.height : devmode.dmPelsHeight;\r
79                 screenX_ = devmode.dmPosition.x;\r
80                 screenY_ = devmode.dmPosition.y;\r
81 #else\r
82                 if(!windowed)\r
83                         BOOST_THROW_EXCEPTION(not_supported() << msg_info("OGLConsumer doesn't support non-Win32 fullscreen"));\r
84 \r
85                 if(screen_index != 0)\r
86                         CASPAR_LOG(warning) << "OGLConsumer only supports screen_index=0 for non-Win32";\r
87                 \r
88                 screen_width_ = format_desc_.width;\r
89                 screen_height_ = format_desc_.height;\r
90                 screenX_ = 0;\r
91                 screenY_ = 0;\r
92 #endif\r
93                 frame_buffer_.set_capacity(1);\r
94                 thread_ = boost::thread([=]{run();});\r
95         }\r
96         \r
97         ~implementation()\r
98         {\r
99                 frame_buffer_.push(nullptr),\r
100                 thread_.join();\r
101 \r
102                 if(texture_)\r
103                         glDeleteTextures(1, &texture_);\r
104                 if(pbos_[0] && pbos_[1])\r
105                         glDeleteBuffers(2, pbos_);\r
106         }\r
107 \r
108         void init()     \r
109         {\r
110                 window_.reset(new sf::Window());\r
111                 window_->Create(sf::VideoMode(format_desc_.width, format_desc_.height, 32), "CasparCG", windowed_ ? sf::Style::Titlebar : sf::Style::Fullscreen);\r
112                 window_->ShowMouseCursor(false);\r
113                 window_->SetPosition(screenX_, screenY_);\r
114                 window_->SetSize(screen_width_, screen_height_);\r
115                 window_->SetActive();\r
116 \r
117                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);\r
118                 glEnable(GL_TEXTURE_2D);\r
119                                         \r
120                 glViewport(0, 0, screen_width_, screen_height_);\r
121                 \r
122                 GL_CHECK();\r
123                 \r
124                 std::pair<float, float> target_ratio = None();\r
125                 if(stretch_ == ogl::fill)\r
126                         target_ratio = Fill();\r
127                 else if(stretch_ == ogl::uniform)\r
128                         target_ratio = Uniform();\r
129                 else if(stretch_ == ogl::uniform_to_fill)\r
130                         target_ratio = UniformToFill();\r
131 \r
132                 float wSize = target_ratio.first;\r
133                 float hSize = target_ratio.second;\r
134 \r
135                 dlist_ = glGenLists(1);\r
136                 GL_CHECK();\r
137 \r
138                 glNewList(dlist_, GL_COMPILE);\r
139                         glBegin(GL_QUADS);\r
140                                 glTexCoord2f(0.0f,       1.0f);         glVertex2f(-wSize, -hSize);\r
141                                 glTexCoord2f(1.0f,       1.0f);         glVertex2f( wSize, -hSize);\r
142                                 glTexCoord2f(1.0f,       0.0f);         glVertex2f( wSize,  hSize);\r
143                                 glTexCoord2f(0.0f,       0.0f);         glVertex2f(-wSize,  hSize);\r
144                         glEnd();        \r
145                 glEndList();\r
146                 GL_CHECK();\r
147                         \r
148                 glGenTextures(1, &texture_);    \r
149                 GL_CHECK();\r
150 \r
151                 glBindTexture( GL_TEXTURE_2D, texture_);\r
152                 GL_CHECK();\r
153 \r
154                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );\r
155                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );\r
156                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );\r
157                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );\r
158 \r
159                 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);\r
160                 GL_CHECK();\r
161 \r
162                 glGenBuffersARB(2, pbos_);\r
163                 GL_CHECK();\r
164                 glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos_[0]);\r
165                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW);\r
166                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);\r
167                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW);              \r
168 \r
169                 pbo_index_ = 0;\r
170         }\r
171 \r
172         std::pair<float, float> None()\r
173         {\r
174                 float width = static_cast<float>(format_desc_.width)/static_cast<float>(screen_width_);\r
175                 float height = static_cast<float>(format_desc_.height)/static_cast<float>(screen_height_);\r
176 \r
177                 return std::make_pair(width, height);\r
178         }\r
179 \r
180         std::pair<float, float> Uniform()\r
181         {\r
182                 float aspect = static_cast<float>(format_desc_.width)/static_cast<float>(format_desc_.height);\r
183                 float width = std::min(1.0f, static_cast<float>(screen_height_)*aspect/static_cast<float>(screen_width_));\r
184                 float height = static_cast<float>(screen_width_*width)/static_cast<float>(screen_height_*aspect);\r
185 \r
186                 return std::make_pair(width, height);\r
187         }\r
188 \r
189         std::pair<float, float> Fill()\r
190         {\r
191                 return std::make_pair(1.0f, 1.0f);\r
192         }\r
193 \r
194         std::pair<float, float> UniformToFill()\r
195         {\r
196                 float wr = static_cast<float>(format_desc_.width)/static_cast<float>(screen_width_);\r
197                 float hr = static_cast<float>(format_desc_.height)/static_cast<float>(screen_height_);\r
198                 float r_inv = 1.0f/std::min(wr, hr);\r
199 \r
200                 float width = wr*r_inv;\r
201                 float height = hr*r_inv;\r
202 \r
203                 return std::make_pair(width, height);\r
204         }\r
205 \r
206         void render(const gpu_frame_ptr& frame)\r
207         {                                       \r
208                 // Render\r
209                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r
210                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);\r
211                 glLoadIdentity();\r
212         \r
213                 glBindTexture(GL_TEXTURE_2D, texture_);\r
214                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[pbo_index_]);\r
215         \r
216                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);\r
217 \r
218                 glCallList(dlist_);             \r
219 \r
220                 // Update\r
221                 int nextPboIndex = pbo_index_ ^ 1;\r
222 \r
223                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[nextPboIndex]);\r
224                 glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, NULL, GL_STREAM_DRAW);\r
225                 GLubyte* ptr = static_cast<GLubyte*>(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));\r
226 \r
227                 if(ptr != NULL)                 \r
228                 {\r
229                         common::image::copy(ptr, frame->data(), frame->size());\r
230                         glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);\r
231                 }\r
232 \r
233                 // Swap\r
234                 pbo_index_ = nextPboIndex;\r
235         }\r
236                         \r
237         void display(const gpu_frame_ptr& frame)\r
238         {\r
239                 if(frame == nullptr)\r
240                         return;         \r
241 \r
242                 if(exception_ != nullptr)\r
243                         std::rethrow_exception(exception_);\r
244 \r
245                 frame_buffer_.push(frame);\r
246         }\r
247 \r
248         void run()\r
249         {                       \r
250                 init();\r
251                 \r
252                 auto period = boost::posix_time::microseconds(static_cast<long>(get_frame_format_period(format_desc_)*1000000.0));\r
253                 auto time = boost::posix_time::microsec_clock::local_time();\r
254                 \r
255                 gpu_frame_ptr frame;\r
256                 do\r
257                 {\r
258                         try\r
259                         {               \r
260                                 frame_buffer_.pop(frame);\r
261                                 if(frame != nullptr)\r
262                                 {\r
263                                         auto remaining = period - (boost::posix_time::microsec_clock::local_time() - time);\r
264                                         if(remaining > boost::posix_time::microseconds(5000))\r
265                                                 boost::this_thread::sleep(remaining - boost::posix_time::microseconds(5000));\r
266                                         time = boost::posix_time::microsec_clock::local_time();\r
267 \r
268                                         sf::Event e;\r
269                                         while(window_->GetEvent(e)){}\r
270                                         window_->SetActive();\r
271                                         render(frame);\r
272                                         window_->Display();\r
273                                 }\r
274                         }\r
275                         catch(...)\r
276                         {\r
277                                 exception_ = std::current_exception();\r
278                         }\r
279                 }               \r
280                 while(frame != nullptr);\r
281         }\r
282                 \r
283 \r
284         GLuint dlist_;\r
285         GLuint texture_;\r
286 \r
287         bool windowed_;\r
288         unsigned int screen_width_;\r
289         unsigned int screen_height_;\r
290         unsigned int screenX_;\r
291         unsigned int screenY_;\r
292                                 \r
293         GLuint pbos_[2];\r
294         int pbo_index_;\r
295 \r
296         std::unique_ptr<sf::Window> window_;\r
297         stretch stretch_;\r
298         caspar::frame_format_desc format_desc_;\r
299 \r
300         std::exception_ptr exception_;\r
301         boost::thread thread_;\r
302         tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
303 };\r
304 \r
305 ogl_frame_consumer::ogl_frame_consumer(const caspar::frame_format_desc& format_desc, unsigned int screen_index, stretch stretch, bool windowed)\r
306 : impl_(new implementation(format_desc, screen_index, stretch, windowed)){}\r
307 const caspar::frame_format_desc& ogl_frame_consumer::get_frame_format_desc() const{return impl_->format_desc_;}\r
308 void ogl_frame_consumer::display(const gpu_frame_ptr& frame){impl_->display(frame);}\r
309 }}\r