]> git.sesse.net Git - casparcg/blob - modules/screen/consumer/screen_consumer.cpp
45b5b8a30bcf90aba773e7abd093c823dbfd9b6f
[casparcg] / modules / screen / consumer / screen_consumer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "screen_consumer.h"
23
24 #include <GL/glew.h>
25 #include <SFML/Window.hpp>
26
27 #include <common/diagnostics/graph.h>
28 #include <common/gl/gl_check.h>
29 #include <common/log.h>
30 #include <common/memory.h>
31 #include <common/array.h>
32 #include <common/memshfl.h>
33 #include <common/utf.h>
34 #include <common/prec_timer.h>
35
36 #include <ffmpeg/producer/filter/filter.h>
37
38 #include <core/video_format.h>
39 #include <core/frame/frame.h>
40 #include <core/consumer/frame_consumer.h>
41
42 #include <boost/timer.hpp>
43 #include <boost/circular_buffer.hpp>
44 #include <boost/lexical_cast.hpp>
45 #include <boost/foreach.hpp>
46 #include <boost/property_tree/ptree.hpp>
47 #include <boost/thread.hpp>
48
49 #include <tbb/atomic.h>
50 #include <tbb/concurrent_queue.h>
51 #include <tbb/parallel_for.h>
52
53 #include <boost/assign.hpp>
54
55 #include <asmlib.h>
56
57 #include <algorithm>
58 #include <vector>
59
60 #if defined(_MSC_VER)
61 #pragma warning (push)
62 #pragma warning (disable : 4244)
63 #endif
64 extern "C" 
65 {
66         #define __STDC_CONSTANT_MACROS
67         #define __STDC_LIMIT_MACROS
68         #include <libavcodec/avcodec.h>
69         #include <libavutil/imgutils.h>
70 }
71 #if defined(_MSC_VER)
72 #pragma warning (pop)
73 #endif
74
75 namespace caspar { namespace screen {
76                 
77 enum stretch
78 {
79         none,
80         uniform,
81         fill,
82         uniform_to_fill
83 };
84
85 struct configuration
86 {
87         enum aspect_ratio
88         {
89                 aspect_4_3 = 0,
90                 aspect_16_9,
91                 aspect_invalid,
92         };
93                 
94         std::wstring    name;
95         int                             screen_index;
96         stretch                 stretch;
97         bool                    windowed;
98         bool                    auto_deinterlace;
99         bool                    key_only;
100         aspect_ratio    aspect; 
101         bool                    vsync;
102
103         configuration()
104                 : name(L"ogl")
105                 , screen_index(0)
106                 , stretch(fill)
107                 , windowed(true)
108                 , auto_deinterlace(true)
109                 , key_only(false)
110                 , aspect(aspect_invalid)
111                 , vsync(true)
112         {
113         }
114 };
115
116 struct screen_consumer : boost::noncopyable
117 {               
118         const configuration                                     config_;
119         core::video_format_desc                         format_desc_;
120         int                                                                     channel_index_;
121
122         GLuint                                                          texture_;
123         std::vector<GLuint>                                     pbos_;
124                         
125         float                                                           width_;
126         float                                                           height_;        
127         int                                                                     screen_x_;
128         int                                                                     screen_y_;
129         int                                                                     screen_width_;
130         int                                                                     screen_height_;
131         int                                                                     square_width_;
132         int                                                                     square_height_;                         
133         
134         sf::Window                                                      window_;
135         
136         spl::shared_ptr<diagnostics::graph>     graph_;
137         boost::timer                                            perf_timer_;
138         boost::timer                                            tick_timer_;
139
140         caspar::prec_timer                                      wait_timer_;
141
142         tbb::concurrent_bounded_queue<core::const_frame>        frame_buffer_;
143
144         boost::thread                                           thread_;
145         tbb::atomic<bool>                                       is_running_;
146         
147         ffmpeg::filter                                          filter_;
148 public:
149         screen_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index) 
150                 : config_(config)
151                 , format_desc_(format_desc)
152                 , channel_index_(channel_index)
153                 , texture_(0)
154                 , pbos_(2, 0)   
155                 , screen_width_(format_desc.width)
156                 , screen_height_(format_desc.height)
157                 , square_width_(format_desc.square_width)
158                 , square_height_(format_desc.square_height)
159                 , filter_(format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? L"" : L"YADIF=1:-1", boost::assign::list_of(PIX_FMT_BGRA))
160         {               
161                 if(format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_4_3)
162                 {
163                         // Use default values which are 4:3.
164                 }
165                 else
166                 {
167                         if(config_.aspect == configuration::aspect_16_9)
168                                 square_width_ = (format_desc.height*16)/9;
169                         else if(config_.aspect == configuration::aspect_4_3)
170                                 square_width_ = (format_desc.height*4)/3;
171                 }
172
173                 frame_buffer_.set_capacity(2);
174                 
175                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
176                 graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
177                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
178                 graph_->set_text(print());
179                 diagnostics::register_graph(graph_);
180                                                                         
181                 DISPLAY_DEVICE d_device = {sizeof(d_device), 0};                        
182                 std::vector<DISPLAY_DEVICE> displayDevices;
183                 for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n)
184                         displayDevices.push_back(d_device);
185
186                 if(config_.screen_index >= displayDevices.size())
187                         CASPAR_LOG(warning) << print() << L" Invalid screen-index: " << config_.screen_index;
188                 
189                 DEVMODE devmode = {};
190                 if(!EnumDisplaySettings(displayDevices[config_.screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode))
191                         CASPAR_LOG(warning) << print() << L" Could not find display settings for screen-index: " << config_.screen_index;
192                 
193                 screen_x_               = devmode.dmPosition.x;
194                 screen_y_               = devmode.dmPosition.y;
195                 screen_width_   = config_.windowed ? square_width_ : devmode.dmPelsWidth;
196                 screen_height_  = config_.windowed ? square_height_ : devmode.dmPelsHeight;
197                 
198                 is_running_ = true;
199                 thread_ = boost::thread([this]{run();});
200         }
201         
202         ~screen_consumer()
203         {
204                 is_running_ = false;
205                 frame_buffer_.try_push(core::const_frame::empty());
206                 thread_.join();
207         }
208
209         void init()
210         {
211                 window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), u8(print()), config_.windowed ? sf::Style::Resize | sf::Style::Close : sf::Style::Fullscreen);
212                 window_.ShowMouseCursor(false);
213                 window_.SetPosition(screen_x_, screen_y_);
214                 window_.SetSize(screen_width_, screen_height_);
215                 window_.SetActive();
216                 
217                 if(!GLEW_VERSION_2_1 && glewInit() != GLEW_OK)
218                         CASPAR_THROW_EXCEPTION(gl::ogl_exception() << msg_info("Failed to initialize GLEW."));
219
220                 if(!GLEW_VERSION_2_1)
221                         CASPAR_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));
222
223                 GL(glEnable(GL_TEXTURE_2D));
224                 GL(glDisable(GL_DEPTH_TEST));           
225                 GL(glClearColor(0.0, 0.0, 0.0, 0.0));
226                 GL(glViewport(0, 0, format_desc_.width, format_desc_.height));
227                 GL(glLoadIdentity());
228                                 
229                 calculate_aspect();
230                         
231                 GL(glGenTextures(1, &texture_));
232                 GL(glBindTexture(GL_TEXTURE_2D, texture_));
233                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
234                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
235                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
236                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
237                 GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0));
238                 GL(glBindTexture(GL_TEXTURE_2D, 0));
239                                         
240                 GL(glGenBuffers(2, pbos_.data()));
241                         
242                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]);
243                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);
244                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);
245                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);
246                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
247                 
248                 auto wglSwapIntervalEXT = reinterpret_cast<void(APIENTRY*)(int)>(wglGetProcAddress("wglSwapIntervalEXT"));
249                 if(wglSwapIntervalEXT)
250                 {
251                         if(config_.vsync)
252                         {
253                                 wglSwapIntervalEXT(1);
254                                 CASPAR_LOG(info) << print() << " Enabled vsync.";
255                         }
256                         else
257                                 wglSwapIntervalEXT(0);
258                 }
259
260                 CASPAR_LOG(info) << print() << " Successfully Initialized.";
261         }
262
263         void uninit()
264         {               
265                 if(texture_)
266                         glDeleteTextures(1, &texture_);
267
268                 BOOST_FOREACH(auto& pbo, pbos_)
269                 {
270                         if(pbo)
271                                 glDeleteBuffers(1, &pbo);
272                 }
273         }
274
275         void run()
276         {
277                 try
278                 {
279                         init();
280
281                         while(is_running_)
282                         {                       
283                                 try
284                                 {
285                                         sf::Event e;            
286                                         while(window_.GetEvent(e))
287                                         {
288                                                 if(e.Type == sf::Event::Resized)
289                                                         calculate_aspect();
290                                                 else if(e.Type == sf::Event::Closed)
291                                                         is_running_ = false;
292                                         }
293                         
294                                         auto frame = core::const_frame::empty();
295                                         frame_buffer_.pop(frame);
296
297                                         render_and_draw_frame(frame);
298                                         
299                                         /*perf_timer_.restart();
300                                         render(frame);
301                                         graph_->set_value("frame-time", perf_timer_.elapsed()*format_desc_.fps*0.5);    
302
303                                         window_.Display();*/
304
305                                         graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);     
306                                         tick_timer_.restart();
307                                 }
308                                 catch(...)
309                                 {
310                                         CASPAR_LOG_CURRENT_EXCEPTION();
311                                         is_running_ = false;
312                                 }
313                         }
314
315                         uninit();
316                 }
317                 catch(...)
318                 {
319                         CASPAR_LOG_CURRENT_EXCEPTION();
320                 }
321         }
322
323         void try_sleep_almost_until_vblank()
324         {
325                 static const double THRESHOLD = 0.003;
326                 double threshold = config_.vsync ? THRESHOLD : 0.0;
327
328                 auto frame_time = 1.0 / (format_desc_.fps * format_desc_.field_count);
329
330                 wait_timer_.tick(frame_time - threshold);
331         }
332
333         void wait_for_vblank_and_display()
334         {
335                 try_sleep_almost_until_vblank();
336                 window_.Display();
337                 // Make sure that the next tick measures the duration from this point in time.
338                 wait_timer_.tick(0.0);
339         }
340
341         spl::shared_ptr<AVFrame> get_av_frame()
342         {               
343                 spl::shared_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);      
344                 avcodec_get_frame_defaults(av_frame.get());
345                                                 
346                 av_frame->linesize[0]           = format_desc_.width*4;                 
347                 av_frame->format                        = PIX_FMT_BGRA;
348                 av_frame->width                         = format_desc_.width;
349                 av_frame->height                        = format_desc_.height;
350                 av_frame->interlaced_frame      = format_desc_.field_mode != core::field_mode::progressive;
351                 av_frame->top_field_first       = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;
352
353                 return av_frame;
354         }
355
356         void render_and_draw_frame(core::const_frame frame)
357         {
358                 if(static_cast<size_t>(frame.image_data().size()) != format_desc_.size)
359                         return;
360
361                 if(screen_width_ == 0 && screen_height_ == 0)
362                         return;
363                                         
364                 perf_timer_.restart();
365                 auto av_frame = get_av_frame();
366                 av_frame->data[0] = const_cast<uint8_t*>(frame.image_data().begin());
367
368                 filter_.push(av_frame);
369                 auto frames = filter_.poll_all();
370
371                 if (frames.empty())
372                         return;
373
374                 if (frames.size() == 1)
375                 {
376                         render(frames[0]);
377                         graph_->set_value("frame-time", perf_timer_.elapsed() * format_desc_.fps * 0.5);
378
379                         wait_for_vblank_and_display(); // progressive frame
380                 }
381                 else if (frames.size() == 2)
382                 {
383                         render(frames[0]);
384                         double perf_elapsed = perf_timer_.elapsed();
385
386                         wait_for_vblank_and_display(); // field1
387
388                         perf_timer_.restart();
389                         render(frames[1]);
390                         perf_elapsed += perf_timer_.elapsed();
391                         graph_->set_value("frame-time", perf_elapsed * format_desc_.fps * 0.5);
392
393                         wait_for_vblank_and_display(); // field2
394                 }
395         }
396
397         void render(spl::shared_ptr<AVFrame> av_frame)
398         {
399                 GL(glBindTexture(GL_TEXTURE_2D, texture_));
400
401                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]));
402                 GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0));
403
404                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]));
405                 GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW));
406
407                 auto ptr = reinterpret_cast<char*>(GL2(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)));
408                 if(ptr)
409                 {
410                         if(config_.key_only)
411                         {
412                                 tbb::parallel_for(tbb::blocked_range<int>(0, format_desc_.height), [&](const tbb::blocked_range<int>& r)
413                                 {
414                                         for(int n = r.begin(); n != r.end(); ++n)
415                                                 aligned_memshfl(ptr+n*format_desc_.width*4, av_frame->data[0]+n*av_frame->linesize[0], format_desc_.width*4, 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
416                                 });
417                         }
418                         else
419                         {       
420                                 tbb::parallel_for(tbb::blocked_range<int>(0, format_desc_.height), [&](const tbb::blocked_range<int>& r)
421                                 {
422                                         for(int n = r.begin(); n != r.end(); ++n)
423                                                 A_memcpy(ptr+n*format_desc_.width*4, av_frame->data[0]+n*av_frame->linesize[0], format_desc_.width*4);
424                                 });
425                         }
426                         
427                         GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)); // release the mapped buffer
428                 }
429
430                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
431                                 
432                 GL(glClear(GL_COLOR_BUFFER_BIT));                       
433                 glBegin(GL_QUADS);
434                                 glTexCoord2f(0.0f,        1.0f);        glVertex2f(-width_, -height_);
435                                 glTexCoord2f(1.0f,        1.0f);        glVertex2f( width_, -height_);
436                                 glTexCoord2f(1.0f,        0.0f);        glVertex2f( width_,  height_);
437                                 glTexCoord2f(0.0f,        0.0f);        glVertex2f(-width_,  height_);
438                 glEnd();
439                 
440                 GL(glBindTexture(GL_TEXTURE_2D, 0));
441
442                 std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());
443         }
444
445
446         bool send(core::const_frame frame)
447         {
448                 if(!frame_buffer_.try_push(frame))
449                         graph_->set_tag("dropped-frame");
450                 return is_running_;
451         }
452                 
453         std::wstring print() const
454         {       
455                 return config_.name + L"[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";
456         }
457         
458         void calculate_aspect()
459         {
460                 if(config_.windowed)
461                 {
462                         screen_height_ = window_.GetHeight();
463                         screen_width_ = window_.GetWidth();
464                 }
465                 
466                 GL(glViewport(0, 0, screen_width_, screen_height_));
467
468                 std::pair<float, float> target_ratio = None();
469                 if(config_.stretch == fill)
470                         target_ratio = Fill();
471                 else if(config_.stretch == uniform)
472                         target_ratio = Uniform();
473                 else if(config_.stretch == uniform_to_fill)
474                         target_ratio = UniformToFill();
475
476                 width_ = target_ratio.first;
477                 height_ = target_ratio.second;
478         }
479                 
480         std::pair<float, float> None()
481         {
482                 float width = static_cast<float>(square_width_)/static_cast<float>(screen_width_);
483                 float height = static_cast<float>(square_height_)/static_cast<float>(screen_height_);
484
485                 return std::make_pair(width, height);
486         }
487
488         std::pair<float, float> Uniform()
489         {
490                 float aspect = static_cast<float>(square_width_)/static_cast<float>(square_height_);
491                 float width = std::min(1.0f, static_cast<float>(screen_height_)*aspect/static_cast<float>(screen_width_));
492                 float height = static_cast<float>(screen_width_*width)/static_cast<float>(screen_height_*aspect);
493
494                 return std::make_pair(width, height);
495         }
496
497         std::pair<float, float> Fill()
498         {
499                 return std::make_pair(1.0f, 1.0f);
500         }
501
502         std::pair<float, float> UniformToFill()
503         {
504                 float wr = static_cast<float>(square_width_)/static_cast<float>(screen_width_);
505                 float hr = static_cast<float>(square_height_)/static_cast<float>(screen_height_);
506                 float r_inv = 1.0f/std::min(wr, hr);
507
508                 float width = wr*r_inv;
509                 float height = hr*r_inv;
510
511                 return std::make_pair(width, height);
512         }
513 };
514
515
516 struct screen_consumer_proxy : public core::frame_consumer
517 {
518         const configuration config_;
519         std::unique_ptr<screen_consumer> consumer_;
520
521 public:
522
523         screen_consumer_proxy(const configuration& config)
524                 : config_(config){}
525         
526         // frame_consumer
527
528         void initialize(const core::video_format_desc& format_desc, int channel_index) override
529         {
530                 consumer_.reset();
531                 consumer_.reset(new screen_consumer(config_, format_desc, channel_index));
532         }
533         
534         bool send(core::const_frame frame) override
535         {
536                 return consumer_->send(frame);
537         }
538         
539         std::wstring print() const override
540         {
541                 return consumer_ ? consumer_->print() : L"[screen_consumer]";
542         }
543
544         std::wstring name() const override
545         {
546                 return L"screen";
547         }
548
549         boost::property_tree::wptree info() const override
550         {
551                 boost::property_tree::wptree info;
552                 info.add(L"type", L"screen");
553                 info.add(L"key-only", config_.key_only);
554                 info.add(L"windowed", config_.windowed);
555                 info.add(L"auto-deinterlace", config_.auto_deinterlace);
556                 return info;
557         }
558
559         bool has_synchronization_clock() const override
560         {
561                 return false;
562         }
563         
564         int buffer_depth() const override
565         {
566                 return 2;
567         }
568
569         int index() const override
570         {
571                 return 600 + (config_.key_only ? 10 : 0) + config_.screen_index;
572         }
573
574         void subscribe(const monitor::observable::observer_ptr& o) override
575         {
576         }
577
578         void unsubscribe(const monitor::observable::observer_ptr& o) override
579         {
580         }       
581 };      
582
583 spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params)
584 {
585         if(params.size() < 1 || params[0] != L"SCREEN")
586                 return core::frame_consumer::empty();
587         
588         configuration config;
589                 
590         if(params.size() > 1)
591                 config.screen_index = boost::lexical_cast<int>(params[1]);
592                 
593         config.key_only = std::find(params.begin(), params.end(), L"WINDOWED") != params.end();
594         config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end();
595
596         auto name_it    = std::find(params.begin(), params.end(), L"NAME");
597         if(name_it != params.end() && ++name_it != params.end())
598                 config.name = *name_it;
599
600         return spl::make_shared<screen_consumer_proxy>(config);
601 }
602
603 spl::shared_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree) 
604 {
605         configuration config;
606         config.name                             = ptree.get(L"name",     config.name);
607         config.screen_index             = ptree.get(L"device",   config.screen_index+1)-1;
608         config.windowed                 = ptree.get(L"windowed", config.windowed);
609         config.key_only                 = ptree.get(L"key-only", config.key_only);
610         config.auto_deinterlace = ptree.get(L"auto-deinterlace", config.auto_deinterlace);
611         config.vsync                    = ptree.get(L"vsync", config.vsync);
612         
613         auto stretch_str = ptree.get(L"stretch", L"default");
614         if(stretch_str == L"uniform")
615                 config.stretch = stretch::uniform;
616         else if(stretch_str == L"uniform_to_fill")
617                 config.stretch = stretch::uniform_to_fill;
618
619         auto aspect_str = ptree.get(L"aspect-ratio", L"default");
620         if(aspect_str == L"16:9")
621                 config.aspect = configuration::aspect_16_9;
622         else if(aspect_str == L"4:3")
623                 config.aspect = configuration::aspect_4_3;
624         
625         return spl::make_shared<screen_consumer_proxy>(config);
626 }
627
628 }}